03 Microcorruption - Hanoi
We now take a trip from Sydney up to Hanoi
Manual
Lockitall LOCKIT PRO r b.01
______________________________________________________________________
User Manual: Lockitall LockIT Pro, rev b.01
______________________________________________________________________
OVERVIEW
- This lock is attached the the LockIT Pro HSM-1.
- We have updated the lock firmware to connect with the hardware
security module.
DETAILS
The LockIT Pro b.01 is the first of a new series of locks. It is
controlled by a MSP430 microcontroller, and is the most advanced
MCU-controlled lock available on the market. The MSP430 is a very
low-power device which allows the LockIT Pro to run in almost any
environment.
The LockIT Pro contains a Bluetooth chip allowing it to
communiciate with the LockIT Pro App, allowing the LockIT Pro to
be inaccessable from the exterior of the building.
There is no default password on the LockIT Pro HSM-1. Upon
receiving the LockIT Pro, a new password must be set by first
connecting the LockitPRO HSM to output port two, connecting it to
the LockIT Pro App, and entering a new password when prompted, and
then restarting the LockIT Pro using the red button on the back.
LockIT Pro Hardware Security Module 1 stores the login password,
ensuring users can not access the password through other means.
The LockIT Pro can send the LockIT Pro HSM-1 a password, and the
HSM will return if the password is correct by setting a flag in
memory.
This is Hardware Version B. It contains the Bluetooth connector
built in, and two available ports: the LockIT Pro Deadbolt should
be connected to port 1, and the LockIT Pro HSM-1 should be
connected to port 2.
This is Software Revision 01, allowing it to communicate with the
LockIT Pro HSM-1
(c) 2013 LOCKITALL Page 1/1
Reconnaissance
The manual here states that this lock is now connected to some external hardware, the HSM-1 (Hardware Security Module). If we take a look in the Lockitall Manual1 we find the following about the HSM-1
This is great new for them, much better to not hard-coded the password in the code, it does however present a challenge for us. We want to open the lock, and as before we don’t know the password, but this time we will not be able to find the password in the code, so we have to find a way to open the lock without the password.
Enumeration
Let’s see if we can find some way to do this by looking at the code
Main
We start in main
╭ int main(int argc, char **argv, char **envp);
│ 0x4438 call #login
╰ 0x443c clr r15
Well, that fast easy, lets look at login
login
╭ login();
│ 0x4520 clr.b &0x2410
Here we clear a byte a 0x2410
, this seems a bit strange. Nothing has touched this byte before this function, let’s keep 0x2410
in mind
│ 0x4524 mov #0x447e, r15
│ ; "Enter the password to continue."
│ 0x4528 call #puts
│ 0x452c mov #0x449e, r15
│ ; "Remember: passwords are between 8 and 16 characters."
│ 0x4530 call #puts
│ 0x4534 mov #0x001c, r14
│ 0x4538 mov #0x2400, r15
│ 0x453c call #getsn
This part we have seen before, the code prints two times, a new part tho is that we now get a hint about the password length being between 8 and 16 that is new. After the prints the code asks us to input a password. Notice the 0x2400
argument to the getsn
function. This is 16 bytes before the 0x2410
byte that this function cleared in the start, and our password limit is 16, seems interesting.
│ 0x4540 mov #0x2400, r15
│ 0x4544 call #test_password_valid
│ 0x4548 tst r15
Next we pass the password buffer to test_password_valid
and test if R15
is zero, here R15
must be the return value of the test_password_valid
function, and whether the password was valid or not
│ ╭─< 0x454a jeq $+0x0008
│ │ 0x454c mov.b #0x006d, &0x2410
│ ╰─> 0x4552 mov #0x44d3, r15
│ 0x4556 call #puts
If test_password_valid
return a zero in R15
we jump over one instruction. This instruction is messing with the 0x2410
address we saw in the start, moving 0x6D
into its place. Afterward we do another print.
│ 0x455a cmp.b #0x0002, &0x2410
│ ╭─< 0x4560 jnz $+0x0010
│ │ 0x4562 mov #0x44f1, r15
│ │ 0x4566 call #puts
│ │ 0x456a call #unlock_door
│ │ 0x456e ret
│ ╰─> 0x4570 mov #0x4501, r15
│ 0x4574 call #puts
╰ 0x4578 ret
The last piece of the code compare this mystery address with the value 0x02
which seem a bit strange. This function started by clearing it, so it contains zero, then depending on the result of test_password_valid
it will contain either 0x6D
or keep the zero. Reading forward we see that we need this comparison to be true
for the code to call unlock_door
test_password_valid
Let’s take a look at test_password_valid
to see if this function will write 0x02
to 0x2410
╭ test_password_valid();
│ 0x4454 push r4
│ 0x4456 mov sp, r4
│ 0x4458 incd r4
│ 0x445a decd sp
│ 0x445c clr.b 0xfffc(r4)
│ 0x4460 mov #0xfffc, r14
│ 0x4464 add r4, r14
│ 0x4466 push r14
│ 0x4468 push r15
│ 0x446a push #0x007d
│ 0x446e call #INT
│ 0x4472 mov.b 0xfffc(r4), r15
│ 0x4476 sxt r15
│ 0x4478 add #8, sp
│ 0x447a pop r4
╰ 0x447c ret
It does not look like this code is doing anything with the value in 0x2410
Exploit
Remember that the lock tells us that the password length is between 8 and 16, and the 0x2410
is 16 bytes after the where our password is stored. What if we input a password that has 17 bytes? Then we would be the ones writing to that 0x2410
address. This kind of vulnerability is known as an overflow. We are inputting more data than the code expects, and the code does not check and handle that case, so the input is just written to memory.
Let’s try to use the web debugger and input the character ‘A’ 17 times
0160: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0170: *
2400: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
2410: 4100 0000 0000 0000 0000 0000 0000 0000 A...............
2420: 0000 0000 0000 0000 0000 0000 0000 0000 ................
2430: *
43f0: 8e45 0000 d845 0200 0024 1c00 4045 3c44 .E...E...$..@E<D
Looking at the Live Memory Dump in the web debugger, right after we have inputted the A’s we can see them starting at address 0x2400
and surprise, the last ‘A’ is at the 0x2410
. Recall from the login
we want a 0x02
here, so if we input 16 characters and then a 0x02
the unlock_door
should be called. NOTE: there is a checkbox when inputting the password where we can tell it to take raw hex input.
From my reverse engineering this code will no work even with the correct password, nothing in the code sets address 0x2410
to 0x02
, if you see that I have missed something please write me