This is the sixth in a series. You might want to read the previous post before reading this.
This post is based on the Cusco level on microcorruption.com. Like last time, we’re trying to find an input to open a lock without knowing the correct password, using knowledge of assembly language.
First glance
You should know the drill by now. Let’s look at the login
function.
4500: 3150 f0ff add #0xfff0, sp
4504: 3f40 7c44 mov #0x447c "Enter the password to continue.", r15
4508: b012 a645 call #0x45a6 <puts>
450c: 3f40 9c44 mov #0x449c "Remember: passwords are between 8 and 16 characters.", r15
4510: b012 a645 call #0x45a6 <puts>
Adding 0xfff0
to the stack pointer decreases it by 16. That means adding 16 bytes to the stack for use by the current frame. Then the code writes out some text.
4514: 3e40 3000 mov #0x30, r14
4518: 0f41 mov sp, r15
451a: b012 9645 call #0x4596 <getsn>
This should look familiar too - familiar code with a familiar bug. The code gets input by calling getsn
, storing the result at the address of the stack pointer, and allowing up to 0x30 = 48 characters. Which is 32 more bytes than was just added to the stack frame to allow for it.
We’ve already seen in a previous episode that the first two bytes (on a 16-bit CPU) beyond the current stack frame are where the return address is stored. When ret
is called from a stack frame, it will be those two bytes that the CPU returns to.
And it’s those two bytes that will be the first we overwrite with a password beyond 16 characters. What address would we like the function to return to? Well, unlock_door
at 0x 4446
sounds like a good candidate. We have to change the order of the bytes because addresses are stored in a little-endian way. So we just need an input with 0x4644
in the 17th and 18th bytes. I used 0x0102030405060708090a0b0c0d0e0f104644
.
Endnotes
Lots of this was similar to code we’ve seen before. Buffer overflows from an input that wasn’t correctly sanitised, and overwriting return values. Next time: the myth of security through obscurity.