blog/04-microcorruption

04 Microcorruption - Cusco

Next destination is Cusco

Manual
Lockitall                                            LOCKIT PRO r b.02
______________________________________________________________________

              User Manual: Lockitall LockIT Pro, rev b.02
______________________________________________________________________


OVERVIEW

    - We have fixed issues with passwords which may be too long.
    - This lock is attached the the LockIT Pro HSM-1.

DETAILS

    The LockIT Pro b.02  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 02. We have improved the security of the
    lock by  removing a conditional  flag that could  accidentally get
    set by passwords that were too long.


(c) 2013 LOCKITALL                                            Page 1/1

Now the claim to have fixed the previous vulnerability, but still using the same external hardware module. So still no passwords in the code.

Enumeration

Once again, let’s check the code for bugs that we might be able to use

Main

We start in main

int main(int argc, char **argv, char **envp);
0x4438      call  #login

Surprisingly this time main is even shorter than last time

login

╭ login();
0x4500      add   #0xfff0, sp

This time login create some room on the stack, let try to find out how much.

From the __init_stack in the prologue the stack pointer is set to 0x4400, and nothing else is touching SP before this line in login, or is there?

The only instruction in main is a call instruction, but what exactly does the call instruction do?

Door unlocked

Reading the MSP430 User manual1 we can read what call does.

  1. The destination (here the address of login) is stored temporarily
  2. 2 is subtracted from the stack pointer
  3. The Program Counter is written to the address pointed to by the stack pointer
  4. The destination address is the written to the Program Counter

Spelled out a bit more, the stack pointer is moved 2 bytes to make room for the address that comes after the call instruction. The point of this is to save the return address, meaning the address that the called function needs to return to when it is reach its end. To do the actual jump, the address of the function is moved into the program counter. The program counter holds the address of the next instruction to execute and is incremented every time. So by writing to the program counter makes the program jump.

Back to the stack pointer. It is initialized to 0x4400 then the call instruction subtract 2 and writes the return address.

Address  Stack Content
         +---------+   Lower memory addresses
         |   ...   |
         +---------+
 0x43FC  | 0x0000  |
         +---------+
 0x43FE  | 0x3C44  |   (SP-2) after the call instruction
         +---------+
 0x4400  | 0x3140  |   Initial SP
         +---------+
 0x4402  | 0x0044  |
         +---------+
         +---------+   Higher memory addresses

The above is an illustration of the stack after the call login instruction, and now we finally come to the first instruction of login where we add 0xFFF0 to SP, so 0xFFF0 + 0x43FE = 0x143EE we have seen this before, the calculation overflows, so the SP will end up with the truncated value 0x43EE, we could have gotten here by subtracting 0x10 or 16 from the stack pointer, the new model of the stack now looks like this

Address  Stack Content
         +---------+   Lower memory addresses
 0x43EE  | 0x0000  |   (SP-18) after login moves SP
 0x43F0  | 0x0000  |
 0x43F2  | 0x0000  |
 0x43F4  | 0x0000  |
 0x43F6  | 0x0000  |
 0x43F8  | 0x0000  |
 0x43FA  | 0x0000  |
 0x43FC  | 0x0000  |
         +---------+
 0x43FE  | 0x3C44  |   (SP-2) after the call instruction in main
         +---------+
 0x4400  | 0x3140  |   Initial SP
         +---------+
         +---------+   Higher memory addresses

This is a very common way to work with the stack, when a call to a function is made, the return address is pushed to the stack, for the called function to know where to return to, then at the start of the called function, it will make room on the stack for local variables. At the end of the function it will move the stack pointer back to where it started, then the ret will pop the return address of the stack and set the program counter to that address. This is called a stack frame, the idea is that each function has its own stack frame for local variables. For more info start here Call Stack

Back to login

0x4504      mov   #0x447c, r15
           ; "Enter the password to continue."
0x4508      call  #puts
0x450c      mov   #0x449c, r15
0x4510      call  #puts
           ; "Remember: passwords are between 8 and 16 characters."
0x4514      mov   #0x0030, r14
0x4518      mov   sp, r15
0x451a      call  #getsn

This looks familiar, we print some text and then asks for the password, no magic here. Notice that we are asking for maximum password length of 16 bytes, which fix exactly into the free space we just created on the stack. If we look closely, we see that two arguments are prepared for getsn, R15 is where to save the input data, R14 is how many bytes to read from the user. R14 is set to the value 0x30, but we only have space on the stack for 0x10 bytes. So the getsn will read 32 bytes more that we actually have room for in the stack frame.

0x451e      mov   sp, r15
0x4520      call  #test_password_valid
0x4524      tst   r15

And as in Hanoi we call the test_password_valid on our password buffer. And test the result.

│       ╭─< 0x4526      jeq   $+0x000c
│       │   0x4528      call  #unlock_door
│       │   0x452c      mov   #0x44d1, r15
│      ╭──< 0x4530      jmp   $+0x0006
│      │╰─> 0x4532      mov   #0x44e1, r15
│      ╰──> 0x4536      call  #puts

The code now branches, if the password is incorrect, we jump, put the address of the string That password is not correct. into R15 and call puts on it.

If the password is correct, unlock_door is called and the address of the string Access granted. is moved into R15 and puts is called.

The vulnerable compare from before is gone, so we cannot exploit that, and we don’t know the password. So they did actually fix the vulnerability from before.

0x453a      add   #0x0010, sp
0x453e      ret

At the end we see that we are now adding 0x10 to the stack pointer. We are now “cleaning” up the stack frame. We then call ret, this will move the value on the stack into the program counter, and then add 2 to the stack pointer, hence successfully returning to main or are we?

Exploit

It’s a bit hard to solve this without knowing anything about buffer overflows beforehand, but if you play around with the password input in the online debugger, and maybe violate the rule about maximum 16 byte password length, after all the getsn function is instructed to read 48 bytes of password data. You will notice that you start to overwrite the data that comes after the “allocated” stack frame.

Address  Stack Content
         +---------+   Lower memory addresses
 0x43EE  | 0x0000  |   (SP-18) after login moves SP
 0x43F0  | 0x0000  |
 0x43F2  | 0x0000  |
 0x43F4  | 0x0000  |
 0x43F6  | 0x0000  |
 0x43F8  | 0x0000  |
 0x43FA  | 0x0000  |
 0x43FC  | 0x0000  |
         +---------+
 0x43FE  | 0x3C44  |   (SP-2) after the call instruction in main
         +---------+
 0x4400  | 0x3140  |   Initial SP
         +---------+
         +---------+   Higher memory addresses

Recall our stack model here. The password we input will be stored on the stack, starting at address 0x43EE going down. We only have 16 bytes of unused space, so byte 17 and 18 will actually overwrite the value in 0x43FE.

This value is quite interesting, because this is actually the address from where the program will begin to execute code when it returns from login. So we can create a payload/password that will make the program jump to where ever we want and execute code there, sounds awesome.

But where do we want it to jump to? The unlock_door of course. So we create our payload 16 ‘A’ and then the address of the unlock_door function ‘0x4446’

Overwrite

When using this payload, the online debugger actually tells us that the __init_stack get overwritten, this is because a null byte is added to our input, so even tho our input is only 18 bytes and should only overwrite the return address, we actually end up having a 0x00 at the 19 byte position, and that is at address 0x4400

Door unlocked


1

User Guide

/microcorruption/