This challenge involves the same exploitation method used in the bcloud write-up, but it’s an ARM binary. Also, I’m gonna show you our “ghetto” solution which is a lot less involved from the intentional solution but requires 1 nibble brute-force of system().
If we start the binary we see:
In getPassphrase() we see there’s a single check via strncmp for the passphrase.
We also see that we get 3 tries to enter the correct passphrase and if we are wrong, the printf function prints our input back to us. We also see that the passphrase gets stored on the .BSS section. And if we look at the .BSS, we see that the PassphraseBuffer borders with the Secret buffer. In secret we have stored a ptr to a heap allocated buffer that will be used later for storing our secret by using the editSecret() function.
So, if we enter incorrect passphrase of size of 8 bytes, we will cause printf to print the “BadBoy” message along with our 8 bytes of garbage followed by the ptr to the heap allocated buffer. This way we get our heap leak :).
We already covered the getPassphrase() function, and printMenu() is self explanatory.
getChoice() is used for the main menu’s case statement, and by signName().
This function uses a heap buffer of size 0x10 bytes. The ptr to this buffer is stored in the .BSS 0x10fbc (see on the screenshot above).
We can see an obvious overflow in this function. We have 8 bytes pre-allocated buffer on the heap while fgets takes 24 bytes of input.
Looks like we can use only once (right?). We also see that we control the size of the buffer for our name.
As you have probably figured out we can apply the same method as the bcloud challenge, House of Force. We can leak the heap’s address by inputting 8 byte incorrect secret. We can overflow the Wilderness’s size with editSecret() and we can control the allocation of one (we will see later if it’s just one :P ) huge heap buffer.
The ghetto solution
This solution is dirty and it requires 1 nibble bruteforce. I like it because it makes it unintentional, and I apologize for having brute-forced it (it look us like ~40-50 attempts because of initial miss-calculation). The way we did it is by overwriting the .bss starting at 0x10fb0 which is where the PassphraseBuffer -4 bytes starts (because we have to account for the metadata of the next allocation). Next we allocate a small chunk via updateKey() we overwrite the ptr to this new heap chunk since it’s stored in the .bss under the key variable, with the location of the GOT @0x10f72. This is the tricky part, the 0x10f72 address falls right in the middle of malloc@GOT, exactly 14 bytes away is where atoi@GOT starts. Using the same function updateKey(), on the next run updateKey() writes exactly 16 bytes of data in the GOT. Starting at where our write in the GOT begins, we write 2 bytes on top of MSBs of malloc@GOT, 4 bytes full overwrite of _gmon_start__@GOT, 4 bytes of full overwrite of exit@GOT and 2 bytes overwrite of LSBs of atoi@GOT. This way we are doing a partial overwrite of atoi with the address of system, the randomized MSBs due to ASLR don’t change and only the MSB nibble of our 2 byte overwrite is brute-forced (because this nibble do get affected by ASLR).
One questions is still unanswered thought, “How did we get the right address of system() if we don’t have the server’s libc ?”. Well here we got lucky :), we thought of “Would they be using QEMU to emulate the ARM env?”, and sure enough the libc used on the server was identical to the libc found in https://people.debian.org/~aurel32/qemu/armhf/ image, but also it was on the raspberry pie device we were using with slight difference which would not have made a difference. The other question is “Why would the other MSBs of the full system() address match the atoi() address?”. Because we got lucky again :P, well looking at the distance between atoi() and system() on any of the tested glibcs, we can see that both of them are relatively close to each other.
Once we overwrite atoi with system, we can use the main menu’s getChoice() function to supply the “/bin/sh” argument directly and pop a shell.
Unintended solution’s script
For the intended solution the only difference was to find the full address of system(). Since libc is not provided to us and system() is not in the GOT, we needed to either manually traverse the link_map of the server’s libc or use a tool like pwntools/binjitsu that would do it for us given we have a function that leaks any requested address.
The question here is, how do we get multiple arbitrary read/write operations. First we use House of Force to reach the pointers placed in the bss. Next we allocate a new chunk so we can control them. We overwrite the secret’s ptr with 0x10fb4 so we can keep control of all the pointers there via the editSecret() function. The key’s pointer we overwrite with address of fread@got.
This way we can craft a leak function from updateKey(), by replacing fread@got with printf@plt. So whenever updateKey() is called printf() is going to execute with the key@bss ptr as argument which we control via editSecret().
Next we find the address of system() and we use editSecret() to replace atoi@got with system(). Then on getChoice() in the main menu we simply enter “/bin/sh” as argument and we win :).
Obviously we did this better looking solution after the CTF, so we just tested it locally.