Monday, February 23, 2015

Microcorruption IX - Santa Cruz

Once again, our developers have tried to properly check the length of username and passwords to prevent toying. Let's take a look at what we can find. As always, we'll start by trying to figure out how our stack looks.


To start out we push r11 and r4 onto the stack, grab 40 bytes of the stack for this frame, and set a couple of values (a 0 here, a 0x10 there, and a 0x8 somewhere else). The 0x10 and 0x8 are probably related to the length of the username and password (between 8 and 16 characters), but we'll figure that out as we go.We can also see that r4 has taken on the value of sp when we first enter the function, at the ra.  We use this as a jumping point for a lot of memory operations after this, so it's worth noting.

Next, we can see that after requesting the username it is strcpy()'d from 0x2400 into r4 - 42, so this tells us our username starts at *0x43a2. Again we see a strcpy() for the password going into r4 - 23, making our password start at *0x43b5. We can now map out a fair amount of our stack like below. We'll mark don't know's with X, differentiate between deliberately written 0 bytes and null terminators by using \0 in strings, and mark the username and password buffers with U and P, respectively. This diagram assumes both username and password are exactly 16 characters long, the \0's could technically move around

       +--------------+
      0x43cc | ra - 2 bytes | <- r4
       +--------------+
0x43ca |r11 - 2 bytes |
       +--------------+
0x43c8 |r04 - 2 bytes |
       +--------------+
0x43c6 |  0x0  |   X  |
       +--------------+
0x43c4 |   P   |  \0  |
       +--------------+
0x43c2 |   P   |   P  |
       +--------------+
0x43c0 |   P   |   P  |
       +--------------+
0x43be |   P   |   P  |
       +--------------+
0x43bc |   P   |   P  |
       +--------------+
0x43ba |   P   |   P  |
       +--------------+
0x43b8 |   P   |   P  |
       +--------------+
0x43b6 |   P   |   P  |
       +--------------+
0x43b4 |  0x10 |   P  |
       +--------------+
0x43b2 |  \0   | 0x8  |
       +--------------+
0x43b0 |   U   |   U  |
       +--------------+
0x43ae |   U   |   U  |
       +--------------+
0x43ac |   U   |   U  |
       +--------------+
0x43aa |   U   |   U  |
       +--------------+
0x43a8 |   U   |   U  |
       +--------------+
0x43a6 |   U   |   U  |
       +--------------+
0x43a4 |   U   |   U  |
       +--------------+
0x43a2 |   U   |   U  |
       +--------------+
      0x43a0 |   X   |   X  | <- sp
       +--------------+

Now that we know what the stack looks like and we know we're using strcpy(), we can try to overwrite the ra by just blasting a really long username or password into the stack, smashing everything along the way. Looking further into the code though, we'll notice that the length of the password is being verified by checking that it is greater than the value it 0x43b2 and less than or equal to the value at 0x43b4 - values which we can control when overwriting password.


Should you pass those checks, the password is sent to the external device and the program will unlock the door if it's the correct password.


In an attempt to prevent us from being able to utilize a buffer overflow where we smashed ra, it checks if the 0x0 byte we wrote earlier at 0x43c6 is still intact, and if it's not quits. If it is, however, we're in business. Since we're using strcpy() to copy our username from 0x2400 into the buffer, we can't replace that byte there. So what we'll do is smash everything in between with the username, overwrite the ra, and then have the password be 17 characters long so the null byte keeps that 0 intact. Great, however we won't properly pass the length check anymore. To remedy this, we can make sure that in our username buffer we make the values for the minimum and maximum length work for us - 0x8 and 0x12 should do it. We also see that the unlock_door() function is located a *0x44aa, so we can use that in our exploit. In the end, we'll write:

username:  'A'* 17 + \x80\x10 + 'A'*23 + \x44\x4a   4141414141414141414141414141414141081241414141414141414141414141414141414141414141414a44
password: 'A' * 17
4141414141414141414141414141414141

This will make all the checks pass, hit the ret instruction, and start executing at our smashed ra.

Thursday, February 19, 2015

Microcorruption VIII - Johannesburg

So now these guys have gotten wise - they're putting in extra measures to make absolutely sure we haven't written outside of the buffer they've given us - 16 characters or bust! Let's check out how everything is working though. Always break at the entry point and map out the stack.


