Microcorruption (Embedded Security CTF): Hanoi

Damn, Daniel, back it again with more CTFing. Yes. CTFs never end.

This level is Hanoi, and our message this time says some things about hardware:

Further down-screen, the message reads:

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.

As mentioned on the New Orleans level, I’ve already solved a bunch of these and am posting a cleaned-up and more sane version of the notes I took during the levels. After a certain point, multiple cities will open up after you solve a previous city. So, you might do the levels in a slightly different order than me.

Testing things out

If we start the level, and type c to continue program execution, we’re prompted for a password. Interesting that it limits the password length to 8-16 characters.

I type 16 “A"s and hit enter. If I scroll through the memory management window, I see that my password ended up in memory at 0x2400.

“AAAAAAAAAAAAAAAA” was not the correct password, of course.

Hanoi Code

If we look at main, we see:

4438 <main>
4438:  b012 2045      call	#0x4520 <login>
443c:  0f43           clr	r15

In other words, virtually all the logic is in the login function. Let’s see what’s going on there.

4520 <login>
4520:  c243 1024      mov.b	#0x0, &0x2410
4524:  3f40 7e44      mov	#0x447e "Enter the password to continue.", r15
4528:  b012 de45      call	#0x45de <puts>
452c:  3f40 9e44      mov	#0x449e "Remember: passwords are between 8 and 16 characters.", r15
4530:  b012 de45      call	#0x45de <puts>
4534:  3e40 1c00      mov	#0x1c, r14
4538:  3f40 0024      mov	#0x2400, r15
453c:  b012 ce45      call	#0x45ce <getsn>
4540:  3f40 0024      mov	#0x2400, r15
4544:  b012 5444      call	#0x4454 <test_password_valid>
4548:  0f93           tst	r15
454a:  0324           jz	$+0x8
454c:  f240 5800 1024 mov.b	#0x58, &0x2410
4552:  3f40 d344      mov	#0x44d3 "Testing if password is valid.", r15
4556:  b012 de45      call	#0x45de <puts>
455a:  f290 0400 1024 cmp.b	#0x4, &0x2410
4560:  0720           jne	#0x4570 <login+0x50>
4562:  3f40 f144      mov	#0x44f1 "Access granted.", r15
4566:  b012 de45      call	#0x45de <puts>
456a:  b012 4844      call	#0x4448 <unlock_door>
456e:  3041           ret
4570:  3f40 0145      mov	#0x4501 "That password is not correct.", r15
4574:  b012 de45      call	#0x45de <puts>
4578:  3041           ret

In broad strokes, let’s describe what’s going on here.

In 4520 to 4540, we’re prompting the user for a password, and reminding them that there’s a 8-16 char limit. Then we’re reading in their input, and storing it in memory at 0x2400.

Next, we’re calling test_password_valid, presumably to test if the password is valid.

Then, we tell the user we’re testing their password. Depending on the results, we say it’s invalid, or that we’re granting access, and display that message. Lots of puts calls all over the place.

test_password_valid

That function name looks intriguing, right? Let’s check it out.

After poking around with this function a bit, I’m still not sure how it’s useful to us. But, if you step through it, you can see how the interrupt-to-unlock functionality works. That’s the 0x7d line onwards. You can read more about it in the Embedded CTF manual on page 9.

So if that doesn’t help us, what next? If you keep stepping, you’ll eventually return back to the login function.

Logging In…

This next part is interesting (for real this time!)

After we’ve returned from our test_password_valid goose chase, we skip over the mov.b call, and print out “Testing if password is valid.”

Oh, so NOW we’re testing it? If we hadn’t skipped over that mov.b line, we’d have 0x58 in location 0x2410. But… we don’t, and it’s not entirely clear how we’d get to that line anyway.

It doesn’t matter though, since the next line (at 455a) is comparing the contents at address 0x2410 with “0x4”. And 0x2410 is pretty close to the location of our password, right? Our password is at 0x2400

I know the program said we have to enter 8-16 characters, but what happens if we don’t follow the rules?

AAAAAAAAAAAAAAAA(B)

If we reset the program, and this time enter in 16 A’s, followed by one B (just to make it easier to see):

Now we’ve got a 42 (“B”) at location 0x2410. We entered in “too many” characters, broke the rules, and it let us!

Let’s replace that 42 with a 0x4 instead, so we can pass our cmp.b call.

But 0x4 isn’t a readable ASCII character. We’ll have to use hex-encoded input instead.

Hanoi Level Solution

So, let’s craft our response using the hex-encoded input option.

If we check the hex input box, we need to send “41” instead of “A”, and so on.

That means we want 16 “41"s in a row, followed by “04”.

Does it matter that the first 16 bytes are 41? No, it doesn’t. Put whatever you want.

Hit send, and continue through any breakpoints. We just buffer overflowed our way to success. : )

TL;DR

If we give a hex-encoded input of “4141414141414141414141414141414104”, and overwrite the comparison value, we can get in!

Type solve, and then rerun the program to complete the Hanoi level.