Microcorruption (Embedded Security CTF): Reykjavik

Another day, another revision of the Lockitall Bluetooth lock. This writeup is for the Reykjavik level, which I hopefully will be able to spell without hesitation by the end of this blog post.

Software Updates

The Lockitall release notes are getting a bit cheeky:

This is Software Revision 02. This release contains military-grade
encryption so users can be confident that the passwords they enter
can not be read from memory.  We apologize for making it too easy
for the password to be recovered on prior versions.  The engineers
responsible have been sacked.

Poor engineers. Well, time to ruin their day again.

Reykjavik Code

If we take a peek at main, it’s a bit different from other levels:

That’s it. Moving some memory around, calling enc, and then we’re done. Huh.

If we look at enc, we see:

4486 <enc>
4486:  0b12           push	r11
4488:  0a12           push	r10
448a:  0912           push	r9
448c:  0812           push	r8
448e:  0d43           clr	r13
4490:  cd4d 7c24      mov.b	r13, 0x247c(r13)
4494:  1d53           inc	r13
4496:  3d90 0001      cmp	#0x100, r13
449a:  fa23           jne	#0x4490 <enc+0xa>
449c:  3c40 7c24      mov	#0x247c, r12
44a0:  0d43           clr	r13
44a2:  0b4d           mov	r13, r11
44a4:  684c           mov.b	@r12, r8
44a6:  4a48           mov.b	r8, r10
44a8:  0d5a           add	r10, r13
44aa:  0a4b           mov	r11, r10
44ac:  3af0 0f00      and	#0xf, r10
44b0:  5a4a 7244      mov.b	0x4472(r10), r10
44b4:  8a11           sxt	r10
44b6:  0d5a           add	r10, r13
44b8:  3df0 ff00      and	#0xff, r13
44bc:  0a4d           mov	r13, r10
44be:  3a50 7c24      add	#0x247c, r10
44c2:  694a           mov.b	@r10, r9
44c4:  ca48 0000      mov.b	r8, 0x0(r10)
44c8:  cc49 0000      mov.b	r9, 0x0(r12)
44cc:  1b53           inc	r11
44ce:  1c53           inc	r12
44d0:  3b90 0001      cmp	#0x100, r11
44d4:  e723           jne	#0x44a4 <enc+0x1e>
44d6:  0b43           clr	r11
44d8:  0c4b           mov	r11, r12
44da:  183c           jmp	#0x450c <enc+0x86>
44dc:  1c53           inc	r12
44de:  3cf0 ff00      and	#0xff, r12
44e2:  0a4c           mov	r12, r10
44e4:  3a50 7c24      add	#0x247c, r10
44e8:  684a           mov.b	@r10, r8
44ea:  4b58           add.b	r8, r11
44ec:  4b4b           mov.b	r11, r11
44ee:  0d4b           mov	r11, r13
44f0:  3d50 7c24      add	#0x247c, r13
44f4:  694d           mov.b	@r13, r9
44f6:  cd48 0000      mov.b	r8, 0x0(r13)
44fa:  ca49 0000      mov.b	r9, 0x0(r10)
44fe:  695d           add.b	@r13, r9
4500:  4d49           mov.b	r9, r13
4502:  dfed 7c24 0000 xor.b	0x247c(r13), 0x0(r15)
4508:  1f53           inc	r15
450a:  3e53           add	#-0x1, r14
450c:  0e93           tst	r14
450e:  e623           jnz	#0x44dc <enc+0x56>
4510:  3841           pop	r8
4512:  3941           pop	r9
4514:  3a41           pop	r10
4516:  3b41           pop	r11
4518:  3041           ret

Phew, that’s a lot of assembly.

Let’s just ignore that for a minute.

Back to main, this was confusing to me the first time I did it, and I’m still a little confused:

4438 <main>
4438:  3e40 2045      mov	#0x4520, r14
443c:  0f4e           mov	r14, r15
443e:  3e40 f800      mov	#0xf8, r14
4442:  3f40 0024      mov	#0x2400, r15
4446:  b012 8644      call	#0x4486 <enc>
444a:  b012 0024      call	#0x2400
444e:  0f43           clr	r15

