Microcorruption CTF Addis Ababa Write-up

Microcorruption CTF Addis Ababa Write-up

- 10 mins


This is a write-up of my solution to the Microcorruption CTF challenge “Addis Ababa” (LOCKIT PRO r b.03).

Right off the bat, in the description of the challenge, we get a hint dropped:

    - We have verified passwords can not be too long.
    - Usernames are printed back to the user for verification.
    - This lock is attached the the LockIT Pro HSM-1.

Let’s see exactly how the program is going to print back the usernames to the user…

This code is significantly larger than the last one. However, it’s very clear that they changed one major thing: the use of printf() instead of puts().

Let’s just play around with the inputs and see what the program spits back out…

Addis Ababa Test Input 1

It looks like whatever input string we give it (e.g. AAA:BBB), the program will print back. All signs are pointing to a format string vulnerability, but let’s verify that’s actually the case by throwing a few %x's at it and seeing if we get anything interesting back…

Addis Ababa Test Input 2

Sweet! Upon inputting AA%x%x, we get the output AA4141, which means that we are reading two 1-byte hex-encoded parameters from the stack (i.e. the A's at the beginning of our input). I won’t spend time explaining how format string vulnerabilities work in this writeup, but you can check out scut’s whitepaper “Exploiting Format String Vulnerabilities” for a useful guide.

Now let’s try and see if we can use %n to write to a location in memory. I’ll use the input value BA%x%n to start with. Also, I’ll set a breakpoint at memory location 0x46b2 inside of login(), which is the cmp.b #0x6e, r14 instruction. This should allow us to break on the handling of the %n (n == 0x6e) format string parameter and lets us examine the registers and memory during execution.

As you can see, by using the %n parameter, we were able to overwrite the memory address 0x4142 (e.g. BA, the first two bytes of our input) with the value that was in register r10 (i.e. 0x2, the number of bytes written out by printf()). Great!

Now, let’s take a step back and see exactly what we need to do to unlock the door…

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>

Inside of main(), we can see that after the call to printf() returns, the instruction tst 0x0(sp) will execute. This instruction will test the sp register against 0x0, and will jump over the unlock_door() call if it’s zero. Let’s set a breakpoint this on instruction and input AAAA and see what the registers look like…

Addis Ababa Test Input 3

Looks like 0x40c4 has a 0x0 value and indeed prevents the call to unlock_door() from executing. I wonder what will happen if we use our newly weaponized format string vulnerability and write a non-zero value to the sp register (location 0x40c4)…

Addis Ababa Solve

:+1: :beers:

Flag (mouse over to reveal)




Security guy, busticati, professional button-pusher

comments powered by Disqus
rss keybase facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora