缓冲区溢出保护机制——Linux
缓冲区溢出保护机制
Linux
canary(栈保护)
栈溢出保护是一种缓冲区溢出攻击的缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行。
当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,该cookie往往放置在ebp/rbp的正上方,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。
攻击者在覆盖返回地址的时候也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux中我们将cookie信息称为canary。
gcc在4.2版本中添加了栈保护的编译参数:
-fstack-protector:只为局部变量中包含长度不小于8byte的char数组的函数插入保护代码
-fstack-protector-all:满足以下三个条件都会插入保护代码:
1、局部变量的地址作为赋值语句的右值或函数参数
2、局部变量包含数组类型的局部变量,不管数组长度
3、带register声明的局部变量
FORTIFY
先看一个例子:
void fun(char *s) {
char buf[0x100];
strcpy(buf, s);
/* Don't allow gcc to optimise away the buf */
asm volatile("" :: "m" (buf));
}
用包含参数-U_FORTIFY_SOURCE编译:
08048450 <fun>:
push %ebp ;
mov %esp,%ebp
sub $0x118,%esp ; 将0x118存储到栈上
mov 0x8(%ebp),%eax ; 将目标参数载入eax
mov %eax,0x4(%esp) ; 保存目标参数
lea -0x108(%ebp),%eax ; 数组buf
mov %eax,(%esp) ; 保存
call 8048320 <strcpy@plt>
leave ;
ret
用包含参数-D_FORTIFY_SOURCE=2编译:
08048470 <fun>:
push %ebp ;
mov %esp,%ebp
sub $0x118,%esp ;
movl $0x100,0x8(%esp) ; 把0x100当作目标参数保存
mov 0x8(%ebp),%eax ;
mov %eax,0x4(%esp) ;
lea -0x108(%ebp),%eax ;
mov %eax,(%esp) ;
call 8048370 <__strcpy_chk@plt>
leave ;
ret
可以看出,gcc生成了一些附加代码,通过对数组大小的判断替换strcpy, memcpy, memset等函数名,达到防止缓冲区溢出的作用。
NX
NX即No-eXecute(不可执行)的意思,NX的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常。
等同于Windows下的DEP。
gcc编译器默认开启NX选项,通过-z execstack可以关闭NX。
PIE
Position-Independent-Executable是Binutils,glibc和gcc的一个功能,能用来创建介于共享库和通常可执行代码之间的代码。
标准的可执行程序需要固定的地址,并且只有被装载到这个地址才能正确执行,PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。
引入PIE的原因就是让程序能装载在随机的地址,从而缓解缓冲区溢出攻击。
gcc的-pie和fpie选项
RELRO
设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。
Partial RELRO: gcc -Wl, -z, relro:
ELF节重排
.got, .dtors,etc. precede the .data and .bss
GOT表仍然可写
Full RELRO: gcc -Wl, -z, relro, -z, now
支持Partial RELRO的所有功能
GOT表只读
Linux下查看保护机制
checksec
用来检查可执行文件属性,例如PIE, RELRO, PaX, Canaries, ASLR, Fortify Source等等属性。
gdb peda插件:
pwntools: