本题的知识点相对来说比较简单
总结来说题目结构分为两部分
对php功底比较深的大神来说,本题就很简单了
读题,打开网页后发现为比较简单的php代码审计问题,简单分析下代码如下
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
//获取get型变量、ab
$a = $_GET['a'];
$b = $_GET['b'];
//对a、b的数值做了限制a>600万并且a的长度小于等于3
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
//b的md5切片值为8b184b
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
//json_decode是php5.2.0之后新增的一个PHP内置函数,其作用是对JSON格式的字符串进行编码,
//json_decode接受一个JSON格式的字符串并且把它转换为PHP变量 ,当该参数$assoc为TRUE时,将返回array,否则返回object。
$c=(array)json_decode(@$_GET['c']);
//is_numeric — 检测变量是否为数字或数字字符串
//{'m':xx,'n':{[],xx}}
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])){
//DGGJ是否存在于$c["n"]中
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
遍历数组,如果存在字符串DGGJ则出现报错
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
//如果$key1 && $key2都成立,输出flag
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?> Emmm...
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...");
}
isset($a) && intval($a) > 6000000 && strlen($a) <= 3
变量a存在并且满足下面的条件,长度小,内容大,很容易想到科学计数法,解析如下
isset($b) && '8b184b' === substr(md5($b),-6,6)
分析可得b变量的md5值后6位是8b184b
,没什么巧,直接上脚本爆破
import hashlib
def get_md5(password):
#1- 实例化加密对象
md5 = hashlib.md5()
#2- 进行加密操作
md5.update(password.encode('utf-8'))
#3- 返回加密后的结果
return md5.hexdigest()
for i in range(1,100000):
md51=get_md5(str(i))
if md51[-6:]=='8b184b':
print(i)
爆破得b为53724
c变量的获取相对比较麻烦,依次分析
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");
}
(array)json_decode(@$_GET['c'])
:变量c为josn格式的字符串
is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022
:变量c为数组并且c中的m值不是数字或者字符串,并且值大于2022
is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])
:变量c的n值为数组,并且n数组有两个值,并且c变量n值中的第一个值为数组,此时我们可以获取c变量的格式为{‘m’:xx,‘n’:[[xx,xx…],xx]}
$d = array_search("DGGJ", $c["n"]);
:变量c的n值中查找是否有DGGJ
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
分析到这里,题目的两个难点出来了
关于m的取值,可以先看一个知识点。
PHP是一门弱类型语言,当涉及“”判断时,它的表现是有一些奇特的。
例如,“a0”将会得到true,“a==1”将会得到false。
(int)"1234abc"=1234
(int)"abcd"=0
(int)"1a2b3c"=1
(int)"a12b"=0
(int)"000123abc"=123
原理:PHP语言中在涉及“==”判断时,如果运算符两边分别为字符串(不以数字开头的字符串),会将字符串转化为数字0,再进行比较。如果是以数字开头的字符串,那么它将会转化成开头的数字(即第一个字母前的所有数字)。
唯一的例外就是上面我们提到的科学计数法9e9
这种会直接被认定为科学计数法
由此,我们可以得出m的值可以为2023a
刚开始,通过分析,我以为只需要再n中有DGGJ字符即可,但是后面遍历数组后却又反回了no…,这里我们需要学习另外一个知识点,array_search 的绕过,相当于弱比较,我们直接赋值为 0,即可绕过。
由此可以对n进行赋值,“n”:[[0,2],0]
综上分析,获取payload
?a=9e9&b=53724&c={“m”:“2023a”,“n”:[[0,2],0]}
http://xxx:xx/?a=9e9&b=53724&c={“m”:“2023a”,“n”:[[0,2],0]} ,获得flag