Microcorruption (Embedded Security CTF): Sydney

Up next, Sydney!   Our prompt says…

Wait, what was that last bit?

We have received reports that the prior  version of the lock was  bypassable without knowing the password. We have fixed this and removed the password from memory.

Booooooooo ☹️

Sydney Program Structure

So, if we check out the main section of the code again, we can get an idea of how this program works, and what might be different (compared to the New Orleans level).

So it isn’t too different from last time, aside from the fact that this level has no create_password function. Generally speaking, this program:

Nothing too different from the previous level. Let’s see if we can figure out how it determines if a password is valid or not.

That check_password function looks interesting…

Checking Passwords

I set a breakpoint in the check_password function. You can either click on line 448a, or type break check_password in the debugger console. Reset the program if you need to, and then type c to continue until you get to your breakpoint. I gave the program a password attempt of “AAAAAAAA”

The first line says to compare the value 0x7d71 with the value at the address of r15 (starting with the 0th byte).

What is that address? Take a peek at the Register State view.

R15 is (hex) “439c”. If we look at our Live Memory Dump:

See where all the “41"s start? That’s the hex value for ASCII character “A”, which is what I entered as the password. If you count over from 4390, you see that the “A"s start at… 0x439c. Yay.

Okay, so we’re comparing 0x7d71 with the first two bytes of memory at 0x439c… which are “AA” or “0x4141”. We can type s to step to the next instruction. Okay, so that comparison is obviously not going to work. Because our comparison failed, we’ll get kicked down to 0x44ac where r14 is cleared, moved into r15, and we return.

Before we go, however, let’s see what happens when we know the answer is wrong. Another way of asking this is: how does the microcontroller know what the result of the cmp instruction was? It looks at the status register or sr. You may have noticed that before the cmp instruction, the value of sr was 0000. Now, it’s 0004.

If we reset and try again, and enter in “}q”, which is the ASCII equivalent of “7d 71″… we see… oh no. The sr is equal to 0004 again. What’s going on?

The issue is the order in which we’re providing the bytes.

Even though it looks right (see the 7d 71 in the image above), it’s out of order to the computer. This is called endianness.

It’s a bit of a “reading right-to-left vs left-to-right” problem, but for computers. What it means for us is that we have to swap our order. If we reset and enter in “q}” instead of “}q”, and continue to our breakpoint:

Once we’re at our breakpoint, type s to step to the next instruction. Our sr register is set to 0003 this time. Interesting!

You can read more about the status register and conditional flags here. The difference in sr values means that we’re on the right path.

Continuing On…

If we look at the check_password function again, we can get an idea of what we need to provide as a password.

448a <check_password>
448a:  bf90 717d 0000 cmp	#0x7d71, 0x0(r15)
4490:  0d20           jnz	$+0x1c
4492:  bf90 6672 0200 cmp	#0x7266, 0x2(r15)
4498:  0920           jnz	$+0x14
449a:  bf90 5c5f 0400 cmp	#0x5f5c, 0x4(r15)
44a0:  0520           jne	#0x44ac <check_password+0x22>
44a2:  1e43           mov	#0x1, r14
44a4:  bf90 4939 0600 cmp	#0x3949, 0x6(r15)
44aa:  0124           jeq	#0x44ae <check_password+0x24>
44ac:  0e43           clr	r14
44ae:  0f4e           mov	r14, r15
44b0:  3041           ret

We need 0x7d71, adjusted for endianness, and then 0x7266 (also adjusted), and then 0x5f5c (yep, also adjusted).

That is… “q}fr_” Wow. This password’s got entropy. After resetting, and entering that password, we can get all the way to this line:

We’ve moved a value of “1” into r14, which could give us a return value of “true” or “1”, meaning that we gave the correct password.

But… we’ve still got one hurdle left to clear. The last two bytes of the password are compared to 0x3949. Let’s add that to our password and try again: “q}fr_I9”

Sydney Solution

TL;DR The password, as we’ve found by looking at the comparison of two bytes of input at a time, is “q}fr_I9”. We had to consider endianness, and also use the status register comparison flags to help us find our way.

To solve the level, type “solve” and then enter in “q}fr_I9” as the password. Yay!