I’m not sure exactly when, but Microcorruption has updated its site to include a bunch more levels! How exciting.
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
Let’s try that out in the main program.
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.
c in the right-hand side to get started. Then in the popup window, enter
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 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.
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.
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.
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
Microcorruption Vancouver Solution
To solve, type
solve in the prompt section on the right-hand side.
90909030127f00b012a844. Check the box that says