Microcorruption (Embedded Security CTF): New Orleans

One of my former coworkers pointed me to Microcorruption, which is an online (untimed) embedded CTF. How fun!

I started this earlier in the fall, before some travel, and am now returning to it. I’m going to document my solutions for each one, and hopefully complete the CTF. 🙂

What’s this all about?

If you open up the website, you’re greeted with this prompt:

Who wouldn’t want some Cy Yombinator bonds, amirite? Each level requires that you find an exploit in increasingly-more-secure Bluetooth locks, as emulated on the website.

Get signed up for an account, walk through the tutorial, and then click the New Orleans dot on the map to get started.

If you’ve never been to New Orleans IRL, I highly recommend it.

The Challenge

If you’ve gone through the tutorial, some of this will look familiar. When we first get to the New Orleans challenge, we see some release notes for this particular type of lock.

Okay, so there’s no default password (like in the tutorial? I don’t remember).

In the Disassembly window, scroll down until you get to the main function.

Broadly speaking, this program is going to:  

If we run the program once through to see how it works by typing c in the debugger console (to “continue”):

After you hit enter, you should see a password prompt. Let’s guess at the password, with some Midwesterner input:

We now see that the program has stopped here:

So, we type c again in the debugger console, and see that our password was, sadly, invalid.

If you’ve used Microcorruption or a similar program/platform before, this was all obvious to you. I included it for people who are brand new, rusty, etc.

New Orleans Solution

So, ideas on how to get a valid password?

If we take a deeper look at that create_password function, maybe we’ll see something interesting…

Let’s set a breakpoint there by clicking on that line of assembly (such that it turns the styling blue).

If you’re unfamiliar with software breakpoints, check out the Wiki page on it. Tl;dr: it’s a way of pausing program execution so we can get a better look at what’s going on.

Assuming you’ve already reset execution (by typing reset into the debugger console and hitting enter), we can now type c again, to run (continue) the program from the beginning, up until our breakpoint.

You’ll know your breakpoint worked because the text will now be red, in addition to the blue highlighting:

The call <create_password> means that this line of assembly calls the create_password function. Pretty straightforward… let’s take a look at that function.

To do so, from our current breakpoint status, we need to step into the function. Type s (for “step”) in your debugger console, and you should see the disassembly window update to show that the program is now at the first step of the create_password function.

We see a mov line, followed by a bunch of mov.b lines.

447e <create_password>
447e:  3f40 0024      mov	#0x2400, r15
4482:  ff40 6100 0000 mov.b	#0x61, 0x0(r15)
4488:  ff40 4a00 0100 mov.b	#0x4a, 0x1(r15)
448e:  ff40 7400 0200 mov.b	#0x74, 0x2(r15)
4494:  ff40 6500 0300 mov.b	#0x65, 0x3(r15)
449a:  ff40 2600 0400 mov.b	#0x26, 0x4(r15)
44a0:  ff40 4000 0500 mov.b	#0x40, 0x5(r15)
44a6:  ff40 2c00 0600 mov.b	#0x2c, 0x6(r15)
44ac:  cf43 0700      mov.b	#0x0, 0x7(r15)
44b0:  3041           ret

According to the manual (page 13), mov is:

So r15 is set to 0x2400, which is a location in memory (which you can see in the “Live Memory Dump” view). Then, we move some chars into that location, one at a time.

If we repeated step (using s) through, we see:

We’ve written “aJte&@,” to memory location 0x2400. If you take the individual bytes (i.e. “61 4a 74 65 26 40 2c”) and convert them to ASCII, you get the same result.

Let’s hold onto “aJte&@,” and see if we need it later.

If we set a breakpoint at the check_password function (break check_password) or by clicking on the first line of the function, and then continue (c):

44bc <check_password>
44bc:  0e43           clr	r14
44be:  0d4f           mov	r15, r13
44c0:  0d5e           add	r14, r13
44c2:  ee9d 0024      cmp.b	@r13, 0x2400(r14)
44c6:  0520           jne	#0x44d2 <check_password+0x16>
44c8:  1e53           inc	r14
44ca:  3e92           cmp	#0x8, r14
44cc:  f823           jne	#0x44be <check_password+0x2>
44ce:  1f43           mov	#0x1, r15
44d0:  3041           ret
44d2:  0f43           clr	r15
44d4:  3041           ret

We can step through and see that we’re comparing the password we entered against a location in memory (0x2400), one byte at a time. This location is the same one we just wrote to!

More specifically: the password we entered during the prompt was written to location 0x439c. This is the value of r13 in this function.

Then, we look at the byte at the location of r13 (so the byte at 0x439c) and compare it to the r14-th byte of memory location 0x2400. So, for the first time through, we’re looking at the 0th byte, which is “a”.

44c2:  ee9d 0024      cmp.b	@r13, 0x2400(r14)

If it doesn’t match (not equal), we will jump to the 0x44d2 and return from the function. If it does match, we’ll increment r14 such that we’re looking at the next byte of 0x2400, and then loop back to the start of the function and compare again. We’re also seeing if r14 is less than 0x8.

We’ll bail out of this loop as soon as we’ve got an non-matching byte. If all is well, we set r15 to 1, indicating success, and return.

If you want to, you could test this out by having partial matches of the password, and watch as you get kicked out of the byte-compare loop. Or, you could reset the debugger, enter in the correct password, and watch as the lock is opened.

In other words: reset, then c to continue, enter in “aJte&@,” and then c again.

TL; DR

The create_password function spells out the correct password for us.  We just have to grab those bytes, and enter it in when prompted.  

To solve this level, you must first type solve before entering in the correct password of “aJte&@,".

Ta da!