all_exp.py
13.8 KiB1970-01-01 07:59
README.mdx
7.23 KiB2025-11-27 18:09

#2025强网杯决赛 Writeup

#day0

周五下午坐飞机到郑州,然后转出租车。落地都下午 6 点了。然后去酒店。

不得不说这个酒店真的太偏了,每次来都得晕很久的车,等到场馆已经 8 点了。

按理说这个点应该去测一下网络啥的,但是我们上去的时候工作人员说测试已经结束了,,em但是听说 redbud 和天枢的师傅还在里面,btw 我们进不去。

太变态了,今年组委会没有提供 day0 的餐卷,和 aura 点了外卖吃,边吃边和 lotus 聊天,聊了一下个人发展,感觉很焦虑.jpg

lotus 说他就是来贩卖焦虑的

在一楼见到了 tplus,不赖。

在一楼见到了 misaka,不赖。

晚上见了肥猪,他请我去吃烧烤,好吃喵~

#day1

今年的赛制是 awdu + rw,一天半时间,两个赛制同时开,不过大家肯定先做 awdu。

第一天刚上来就是三道 pwn,有一个很 ez 的签到题,输入 . sh 就能拿到 shell,丢给 aura 写了个打全场的脚本,就开始吃分了。

问题是这个题的修复 check 似乎很抽象,一直到比赛结束这道题都没有一个队伍能成功 patch,GG

然后我开始看 somebox 这个题,出于对 xctf final 那两个 somexxx 题目的认可,我感觉这个题估计也不会很难打,然后我发现我错了。

出题人用魔改的四路 LCG 算法加密了一个菜单题的所有输入输出,功能 2 的 login 给了一个 5ms 的延时,还需要侧信道爆破,功能 3 给了一个 shellcode 的后门。

然后我简单看了一下 shellcode 部分,发现和 24 年网鼎杯的题目很相似,翻到了之前写的 exp,我以为我要一血了。。

但是我们队没有密码手和逆向手,前面这个巨抽象的算法我只能靠 GPT5.1 和 gemini3 逆向,,,一开始用 GPT5.1 Agent 根本复现不出来这个 LCG,一直和 AI 对线到 12 点还是没有结果。

队友在看 csheap 那个题,他写了一个很变态的泄漏 pie 出来,概率很低,,我没绷住,我想了一会写了一个能稳定泄漏 pie 的 poc,但是这个题的堆分配是用的出题人自己写的随机分配算法,导致很难堆风水。。在这个地方卡了很久。

后面发现,当程序用的内存快分配完时,随机分配的堆块有很大概率能分配在一起,这样就可以利用这个特性来实现堆风水。

这个题目 tplus 在上午 11 点就拿到一血了,到比赛结束他靠这道题目吃了 6w 分,,而我下午三点多才写出来打全场的脚本,还因为风水的不好导致事实上每一轮只能打一两个靶机,,,然后回去继续看 ai 的逆向,gpt5.1 还是没结果。

然后我换了 gemini3,哦耶出了。但是后续因为对话量过大导致会话炸了,找不到记录了

然后我想着继续用 gemini3 搓后面的侧信道,如图:

btw 我又和 gemini3 对线了1个小时,写出来全是错的。。我彻底没招了,又扔给 gpt5.1 agent,结果它写了两遍写对了。。。

这个时候距离比赛结束还有 40 分钟,然后 0x300R 就拿到了这个题的一血。。。

彻底失败的 awdu day1。。。

没招了,晚上回去看 rw。

先和 aura 看了一眼 trustsql 那个题,结果 aura 看着看着说有源码,然后对着源码很快审到了🕳,秒了直接,,给我看傻了。

然后继续看 somebox,用 AI 很快找到了一个模 5 等于 0 且不影响 shellcode 的指令 cmc,这个题目的攻防逻辑是,防守方可以每次修改配置文件 config.enc,程序会动态 load config.enc 里的沙箱配置,也就是允许防守方禁用除了 openat 外十种系统调用。如果全拿来 ban read 的话是不够的,因为 read 其实有 11 种实现:

read
pread64
readv
preadv
preadv2
io_setup
mmap
io_uring_setup
sendfile
copy_file_range
splice + pipe
read
pread64
readv
preadv
preadv2
io_setup
mmap
io_uring_setup
sendfile
copy_file_range
splice + pipe

扔给队友写打全场的脚本了。

我滚回去看浏览器 pwn,我很快发现这个 patch 和 CVE-2024-12695 很相似,甚至那个 issue 里的 poc 我拿来都可以继续用。

这篇博客写的很清楚。

然后我照着博客很快拿到了 d8 内的任意地址读写,但是打浏览器的时候,我之前从来没打过 chromium,完全不知道为什么我的 exp.js 加载时会卡死。。。然后 gdb 也 attach 不上 render,完全不知道该怎么调试。

一直调试到早上 5 点,还是没有什么好办法。事已至此,先睡会吧。

#day2

噩耗,队友没写出来打全场的脚本,我只好开赛后自己写。

大概花了两轮时间,写了一个多线程打全场的脚本,写了一个解密防守方沙箱配置的脚本(因为题目功能 4 可以读取沙箱配置),根据防守方的沙箱配置,利用没 ban 的那个 read 实现 orw。

然后就很爽了,从第二轮开始打全场,从第 19 名干到第 4 名了(最高)。

似乎还上了一道新 pwn,zig pwn,扔给队友了,我要疯狂 ad 了。

由于看不到攻击流量,所以我只能根据每轮被打的情况猜测着 patch 一些 syscall,我写了一个根据给定的 sysfile 文件生成 config.enc 的脚本,然后上传到靶机里。

这样折腾了四五轮之后,基本上我可以打全场 30 个队伍(除了某个神秘队伍只留了 aio,但是我刚好没时间写 aio 的 shellcode),然后也可以做到保证只被最多一个队伍打(很神秘,完全防不住,不知道他们怎么打的)exp

最后靠着这道 somebox 在 awdu 上吃了不少分,虽然队友那个 zig pwn 卡在任意写之后利用了,但也问题不大,成功压线拿到了二等奖。

第一天一直在牢这个题的逆向部分,总算是时间没白费。

嘻嘻赢了 redbud

#后续

赛后和其他师傅聊天的时候,才知道原来打我的那个队伍只需要 openat 就能实现 orw,原因是每次 nc 防守方靶机,/tmp/boxconfig 是同一个文件,所以可以先 nc 上去用 openat 把沙箱配置文件卡住,这样再 nc 上去时程序无法 load 任何防守方配置的沙箱,然后随便 orw 就赢了。

比赛的时候头都快想破了都没想到这个。

另外吐槽一下春秋的平台,提交 flag 的地方没有给 api 接口,自己抓包抓出来的有点问题也交上去,导致每轮都得手动提交 flag。全场的靶机启动的也慢,15 分钟一轮,3 分钟花在了靶机启动上,还好 somebox 那个题我写了多线程,正常来讲打一次侧信道就要 1min,全场 31 个队伍,,多线程一起打 31 个队伍的话就很快了。。

最后贴一张我上去领奖的照片,第一次来 qwb 线下,开心喵 那道浏览器 rw 没有做出来太可惜了,赛后 tplus 师傅说他也卡在这个地方了

领奖

比赛结束后见了空白,还和肥猪去打了台球,开心~