This is a write-up of my solution to the Microcorruption CTF challenge “Whitehorse” (LOCKIT PRO r c.01).
I found this challenge to be relatively straightforward and easy to solve. Let’s check out what the program is doing.
After the call to main() and then login(), we see a new function named conditional_unlock_door() is being called. After this call returns and if r15 is not 0x0, the message “Access granted” is printed and we return out of the function.
Let’s try some input and examine the state of the memory.
Our input AAAAAAAAAA is placed onto the stack. Following the password buffer, we can see the return address to 0x443c for when we return out of login().
After returning from getsn(), the stack pointer address sp is moved into register r15 and the program calls conditional_unlock_door().
The code will move some values around in the registers, and call the interrupt with code 0x7e (?) and before returning, will execute the instruction mov.b -0x4(r4), r15 which will move a null byte from before our password buffer into r15. Because of this, the tst r15 instruction in login() will fail.
Two things to note. First, we should be able to overwrite the return address 0x443c. Second, conditional_unlock_door() (the way it’s coded) will actually never unlock the door. From the previous challenges, we learned that the code to the interrupt was 0x7f.
However, conditional_unlock_door() uses the code 0x7e, which doesn’t actually unlock the door. Bummer.
But… what if we injected the instructions to the old unlock_door() onto the stack (via our password buffer) and returned execution to it? After all, it’s only 12 bytes long and should fit in the buffer with room to spare before we reach the return address. Let’s whip up our new payload, making sure we fix the address of the call to INT() give it a shot…