使用ret2plt绕过libc安全区

背景

前面介绍ret2libc攻击技术,原理很简单,只需要将system函数的地址填充到eip的位置,然后再把”/bin/bash”地址填充到eip + 8的位置即可以实现攻击。

很快安全专家提出了保护机制。该方法完全为抵抗ret2libc攻击方法而生。既然ret2libc需要将system函数地址填充到栈上,那ASCII armoring就想办法让libc所有函数的地址都包含一个零字节(NULL byte),让strcpy拷贝函数在遇到零地址时结束拷贝,攻击失败。

尽管有了ASCII armoring机制,但很快安全人员发现一种新攻击方法,可以攻破它,这个方法称为ret2plt。在介绍ret2plt之前,我们先简单介绍一下什么是plt。

ELF的姐妹花plt与got

PLT全称为Procedure Linkage Table,中文为链接过程表,它与动态库的动态链接过程息息相关。提到PLT就不得不要提及GOT,GOT全移为Global Offset Table。plt和got是调用动态库函数的重要过程。

为了方便讨论 plt和got,我们将今晚的主角代码请出场(stack4.c)

evilfunction(char *input) {char buffer[512];strcpy(buffer, input);printf(“strcpy %s\n”, input);printf(“strcpy done\n”);}int main(int argc, char *argv[]) {evilfunction(argv[1]);return 0;}

编译

gcc -Wall -g -o stack4 stack4.c -fno-stack-protector -m32

眼尖读者会想到,上述代码中的strcpy和printf函数都是glibc库里面,glibc动态库的运行地址是运行时动态加载才能确定的,编译时是不知道它的真实地址的,那编译器是怎么解决的呢?

ELF规范使用PLT和GOT技术解决动态重定位过程,每个动态库函数(上述的strcpy和printf)都有段PLT桩代码和一个GOT项。 PLT桩代码用于引导调用者跳到动态链接器(ld-linux.so.2),而GOT项则存该函数动态重定位完成之后找到的真正地址。

以上代码为样本,以一张图来显示printf/strcpy函数的 PLT以及GOT的关系。

如果有机会可再写一篇详细介绍PLT和GOT的文章,在里面只做简单的介绍。

PLT表是由多个PLT表项组成,其中第一项是公共项,剩下是每个动态库函数一项,每项由3条指令组成(具体X86和ARM不同,上图为X86版本)。 GOT表也是由多个GOT表项组成,前面有3项个公共项,剩下的是每个动态库函数一项,动态库项存放的是动态库加载之后该函数的真正地址。

对于已经找到真正地址的动态库函数来说,它的GOT表存放真正地址(上图中展示的情况是还未找它的真正地址),它的调用过程非常简单,以evilfunction函数调用strcpy函数为例,就是执行上图中的1,2,3这三步,就执行到strcpy函数内部。 但是任何一个动态库函数调用,它的第一次调用,GOT表项还没有存放它的真正地址,它需要在这一次调用中做两个事情1)通过动态链接找到该函数地址填到GOT表项中,2)再调用。 第2)步调用与已经重定位好的函数调用没有两样,下面简单说一下第1)步。还是以strcpy函数为例:它的GOT项为上图中标示为3的项,编译器在静态链接中,无法知道strcpy的运行地址,所以它的GOT内空指向了strcpy PLT表项中的第二条指令(上图标4),然后跳到公共PLT表项(上图6和7),最后根据GOT第3公共项(上图标8)跳到动态链接器的符号查找函数将strcpy的函数找到,并将地址写到它对应的GOT表项。它的完整过程就是上图中1->2->3->4->5->6->7->8过程。

后面会讲述如何利用PLT和GOT进行攻击,只需了解如下要点即可: 1)evilfunction函数只需要知该函数对应的PLT桩地址即可以调用,其它事情由PLT和GOT处理,evilfunction不需要感知实现细节 2)GOT表项保存动态库函数的地址。

ret2plt攻击路思

ret2libc攻击方法的的要点就是将system和”/bin/bash”两个地址注入到栈中,但是ASCII armoring让system地址产生了零字节,无法进行同样的攻击。 那该如何处理?安全专家想到了一个非常有技巧性的方法,就是能否不直接拷贝system函数的地址,而是通过不同的内存空间拼凑而产生system的地址。这样产生了两个问题

有什么技巧可以多次拼凑成system函数地址 该地址应该在什么地址比较适合

对于第一点,想到了一个叫RRP(pop; pop; ret)指令序列可以将多个strcpy函数调用串起来。 对于第二点,有一个比较不依赖于libc空间的地址,即就是GOT表项。

因此,ret2plt的攻击思想就是:通过多次strcpy函数,拼凑出system函数地址放到某个GOT表中,然后通过GOT来调用system函数。它的栈结构和执行过程如图所示:

整个过程最为巧妙的过程就是利用PPR(pop, pop, ret指令顺序首地址)技巧,使用可以将高地址的两个参数出栈,再执行下一个strcpy函数。

攻击过程

根据上图的栈结构,需要找到如下的地址: 1. strcpy的plt地址 2. PPR指令序列地址 3. system函数地址,并且找到4个地址分别包含system[0], system[1], system[2]和system[3] 4. puts的got地址 5. puts的plt地址 6. “/bin/bash”字符串地址

对准eip位置

gdb ./stack4 (gdb) r “$(perl -e ‘print “A”x524 . “BBBB” . “”’)”

Starting program: /home/ivan/exploit/stack4 “$(perl -e ‘print “A”x524 . “BBBB” . “”’)” strcpy strcpy done

Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? () (gdb)

显然第524字节之后就可以对准EIP了。

strcpy的PLT地址(gdb) disassemble evilfunction Dump of assembler code for function evilfunction:<+<+12>: mov %eax,0x4(%esp)0x08048454 <+16>: lea -0x208(%ebp),%eax0x0804845a <+22>: mov %eax,(%esp)0x0804845d <+25>: call 0x8048350 <strcpy@plt><+38>: mov %edx,0x4(%esp)0x0804846e <+42>: mov %eax,(%esp)0x08048471 <+45>: call 0x8048340 <printf@plt>0x08048476 <+50>: movl $0x804858b,(%esp)0x0804847d <+57>: call 0x8048360 <puts@plt>0x08048482 <+62>: leave0x08048483 <+63>: ret End of assembler dump.

直接反编译evilfunction可直接获取strcpy的PLT地址为0x08048350。

PPR指令地址

使用objdump -d stack4命令输出汇编指令,然后查找pop/pop/ret指令顺序,结果如下:

8048412:5bpop %ebx 8048413:5dpop %ebp 8048414:c3ret

因此PPR的地址为: 0x08048412。

system[0..3]地址十七岁怎么会有七十岁的忧伤,十八岁怎么会有八十岁的等待。

使用ret2plt绕过libc安全区

相关文章:

你感兴趣的文章:

标签云: