Microcorruption - Cusco#
Microcorruption gives us a debugger and a password to unlock a device, our job is to find the password reading through assembly code and using some reverse engineering tricks. Each challenge is named after a city, each one more difficult than the previous one.
If you didn’t solve the challenge for yourself, I recommend that you stop reading, solve the challenge and check my solution afterwards, do not spoil the challenge and have fun.
How to solve it#
First of all, the main function just redirects to the login function, so let’s check what it does:
login:
add #0xfff0, sp
mov #0x447c "Enter the password to continue.", r15
call #0x45a6 <puts>
mov #0x449c "Remember: passwords are between 8 and 16 characters.", r15
call #0x45a6 <puts>
mov #0x30, r14
mov sp, r15
call #0x4596 <getsn>
mov sp, r15
call #0x4452 <test_password_valid>
tst r15
jz $+0xc <login+0x32>
call #0x4446 <unlock_door>
mov #0x44d1 "Access granted.", r15
jmp $+0x6 <login+0x36>
mov #0x44e1 "That password is not correct.", r15
call #0x45a6 <puts>
add #0x10, sp
retthe login function does the following:
- print a text asking for a password (8 to 16 characters)
- asks for a password
- check if the password is valid
- print the result (if was a success or not)
- finish the function with a return
The console asks us for a password between 8 and 16 characters. Let’s check if 16 characters is the limit for our input, I’m gonna write a bunch of sevens and then check the memory:
43e0: 5645 0000 a045 0200 ee43 3000 1e45 3737 VE...E...C0..E77
43f0: 3737 3737 3737 3737 3737 3737 3737 3737 7777777777777777
4400: 3737 3737 3737 3737 3737 3737 3737 3737 7777777777777777
4410: 3737 3737 3737 3737 3737 3737 3737 0045 77777777777777.EWell, we can actually write 48 characters in this input, way above the expected, reaching from the address 0x43ee to the address 0x441d.
The problem is, in any way shape or form, the function test_password_valid make an interaction with our input interval. Even if you track the r15 to see what it does, the compare seems to never happen:
call #0x4452 <test_password_valid>
tst r15
jz $+0xc <login+0x32>
call #0x4446 <unlock_door>
mov #0x44d1 "Access granted.", r15In another words, the register r15 is always zero, making it skip the unlock_door function, the one we desire.
But, after that, the program seems to halt. Ok, let’s check that again.
What we can see is that the RET (return) operation is calling the return at the address 0x43fe, that means 2 things.
- If we input more than 16 characters into the password, this data is corrupted by our sevens
- The return position call is between
0x43eeand0x441d, making possible for us to manipulate this return.
So, let’s try to change the return to send us to unlock_door function, let’s find the address for the unlock_door function first:
4446 <unlock_door>
4446: 3012 7f00 push #0x7f
444a: b012 4245 call #0x4542 <INT>
444e: 2153 incd sp
4450: 3041 retSo the address is 0x4446, let’s inject this address to the position 17 of bytes (16 bytes of zeros + 1 byte of unlock_door address).
Remember to invert the bytes, the address is 0x4446, but our input will be 0x4644:
000000000000000000000000000000004644With that, the return operation from the login function will return to the unlock_door function, opening the door.