【攻防世界】easyphp(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;
}

?>

进行代码审计,将整体代码分开分别分析。

使用 $_GET 方法上传参数 $a 与 $b、$c,并进行判断来进行回显:

找到可以获取 flag 的关键代码进行审计:

if($key1 && $key2){
    include "Hgfks.php";
    echo "You're right"."\n";
    echo $flag;
}

审计得知,若要输出 $flag 的值,则需要 $key1 和 $ket2 均为True,即均大于零。

首先查看 $key1 大于零的条件:

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...");
}

代码首先对参数 a 进行判断,要求参数a的值大于 6000000 且 a的位数不超过3位,故可以使用科学计数法来给 参数a 赋值。 ?a= 1e9

接着对参数 b 进行判断,若b存在 且 参数b的后六位 等于 字符串'8b184b' 的话,即赋值 $key1=1。

故编写 MD5破解脚本:

import hashlib
for i in range(100000):
    x = str(i).encode()  #将字符串转换为字节串,因为hashlib.md5()函数的参数必须为字节串
    y = hashlib.md5(x)  #进行md5加密编码
    m = y.hexdigest()  #以十六进制的格式返回编码数据
    if(m[-6:] == "8b184b"):  #若编码后六位等于字符串"8b184b",则代表爆破成功
        print("爆破结果为:" + m)
        break
    print(i)

爆破结果如下:

成功获得 符合条件的b的值---53724。

查看 $key2 大于零的条件:

$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:

第一个 if 判断语句:要求 $c 是一个数组,且 $c['m'] 的值不为数字 (is_numeric()函数判断),在 $c['m'] 的数据类型不为数字的同时还要求 $c['m'] 的键值大于 2022,需要用到 PHP 的弱语言特性。

PHP是一类典型的弱语言,在PHP代码中:

2023abc 与 2022 作比较时,会自动将 字符串类型的 '2022abc' 转换为 数字类型2023 来和 2022 进行比较。

故可以将 $c['m'] 赋值为 字符串 '2023abc'。

被嵌套的第二个 if 语句:$c['n'] 也为一个数组,所以 $c 是典型的 数组嵌套数组,在要求 $c['n'] 为一个数组的同时要求 $c['n'][0] 也为一个数组,所以 $c['n'] 中也嵌套一个数组。

最后利用 array_search()函数要求 $c['n'] 中可以匹配 字符串 'DGGJ',同时利用 foreach 要求 c['n'] 不存在字符串 'DGGJ',看似要求矛盾,实则依然利用PHP的弱语言特性。

若 $c['n'] 的键值包含数字0,当 数字0 和字符串'DGGJ'进行比较时,字符串'DGGJ'会自动被转换为数字零,以此可以达到目的。

所以 payload: c={"m":"2023abc","n":[[0,1],0]}。

所以最终的payload:http://61.147.171.105:53569/?a=1e9&b=53724&c={"m":"2023abc","n":[[0,1],0]}

成功获得 flag。 

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值