拿到题目后首先大致分析一下,找flag,在最后发现一个if语句当同时满足key1=1和key2=1时才给flag,看起来比较复杂,先进行代码审计吧
<?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;
}
代码审计
1.首先来看第一个if语句
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...");
}
get传入a,b两个参数,第一行if先判断a是否大于6000000并且长度不能大于3,所以这里我们可以用科学计数法来表示a,即a=9e9。第二行if进行了一个哈希碰撞,截取md5加密后的b的后六位和8b184b比较,如果相等则满足条件,这里引用了一个脚本
import random
import hashlib
res = "8b184b"
while 1:
temp = random.randint(10**11, 10**12 - 1)
temp = str(temp)
MD5 = hashlib.md5()
MD5.update(temp.encode(encoding='utf-8'))
flag = MD5.hexdigest()
if flag[-6:]==res :
print("碰撞成功:"+flag)
print("明文为:"+temp)
break
else:
print("碰撞中.....")
2.来看第二部分代码
$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");
}
传入一个json格式的数组参数c,并且数组中m的值要大于2022(is_numeric() 函数用于检测变量是否为数字或数字字符串。是数字返回true,否则返回false),当字符串与数字进行比较时会把字符串强制转化为整型即"123abc"会转成123,"abc"会转成0,类似于弱比较所以这里的m等于2023abc即可。接着第三行key为n的value必须为一个数组并且要有两个,并且第一个要是数组,所以n大致为{"m":"9999abc","n":[["xxx"],xxx]}。接着执行array_search() 函数(array_search() 函数在数组中搜索某个键值,并返回对应的键名)搜索n中是否有DGGJ,有的话才能使$key2=1,第二次使用foreach循环搜索n中的值是否含有"DGGJ",没有的话才能使$key2=1,我们传入一个数字时,字符串会强制转换成数字,DGGJ会转换成0,所以我们只要传入0就行了,所以c为{"m":"9999abc","n":[["666"],0]},完整的payload为:?a=9e9&b=261815215889&c={"m":"9999abc","n":[["666"],0]}
总结
这道题主要就是弱类型绕过,即字符串在和数字比较时会强制转换成数字,利用这一点可以进行绕过。另外一个就是哈希碰撞的脚本,在平时做题的时候可以多多积累这些脚本,扩大自己的知识库,如果有时间的话应该了解脚本代码是如何实现的。