Essentially the same, but you'll notice that our sp moved 18 bytes this time and sets the one closest to the ra to 0x72. So our stack ends up looking like this


       +--------------+ 0x4400
|      ra      |
|   (2 bytes)  |
       +--------------+ 0x43fe
|    0x0072    |
|   (2 bytes)  |
       +--------------+ 0x43fc
|      buf     |
|  (16 bytes)  |
|              |
|              |
|              |
|              |
            +--------------+ 0x43ec = sp

The stack

Strange that that extra int is there, but lets take a look why.


At *0x4578 we check the value of that byte - if it's still 0x72 then we just exit noting that the password was incorrect, if it isn't then we notify that the password is too long. This is a dumb version of a stack canary, so we'll just go ahead and make sure the 18th byte we enter is 0x72 and continue on our merry way. This compilation contains an unlock_door() function at *0x4644, so we don't even need shellcode (though we could totally reuse the code we wrote last time). Dump in 0x4141414141414141414141414141414141724644 and you win.

Microcorruption VII - Montevideo

Now that the developers of the lock have noticed that last buffer overflow, they decide that we can only copy strings into memory instead of arbitrary data by using strcpy(). For shame.



Once again, break at the entry point and note the address of ra: *0x43fe. Our buffer falls right after that, 16 bytes following the ra (right at the beginning of our stack frame), giving us the following basic stack diagram (same as always)

       +--------------+ 0x4400
|      ra      |
|   (2 bytes)  |
       +--------------+ 0x43fe
|              |
|      buf     |
|  (16 bytes)  |
|              |
|              |
|              |
|              |
            +--------------+ 0x43ee = sp

The stack

This time, however, the gets() call copies our data into *0x2400 then a call to strcpy() copies it into buf. This isn't that big a deal, except for how our original shellcode had a null byte, which strcpy() will interpret as the end of the string and stop copying.

30127f00b01232453041

This byte is in the push #0x7f instruction, as it gets extended out to 0x007f (and stored little endian as 0x7f00). There's lots of different ways of avoiding this, but we'll go with a simple one which is to subtract two numbers that have both the high and low byte populated such that their difference is 0x007f -  I'll choose 0x0180 and 0x0101. Now we'll extend the original shellcode out to (noting that &<INT> is *0x454c):


The new shellcode is a bit longer (14 bytes), but doesn't contain any nulls. So now we just pad with some garbage and overwrite ra with 0x43ee and we're winning.

34408001348001010412b0124c454141ee43

Microcorruption VI - Whitehorse

This is the first 50pt challenge in the series and it gets to be where things start pulling together all this stack smashing and controlling ra business.

Once again, main() calls login() so we'll break at the entry point of login() so we can map out the stack relative to the ra.


As you can see, ra is located at *0x4268  and sp will end up moving 16 bytes to hold our buffer (at *0x4258). Thus, if we write 18 bytes we completely control the ra. For example AAAAAAAAAAAAAAAABC will have the login() function attempt to return to *0x4342 which will segfault and bail.


       +--------------+ 0x426A
|      ra      |
|   (2 bytes)  |
       +--------------+ 0x4268
|              |
|      buf     |
|  (16 bytes)  |
|              |
|              |
|              |
|              |
            +--------------+ 0x4258 = sp

The stack


Instead, we'll put in some instructions to unlock the lock , even though unlock() doesn't exist here. Referencing the manual, pushing 0x7f onto the stack and then calling the <INT> function will unlock the door. Noting that <INT> is located at *0x4532 in this program, we'll assemble that like so:


This shellcode is only 10 bytes, so we can pad the next 6 with garbage and then overwrite the ra with the address of the buffer. Input 0x30127f00b012324530414242424242425842 and the login() function will return into our buffer and unlock the lock.

Microcorruption V - Reykjavik

This is one of the more interesting challenges. Starting out, the program itself doesn't seem to have a lot going on, just a main() that calls a function called enc() that does a bunch of memory operations starting at *0x2400. Right after, we jump into that memory space - what we're looking at is packed code being decrypted at runtime.