We’re moving 0x4520 into r14. Then, we’re moving that into r15 (so now r15 contains “4520”). After that, we move 0xf8 into r14, overwriting the value of 4520. Okay.

But then we move 0x2400 into r15, overwriting 0x4520. What was the point of that earlier instruction then? 🤷‍♀️

Disassembly

Anyway… we’re gonna call enc, then we’ll call whatever is going on at 0x2400. What is going on at 0x2400?

None of that is human readable, unfortunately. However, if we can call it, is it just assembly instructions stored in a different place?

All of… that, but with the extra text stripped out, is:

4c85 1bc5 80df e9bf 3864 2bc6 4277 62b8
c3ca d965 a40a c1a3 bbd1 a6ea b3eb 180f
78af ea7e 5c8e c695 cb6f b8e9 333c 5aa1
5cee 906b d1aa a1c3 a986 8d14 08a5 a22c
baa5 1957 192d abe1 66b9 70ff 4a08 e95c
d919 8069 07a5 ef01 caa2 a30d f344 815e
3e10 e765 2bc8 2837 abad ab3f 8cfa 754d
8ff0 b083 6b3e b3c7 aefe b409

If you head over to the Assembler page on Microcorruption, you can copy/paste that and hit “Disassemble”.

Here’s what we get:

(cut for length)

Oooooh boy. My plan to avoid lengthy assembly blobs didn’t really pan out.

Empirical Learning

Let’s avoid the assembly for just a minute and step through the code.

I set a breakpoint before enc. Here’s what the memory block starting at 0x2400 looked like at that point:

After enc (using another breakpoint), that same memory block looks like this:

So clearly, something happened here, and maybe the assembly I showed earlier is no longer relevant.

Right now, we’re stopped at this breakpoint.

From here, type s to start stepping through the assembly instructions stored at 0x2400. If you watch the “Current Instruction” window, you can get a general sense of what’s going on, one instruction at a time.

For example, at this point, we’re getting ready to prompt the user for a password:

How do I know? Let’s look at 0x4520.

However, as you’re stepping through this, you’re probably noticing that it’s taking an ETERNITY and ain’t nobody got time for that.

Let’s try to get a bigger-picture view of the situation.

Back to the Assembler, Batman

If you go back to this page, you can assemble (or disassemble) instructions.

This time, let’s take the post-enc values from 0x2400.

0b12 0412 0441 2452 3150 e0ff 3b40 2045
073c 1b53 8f11 0f12 0312 b012 6424 2152
6f4b 4f93 f623 3012 0a00 0312 b012 6424  
2152 3012 1f00 3f40 dcff 0f54 0f12 2312
b012 6424 3150 0600 b490 1711 dcff 0520
3012 7f00 b012 6424 2153 3150 2000 3441
3b41 3041 1e41 0200 0212 0f4e 8f10 024f
32d0 0080 b012 1000 3241 3041 d21a 189a
22dc 45b9 4279 2d55 858e a4a2 67d7 14ae
a119 76f6 42cb 1c04 0efa a61b 74a7 416b
d237 a253 22e4 66af c1a5 938b 8971 9b88
fa9b 6674 4e21 2a6b b143 9151 3dcc a6f5
daa7 db3f 8d3c 4d18 4736 dfa6 459a 2461
921d 3291 14e6 8157 b0fe 2ddd 400b 8688
6310 3ab3 612b 0bd9 483f 4e04 5870 4c38
c93c ff36 0e01 7f3e fa55 aeef 051c 242c
3c56 13af e57b 8abf 3040 c537 656e 8278
9af9 9d02 be83 b38c e181 3ad8 395a fce3
4f03 8ec9 9395 4a15 ce3b fd1e 7779 c9c3
5ff2 3dc7 5953 8826 d0b5 d9f8 639e e970
01cd 2119 ca6a d12c 97e2 7538 96c5 8f28
d682 1be5 ab20 7389 48aa 1fa3 472f a564
de2d b710 9081 5205 8d44 cff4 bc2e 577a
d5f4 a851 c243 277d a4ca 1e6b 0000 0000

