ByteCTF2021 chatroom writeup
前言
在今年的ByteCTF中,我出了一道pwn题目,距离上一次打比赛/出题已经过去很久了,所以传统的 heap trick
就没有考虑,而是从我日常工作中挖掘的安全风险入手,简化场景,出了一道 chatroom
,看起来像一个web的奇怪题目。
这个题目其实背后是 Headless Chrome
相关的pwn,我早期的一篇博客其实已经阐述过相关风险,可以参考 Exploit Headless Chrome。 其实这个风险暴露出来的不仅仅是:低版本、误用参数 这两个显而易见的问题,其背后的原因是一些不好的编程习惯被错误地传播:大家都在用 --no-sandbox
参数,好像 it works
就够了,但是在实际场景中,这是很危险的。
题目设计思路
我的本意是设计一个类似聊天室的场景,用户可以在聊天室内发送消息、多媒体文件、链接等,尽可能模拟一个真实场景。 处于风控考虑,对于非白名单的链接,需要进行检查(是否恶意,色流等)。对于URL 检查的逻辑,最好是服务端接收到内容之后,判断是否是URL,随后通过RPC调用走到URL检查的服务去。但是考虑到实际题目,我大大简化了这个场景,直接把检查放在前端了,而且我没有混淆JS,所以可以很直接看到一个HTTP请求。
解决了场景问题,聊天室部分直接用了github的开源项目 node-websocket-Chatroom,后端使用 puppeteer来抓取用户的URL。
为了提升一些难度,同时这也是我曾经遇到过的问题:UA不可靠的情况下怎么判断Chrome版本?
所以我直接在启动参数里把UA给改了:
1 |
|
最终题目成型:
- 非最新版本puppeteer
- –no-sandbox
- UA不准确
Writeup & 非预期
非预期
主要是 zh1x1an2 同学的做法,Exploit狂轰滥炸术,挨个挨个来,最终拿到flag。
预期
UA不可信,但是V8 和 Blink是可信的,不同Chrome版本会有不同的features,所以可以借助这个点,判断一个大版本,便于后续做利用。
参考 : https://chromestatus.com/features
不过这个需要一些积累 && 测试 :)
随后判断出来版本是 M88 之后,找个合适的nday就可以打了 : )
题目环境
1 | docker pull muhe/ctf_chal_chatroom:v7 |
访问 http://localhost:3000
就可以本地测试题目了 :)
不足之处
- 无法防止爆破这种非预期解题方式。
- 使用nday似乎很无趣,但是塞进去一个洞,给一个
patch.diff
似乎又有点奇怪,偏离题目原本的出发点。