动态定位 API 地址的 shellcode

TOC

  1. 1. ASLR
  2. 2. DEP
  3. 3. PIE
  4. 4. Stack Guard
  5. 5. RELRO
  6. 6. 关闭fastbin
  7. 7. prctl沙箱
  8. 8. AddressSanitizer

二进制程序的一些常见保护。

ASLR

aslr是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术。也就是说程序每次执行时,栈、堆、库的位置都不一样。

效果如下所示:

//环境:gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)
#include

int main()
{
int x=0;
printf("%08lX\n",&x);//打印变量x在栈中的地址
}

结果如下:

ex@ex:~/test$ ./a.out 
7FFEDE67D2E4
ex@ex:~/test$ ./a.out
7FFFCD469C84
ex@ex:~/test$ ./a.out
7FFD6501FE14
ex@ex:~/test$ ./a.out
7FFE10E29F94
ex@ex:~/test$ ldd a.out
linux-vdso.so.1 (0x00007ffc2f368000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdf818c9000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdf81ebc000)
ex@ex:~/test$ ldd a.out
linux-vdso.so.1 (0x00007fff33fbc000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8b8d00a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8b8d5fd000)
ex@ex:~/test$ ldd a.out
linux-vdso.so.1 (0x00007fff4a1bd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff285a6f000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff286062000)
ex@ex:~/test$ ldd a.out
linux-vdso.so.1 (0x00007ffc4d39e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f988480b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9884dfe000)

ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。据研究表明ASLR可以有效的降低缓冲区溢出攻击的成功率,如今Linux、FreeBSD、Windows等主流操作系统都已采用了该技术。

Linux检查是否开启ASLR,可用下面命令:

cat /proc/sys/kernel/randomize_va_space

如果/proc/sys/kernel/randomize_va_space里的值为0时,则表示ASLR关闭。

可用下面的命令手动关闭:

echo -n “0” > /proc/sys/kernel/randomize_va_space

-n表示不输出换行

Windows10强制开启ALSR,但是可以通过更改PE文件头里的Optional Header结构体里的DLL Characteristics字段,将DLL Characteristics字段的第7位设置为0即可关闭栈的ASLR,但是系统动态库的ALSR是强制开启的,也可以使用PEtools进行更改,如下图:

DEP

数据执行保护,默认栈的权限是可读、可写、不可执行。

gcc编译器默认开启该保护,编译时加上 -z execstack 则关闭该保护,-z noexecstack 是开启该保护。(-z 是传参给链接器,execstack是使目标文件的栈可以执行)

PIE

PIE(position-independent executable, 地址无关可执行文件)技术就是一个针对代码段.text, 数据段.*data,.bss等固定地址的一个防护技术。同ASLR一样,应用了PIE的程序会在每次加载时都变换加载基址。

gcc编译器 编译时加上-fpie -pie即开启 PIE,原先是默认不开启的,现在好像是默认开启,编译时加上 -no-pie 关闭PIE保护。没有开启的情况下.text, 数据段.*data,.bss等段的地址是固定的。

Stack Guard

编译器对栈溢出的一种保护机制,在函数执行时,先在栈上放置一个随机标识符,函数返回前会先检查标识符是否被修改,如果被修改则直接触发中断来中止程序,可以有效的防止栈溢出攻击。

gcc默认开启 Stack Guard ,编译时加上 -fno-stack-protector 参数就可以关闭 Stack Guard(CANARY)。

RELRO

relro 是一种用于加强对 binary 数据段的保护的技术。relro 分为 partial relro 和 full relro。参数 -z norelro 是关闭RELRO保护。

Partial RELRO

现在gcc 默认编译就是 partial relro,参数是 -z relro

部分区块(比如:.init_array .fini_array .jcr .dynamic .got)在被动态装载(初始化)后,就被标记为只读区块。

Full RELRO

gcc编译参数是-z relro -z now

拥有 Partial RELRO 的所有特性

所有导入的符号都在 startup time 被解析

关闭fastbin

#include <malloc.h>

// mallopt(1, 0);
mallopt(M_MXFAST, 0);

prctl沙箱

#include <sys/prctl.h>

// prctl(38, 1LL, 0LL, 0LL, 0LL);
prctl(PR_SET_NO_NEW_PRIVS, 1LL, 0LL, 0LL, 0LL);

在Linux中,PR_SET_NO_NEW_PRIVS 当一个进程或者子进程设置了PR_SET_NO_NEW_PRIVS属性,则其不能访问一些无法share的操作,这是kernel 3.5后加的feature。

AddressSanitizer

需要安装的库:libasan.x86_64,新版本的gcc可能还需要安装libubsan,虽然说AddressSanitizer是gcc的一部分,但这两库默认是没有安装的。

使用方法很简单,只要在编译程序时加上-fsanitize=address -fno-omit-frame-pointer两个编译选项即可,需要说明的是要使用系统自带的内存管理库,不能使用第三方的内存管理库,因为这个功能要拦截malloc,free等标准函数。gcc几个常用编译选项如下:

-fsanitize=address #开启地址越界检查功能

-fno-omit-frame-pointer #开启后,可以出界更详细的错误信息

-fsanitize=leak #开启内存泄露检查功能

Undefined Behavior Sanitizer 未知行为检测

UBSan有一些处理程序或魔术功能入口点,它们被称为未定义的行为。编译仪器代码通过适当注入检查;如果检查代码检测到UB,则调用这些处理程序。

它的一个gcc编译标志(-fsanitize=undefined),用于添加运行时检测代码。