Hit “Disassemble” and see the results:

Again, this is cut for length. I grabbed the assembly up to the second “ret”:

0b12           push    r11
0412           push    r4
0441           mov    sp, r4
2452           add    #0x4, r4
3150 e0ff      add    #0xffe0, sp
3b40 2045      mov    #0x4520, r11
073c           jmp    $+0x10
1b53           inc    r11
8f11           sxt    r15
0f12           push    r15
0312           push    #0x0
b012 6424      call    #0x2464
2152           add    #0x4, sp
6f4b           mov.b    @r11, r15
4f93           tst.b    r15
f623           jnz    $-0x12
3012 0a00      push    #0xa
0312           push    #0x0
b012 6424      call    #0x2464
2152           add    #0x4, sp
3012 1f00      push    #0x1f
3f40 dcff      mov    #0xffdc, r15
0f54           add    r4, r15
0f12           push    r15
2312           push    #0x2
b012 6424      call    #0x2464
3150 0600      add    #0x6, sp
b490 1711 dcff cmp    #0x1117, -0x24(r4)
0520           jnz    $+0xc
3012 7f00      push    #0x7f
b012 6424      call    #0x2464
2153           incd    sp
3150 2000      add    #0x20, sp
3441           pop    r4
3b41           pop    r11
3041           ret
1e41 0200      mov    0x2(sp), r14
0212           push    sr
0f4e           mov    r14, r15
8f10           swpb    r15
024f           mov    r15, sr
32d0 0080      bis    #0x8000, sr
b012 1000      call    #0x10
3241           pop    sr
3041           ret

Why did I stop there? Because the instructions after that looked weird and were making calls to places in memory like:

d21a 189a      call	&0x9a18

Is 0x9a18 even a place in memory in this example? Further down, we see addresses that end in an odd digit. Wat.

Anyway… that second block (after the first ret) reminds me of the interrupt that we’ve seen in previous examples:

1e41 0200      mov    0x2(sp), r14
0212           push    sr
0f4e           mov    r14, r15
8f10           swpb    r15
024f           mov    r15, sr
32d0 0080      bis    #0x8000, sr
b012 1000      call    #0x10
3241           pop    sr
3041           ret

In the first block, there are multiple lines that call 0x2464.

b012 6424      call    #0x2464

Let’s figure out what that’s about. Set a breakpoint by typing break 2464.

That wasn’t very exciting. If we keep stepping (or continuing) to our breakpoint, we see that we’re slowly typing out “what’s the password?". We also see that we’re going through the interrupt-looking code to print each char.

Let’s assume that we can ignore the interrupt-ish code, and also ignore the code printing up “what’s the password” (or rather, I removed all the code up to the last “call 0x2464” line). That leaves us with:

3150 0600      add    #0x6, sp
b490 1711 dcff cmp    #0x1117, -0x24(r4)
0520           jnz    $+0xc
3012 7f00      push    #0x7f
b012 6424      call    #0x2464
2153           incd    sp
3150 2000      add    #0x20, sp
3441           pop    r4
3b41           pop    r11
3041           ret

Which is much more manageable.

Reykjavik Solution

So, if my assumptions hold up, the answer is somewhere in those 9 lines of assembly. The compare (cmp) looks interesting… presumably if we gave a password, it needs to compare it against a known good value somewhere.

It’s comparing some value to “0x1117”. That doesn’t look like it’s in the ASCII readable range, but let’s give it a shot. If we type “AAAA” as our password, we see the sr change on the next step, then program execution ends shortly after.

If we reset, and enter in “1711” as our password (hex-encoded), let’s see what happens. “1117” becomes “1711” because of endianness.

And it worked!

To solve this level, type “solve” and then enter the password (hex-encoded) of “1711”.