Microcorruption (Embedded Security CTF): Vancouver

I’m not sure exactly when, but Microcorruption has updated its site to include a bunch more levels! How exciting.

Release Notes

As always, we’ll start with the release notes. This one says they’ve got a new design (what else is new!) and it takes a special debug code.

They give us an example code of 8000023041.

Let’s try that out in the main program.

Debug Codes

Before we do that, though, let’s find a good spot for a breakpoint. The main function starts out like this:

And then towards the end, if we get past some length checks, we call r11. We don’t know what that is yet, but let’s click that line to add a breakpoint.

Type c in the right-hand side to get started. Then in the popup window, enter 8000023041.

Then type c again to continue until our breakpoint.

Once we hit that, we can type step to single-step into wherever this call r11 will lead us.

Call of the r11

That’s a Jack London reference for ya.

With the debug input code of “8000023041”, we see that r11 is equal to 3830 at the time of the call r11.

If we call that value, we jump down to address 3830 in code, which has the following contents:

3830: 3030 3233 3034 3100 0000 0000 0000 0000   0023041........

I then stepped through this code, which disassembles to the following MSP430 instructions:

jn $+0x62
jn $-0x19a
jge $+0x62
rrc pc

I then started reading the MSP430 manual that the site links to, like a totally normal person, wondering why we’re doing a jn (jump if negative) and jge (jump if greater or equal) with no previous compare. Like, where did the negative bit even get set?

Then there’s an rrc pc and I start thinking about ways that I could use this instruction to later move pc bytes around and jump somewhere.

Then it finally dawns on me: OHHHH… it’s executing our debug input as shellcode.

8000023041 in hex is 38 30 30 30 30 32 33 30 34 31.

The 3830 gets turned into the address. Then we lose a byte, somewhere. Then the rest of it gets turned into shellcode to execute.

That means we’ve got carte blanche to make the program execute whatever we want, as long as we can pass the length check.

Time to write some assembly!

Classic move on my part, making this way harder than it needs to be.

We need to have:

You may remember from previous levels and the lock manual that a 0x7F interrupt unconditionally unlocks the lock.

Specifically, this level shows an example of the assembly instructions that make that happen:

push #7f
call <INT>

Pretty simple, we push #7f onto the stack and then call the address of the interrupt function, which is at #44a8. You can either look at the op codes from the linked level, or use the disassembler tool to generate your own.

3012 7f00      push    #0x7f
b012 a844      call    #0x44a8

If you adapted the code from the previous level, did you forget to account for endianness of the interrupt function address? Whoops. Make sure that 44a8 address becomes a844 in your opcodes.

That leaves us with: 3012 7f00 b012 a844 for our instructions.

Vancouver Strategy

As mentioned earlier, we need two bytes that will be used as the address for our shellcode, one filler byte, and then our instructions to move 0x7f onto the stack and call the interrupt.

Let’s use 90 for our two address bytes and one filler byte because there’s nothing else at location 0x9090 and, why not.

All together our input is: 90 90 (address) + 90 (filler) + 3012 7f00 b012 a844 (0x7f interrupt to unlock), or 90909030127f00b012a844.

Microcorruption Vancouver Solution

To solve, type solve in the prompt section on the right-hand side.

Enter 90909030127f00b012a844. Check the box that says hex-encoded.

Ta da!