Since this code doesn't fall in the .text of our program, we're not going to see it in the main disassembly window - we'll need to extract the opcodes after they've been decrypted and run them through the separate disassembler that the website provides. Break at the call to *0x2400 and copy the memory from *0x2400 to *0x2564 (where we see the ret instruction) and dump that into the disassembler. This yields the following code:

  1. 0b12 push r11
  2. 0412 push r4
  3. 0441 mov sp, r4
  4. 2452 add #0x4, r4
  5. 3150 e0ff add #0xffe0, sp
  6. 3b40 2045 mov #0x4520, r11
  7. 073c jmp $+0x10
  8. 1b53 inc r11
  9. 8f11 sxt r15
  10. 0f12 push r15
  11. 0312 push #0x0
  12. b012 6424 call #0x2464
  13. 2152 add #0x4, sp
  14. 6f4b mov.b @r11, r15
  15. 4f93 tst.b r15
  16. f623 jnz $-0x12
  17. 3012 0a00 push #0xa
  18. 0312 push #0x0
  19. b012 6424 call #0x2464
  20. 2152 add #0x4, sp
  21. 3012 1f00 push #0x1f
  22. 3f40 dcff mov #0xffdc, r15
  23. 0f54 add r4, r15
  24. 0f12 push r15
  25. 2312 push #0x2
  26. b012 6424 call #0x2464
  27. 3150 0600 add #0x6, sp
  28. b490 7412 dcff cmp #0x1274, -0x24(r4)
  29. 0520 jnz $+0xc
  30. 3012 7f00 push #0x7f
  31. b012 6424 call #0x2464
  32. 2153 incd sp
  33. 3150 2000 add #0x20, sp
  34. 3441 pop r4
  35. 3b41 pop r11
  36. 3041 ret
For a general idea of what's going on, we reference the code we had before and the lock's manual: *0x2464 is the <INT> call, which tosses out the interrupt of our choice and 0x02 is the gets() interrupt. So this means at line 26, we are calling gets() on r4 + 0xffdc (which is r4 - 36 bytes). Looking at line 28 and below, we see that the main check for whether or not we unlock the lock (push 0x7f and call 0x2464 <INT>) is whether or not -0x24(r4) (that is 36 bytes below r4) is 0x1274; this is the address of our password. Remembering that the architecture is little endian, input 0x7412 as the password and the door unlocks.

Microcorruption IV - Cusco

Moving on with the challenges, we start to get to some more of the stuff that requires some actual knowledge of the stack.

The basic structure of this version is that main() immediately calls login() which getsn's the password (once again it takes far too many characters - 48 instead of 16) while allowing a buffer overflow.

       +--------------+ 0x4400
|      ra      |
|   (2 bytes)  |
       +--------------+ 0x43fe
|              |
|      buf     |
|  (16 bytes)  |
|              |
|              |
|              |
|              |
            +--------------+ 0x43ee = sp

The stack

If we break when main() first calls login() we can see ra stored at *0x43fe. sp is then decremented by 16 bytes, which will form our buffer.



If we provide 16 characters to the getsn() call, we then fill the buffer and the next two characters will overwrite the high byte of ra and then the low byte of ra. In short, AAAAAAAAAAAAAAAAXXYY will cause login() to return to *0xYYXX (again, this architecture is little endian). If we look around, we see the address of unlock() is 0x4446 so if we write 16 characters of garbage followed by 0x4644, login() will return into the unlock() function an open the lock. A simple way to do this is to input 0x414141414141414141414141414141414644 (AAAAAAAAAAAAAAAAFD in ascii) and we'll solve the whole thing.

There are a number of other ways to solve this, but in the interest of progressing in the manner that Matasano seemed to want, we'll just go this route.

Wednesday, February 18, 2015

Microcorruption III - Hanoi

Once again, another fairly simple challenge, but this one gets a bit more interesting. We can't see exactly what the password checking is looking for anymore, since it is now being outsourced to another device which uses DMA to set a specific memory address to flag that the password was correct. We can see in login() that after the password is passed to the device, it checks if *0x2410 == 0xb4 and, if so, the password is considered valid.


We can see that our input is being stored at *0x2400 and being copied in with getsn(), which would be fantastic if they didn't let us copy 0x1c (28) characters in.


+----------------------------------+
|            buf          | flag   |
|         (16 bytes)      |(1 byte)|
+----------------------------------+
0x2400                   0x2410      

The stack looks kinda like this


Since the flag at 0x2410 is only 0x10 (16) characters away from the flag byte, we can overflow the buffer with any 16 characters and the 17th we write will set the value of flag, which we can use to signal that the password is correct. Input 0xb4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4 == ´´´´´´´´´´´´´´´´´ and the lock opens.