Microcorruption (Embedded Security CTF): Johannesburg

If I’m not already doing these writeups out of order, I will be soon. This post is about the Johannesburg level of Microcorruption.

Now we’re on software version 4:

Towards the bottom, we see:

This is Software Revision 04. We have improved the security of the
lock by ensuring passwords that are too long will be rejected.

We’ll see about that.

Johannesburg Code

Main isn’t very interesting:

Login looks like this:

452c <login>
452c:  3150 eeff      add	#0xffee, sp
4530:  f140 4c00 1100 mov.b	#0x4c, 0x11(sp)
4536:  3f40 7c44      mov	#0x447c "Enter the password to continue.", r15
453a:  b012 f845      call	#0x45f8 <puts>
453e:  3f40 9c44      mov	#0x449c "Remember: passwords are between 8 and 16 characters.", r15
4542:  b012 f845      call	#0x45f8 <puts>
4546:  3e40 3f00      mov	#0x3f, r14
454a:  3f40 0024      mov	#0x2400, r15
454e:  b012 e845      call	#0x45e8 <getsn>
4552:  3e40 0024      mov	#0x2400, r14
4556:  0f41           mov	sp, r15
4558:  b012 2446      call	#0x4624 <strcpy>
455c:  0f41           mov	sp, r15
455e:  b012 5244      call	#0x4452 <test_password_valid>
4562:  0f93           tst	r15
4564:  0524           jz	#0x4570 <login+0x44>
4566:  b012 4644      call	#0x4446 <unlock_door>
456a:  3f40 d144      mov	#0x44d1 "Access granted.", r15
456e:  023c           jmp	#0x4574 <login+0x48>
4570:  3f40 e144      mov	#0x44e1 "That password is not correct.", r15
4574:  b012 f845      call	#0x45f8 <puts>
4578:  f190 4c00 1100 cmp.b	#0x4c, 0x11(sp)
457e:  0624           jeq	#0x458c <login+0x60>
4580:  3f40 ff44      mov	#0x44ff "Invalid Password Length: password too long.", r15
4584:  b012 f845      call	#0x45f8 <puts>
4588:  3040 3c44      br	#0x443c <__stop_progExec__>
458c:  3150 1200      add	#0x12, sp
4590:  3041           ret

For this (and other challenges), I ended up writing up notes for every single line of assembly because I was feeling very rusty. But for now, I’ll just share a general outline of this function.

Buffer Overflowing Ideas

This software revision technically has some changes to address buffer overflows. But it’s not a preventative measure. It’s a way of detecting it after the fact.

To verify this, I typed in “AAAABBBBCCCCDDDDEEEE” as the password, which is 20 chars long. You can see it copied into memory at 0x2400 and then strcpy’d elsewhere with all chars intact.

So, is there a way we can get through this challenge by using a buffer overflow to redirect the program flow, and also clean up our evidence afterwards?

Note: in case it’s not clear, the strategy here doesn’t involve guessing the password or getting it correct. I’ll be ignoring the “incorrect password” output from here on out.

Goal 1: Clean Up Buffer Overflow Evidence

If the program is expecting to see “0x4c” in a certain location, and isn’t stopping us from writing a too-long string let’s write a long string and make sure “4c” is in the correct place.

Where does it need to be? If we look at line 4530 of login, we see that it’s expecting 0x4c at a point 11 bytes past the current sp location.

And where is that? If we look in the memory view:

The green box is where we need to stick the “4c” (technically on the righthand side).

I reset and reran the program with “AAAABBBBCCCCDDDD” and “4c4c” at the end. “4c” is “L” but you can also enter this in using the hex-encoded option. I threw 2 “E"s and 4 more “A"s on the end, too.

The hex encoded-option would look like: “4141 4141 4242 4242 4343 4343 4444 4444 4c4c 4545 4141 4141” (spacing only for readability).

And if we run the program up until line 4578:

4578:  f190 4c00 1100 cmp.b	#0x4c, 0x11(sp)

Then step over. We see that the sr (status register, which holds flags that represent the result of a previous computation) is 0003, indicating that the equality comparison was successful:

Nice. Okay, what next?

Goal 2: Redirect Program Flow

From here, if you step` over a few more times, you’ll skip the “Invalid password length” message because you tricked the program.

Step a few more times, and you’ll get to this line (458c).

If we look at the memory view at this point, we are here:

If we add 0x12 to this–which is 16 in decimal–we’ll end up here (step once to see this in the debugger).

If we had been better people earlier, we wouldn’t have overwritten this part of memory with “E”. If you reset the program you can see that the value used to be “443c”:

If we check out the manual, you’ll see that this value (“443c”) is tellign the program where to return to. So where is 443c?

The end of program execution. Oh no, we don’t want that. We’re don’t have our door open yet!

Back to the EEAAAAs

In the example payload, I had “EEAAAA” at the end, after “4c4c”. The A’s are extraneous, so we can ignore those. But what about the E’s?

Reset the program if you need to and re-enter that payload.

Once you get to line 458c, and you have this memory view:

Step over a couple more times, so that you return from the login function to an address of 4545.

You’ll see this message in the I/O console:

insn address unaligned

Why? Because we went to an invalid address. So what address do we want to go to?

How about this unlock_door function?

Address “4446”. Write that down, we’ll need it.

Johannesburg Solution

So, we have 16 chars of whatever, plus the “4c” check, plus the address we want to go. Don’t forget to consider endianness.

That looks like:

414141414242424243434343444444444c4c4644

So, we successfully completed both goals. We tricked the overflow detection, and we re-routed the program flow from the “end execution” function to the “unlock door” function.

Type solve and then enter in the payload to complete this level.