Practical Binary Analysis Ch. 5 CTF: Lvl 5
This is my writeup on Level 5 of the Practical Binary Analysis CTF challenge. This flag ended up being a lot more involved in order to successfully capture it.
Fifth Flag: lvl5
If we pass lvl4’s flag to the oracle, we receive the following hint
We are greeted with poem of some sort which seems to hint that the flag lies in unused code within the binary and we need to figure out how to redirect to that code, and we should focus our efforts on static analysis rather than dynamic. Seems simple enough. Lets start with a basic strings analysis on the file
We see some strings related to a key and a decrypted flag, which we don’t see when we run the program. Let’s dump .rodata and see what we can find there
It looks like those two strings begin at 0x400774 and 0x400782 respectively. Lets see where they are accessed in the disassembly
We find two instructions that access the address of these string constants. Now we look at the objdump output to see where those instructions are
We see a large chunk of code, which seems like an unused function, that does some interesting things. One thing that sticks out immediately is the 4 movabs instructions which move four 8-byte immediate magic values into the stack. Maybe this has something to do with our flag? Both of the strings that we saw earlier seem to be within this chunk of code. We just need a way to execute it.
Notice that the starting address of the function appears to be at 0x400620, where rbx is pushed onto the stack. What if we were able to overwrite a pointer somewhere with this address so it jumps here instead? Luckily for us, there is a perfect place for this:
If we recall, our ELF binaries don’t start executing at main(). If we pass our binary to readelf to examine the executable header, we note that the entry point is at 0x400520. This eventually jumps to a PLT stub, __libc_start_main@plt, which sets up the environment for libc.
Here we have a perfect attack vector! It’s kind of up to us the route we want to go at this point to inject our target function address. We could do some GOT hijacking and insert the address into the GOT entry referenced by the PLT stub, or even simpler, we change the value passed to the first argument of __libc_start_main, which is supposed to be a function pointer to the user-defined main(). I decided to go with the latter option
Running the binary now gets us the final flag:
It looks like the “key” was simply the hidden function’s address. The flag for lvl5 is








