When I was bored, I was reading an open source book project initiated by the “Digital Intelligence Security” Research Institute, mainly about a manual about iot security, which introduced a smart door lock CTF. Microcorruption is a smart lock online CTF “game” made by Matasano Security and Square. This CTF focuses on embedded security and challenges players to reverse engineer a fictitious “Lockitall LockIT Pro” lock system.
How it all works:
tl;dr: Given a debugger and a device, find an input that unlocks it. Solve the level with that input.
You’ve been given access to a device that controls a lock. Your job: defeat the lock by exploiting bugs in the device’s code.
You’re playing “Capture The Flag”. You collect points for each level you beat, working your way through steadily more complicated vulnerabilities. Most levels showcase a single kind of real-world software flaw; some levels chain a series of them together.
This device has a simple input: you provide a passcode, and if the passcode is correct, the lock unlocks. Just one problem: you don’t know the passcode. Unlock it anyways.
We’ve done the tedious work for you: we got the device working, hooked a rudimentary debugger up to it, dumped the code for each level and disassembled it for you.
You’ll use the debugger to reverse-engineer the code for each level. You can provide the device with input, then step through the code watching what the device does what that input. You’re looking for a specific input that unlocks the device. Maybe that input is the correct passcode. More likely, though, it’s something else: an input that exploits a bug in the device’s code.
This blog post will record my process of solving door locks in “all over the world”. So far I have broken two door locks, both of them are simple, and I am very lazy so I only record from the next door lock :)
Sydney
Firstly enter the command “c” to let the debugger interrupt where the password is entered, enter a password at random and observe its execution flow in the program.
Enter the command “f” to let the pc finish executing the current function and return to the main function. The “check_password” function below should be very interesting.
After entering “check_password” function, you can see that there is the following comparision, which compares the two characters starting from the 0th, 2nd, 4th, and 6th digits of our input, respectively.
So the password in hexadecimal should be as follows: 3c31734d51263857. i.e. “<1sMQ&8W”.
Oh my bad, the MSP430 uses the instruction set is a little-endian, so the data in memory still need to swap.
Hanoi
The first test is still performed by entering a random password.
This function may be of interest to us, go in and take a look.
The fution calls another funciton, and notice that the address where we entered the password is also pushed onto the stack before.
Looking at the output, it seems that this function just checked the length of the password we entered or something like that.
The key code seems to be in this position: both to ensure that the password length is between 8 and 16 bits, and that the 17th bit has to do a comparision, which should be the familiar pwn technique.
Bypass directly with 00 truncation.
Cusco
We just noticed that there is an interrupt here and we checked the manual to find the following information:
INT 0x7D.
Interface with the HSM-1. Set a flag in memory if the password passed in is correct.
Takes two arguments. The first argument is the password to test, the second is the location of a flag to overwrite if the password is correct.
The judgment logic of the main function is “test r15”, but when we go to the place shown in the figure, we find that r15 is always 0. Are we going to hijack the flow of the program?
Put a segment at the ret of the main function to see if the address of the ret we can control.
Oh dear, this place is really controllable, then everything is easy, just change the ret address to unlock address directly.
offset = 0x10
And the address of unlock:
4528: b012 4644 call #0x4446 <unlock_door> |
payload = (hex)616161616161616161616161616161614644
Reykjavik
When the password was entered, the pc pointer pointed to a strange location.
By looking at the main function, it seems that some encryption has been done to the code. Then we will not let the program run first and locate the enc function directly to observe its logic.
You can see that the first thing is to loop 0x100 times to fill the memory with this data somewhere. This memory is later processed in various ways, and we jump out of the function to see the result after encryption.
Debug cannot parse the code pointed to by pc, we disassemble it manually.
0b12 push r11 |
We can find that the code segment “call #0x2464” was called in many places.
The code segment :
1e41 0200 mov 0x2(sp), r14 |
The program calls the interrupt for user input in the subsequent execution. After entering the password, there is a “cmp” instruction that follows, which can be seen to compare with the password we entered. Notice the value of the pc is 2448 now. So we can break at 2448, then we can input “#021a” to observe the program.
Oh! Sure enough, that’s it.
Whitehorse
When set a breakpoint at ret of the function, we can find that ret address can be overflowed so we can hijack the program flow.
The verfication of the password relies on the “r15” register. So we can hijack the program flow to control the “r15” register.
But after testing, I found that hijacking the “r15” register seemed a bit difficult, so I changed my mind and hijacked it to where the interrupt is handled. According to the user manual, just pass in parameter “0x7f”.
payload: 6161616161616161616161616161616160447f
Montevideo
(To be continued.)