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:
3030
jn $+0x62
3233
jn $-0x19a
3034
jge $+0x62
0000
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:
- Three bytes of filler. The first two bytes need to be a valid, unused address. The third byte, we don’t care.
- Assembly instructions to unlock the lock.
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!