题外话
这道题emmm怎么说呢我感觉这难度标签不至于1星吧,这等下萌新看了看这不直接劝退?不过话说回来,这道题没绕什么弯子思路比较直,绕waf的手段最好记下来备着以后刷题用。
一、审题
这是来自江苏工匠杯的一道题,我们打开题目康康:
又是直接扔给我们一段代码,那就开始快乐的(bushi)代码审计吧,代码如下:
<?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;
}
二、代码审计
首先老样子找到我们的关键词flag,这里是一段if语句,如果$key1和$key2都为1时即可输出我们的flag,再跟进$key1和$key2这两个变量,发现分别在两个if语句里面,所以我们只要满足这两个大条件和内置的相关的小条件即可获得flag
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
首先我们先来看第一个if
$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...");
}
首先传入a,b两个参数,第一个if语句的含义即为将a转化为整型大于6000000,并且a的长度要小于3,这里直接用我们的科学计数法就可直接解决,a=9e9 (9e9=9*10^9),第二句if的含义为将b进行MD5加密并截取加密后字符串的后六位等于"8b184b",这里就是简单的一个哈希碰撞,这里我写了一个懒人简易版的jio本跑就完事了(碰到其他题的哈希碰撞时只需要修改res和flag的截取位置即可)。
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("碰撞中.....")
碰撞完后得到明文:261815215889
此时$key1已经等于1了,接下来看到第二个大if语句
$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参数,注意此时的c要是json格式的字符串,其中key为m的value不能为数字且要大于数字2022,而当字符串与数字相比时会把字符串强制转化为整型即"123abc"会转成123,"abc"会转成0,这里和弱相等绕过时的一样,所以这里的m应该等于"9999abc",继续往下跟进,key为n的value必须为一个数组且value数量必须为2,且第一个value必须是一个数组,那么c的雏形就是: {"m":"9999abc","n":[["xxx"],xxx]},继续往下看,第一次使用array_search搜索n中是否有"DGGJ",有的话才能进行下一步,第二次使用foreach循环搜索n中的值是否含有"DGGJ",没有的话才能使$key2=1,而这里看上去是矛盾的,其实就是矛盾的哈哈哈,但是这是ctf题所以这俩函数必绕一个,刚好array_search可以绕过,建议题后去看看这个函数,这里直接说结论,也是一样的与弱相等类似即该函数进行匹配时如果是字符串与数字,先会把字符串转化为整型再加以比较,而"DGGJ"转化为整型即为0,所以c传的参数为:{"m":"9999abc","n":[["666"],0]} ,完整的payload为:?a=9e9&b=261815215889&c={"m":"9999abc","n":[["666"],0]}
最终得到了我们的flag。
总结
这里绕来绕去大部分还是弱相等问题,出现的知识点的要求就是自己编写哈希碰撞脚本的能力,intval的绕过,array_search的绕过还是值得记下来的。