文章目录
PHP弱类型比较漏洞
一、弱类型比较
<?php
var_dump("admin" == 0); //true
var_dump("1admin" == 1); //true
var_dump("admin1" == 1); //false
var_dump("admin1" == 0); //true
var_dump("0e123456" == "0e445567"); //true
?>
二、Hash比较
<?php
//var_dump("0e123456789012345678901234567890" === "0"); //false
//var_dump("0e123456789012345678901234567890" == "0"); //true
$pass = $_GET['password'];
$password = "0e342768416822451524974117254469";
if (md5($pass) == $password) {
echo "flag{xxx--xxx--xxx}";
}
else{
echo "error";
}
?>
这种情况,基于上面的 0e 开头的弱类型hash值比较,那么此时我们只需要传入一个值使得md5处理之后的值可以以 0e 开头即可
MD5加密后为 0e 开头的字符串
三、布尔比较
<?php
$str = '{"user":true,"pass":true}';
$data = json_decode($str,true);
if($data["user"] == 'root' && $data['pass'] == 'myypass') {
echo "登录成功 获得flag{xxx--xxx--xxx}";
}
else{
echo "登录失败";
}
?>
这里说明,true == "root" && true == "mypass"
是成立的
四、反序列化比较
$str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';
$data = unserialize($str);
if($data["user"] == 'root' && $data['pass'] == 'myypass') {
echo "登录成功 获得flag{xxx--xxx--xxx}";
}
else{
echo "登录失败";
}
说明完全可以通过反序列化构造布尔值来绕过字符串的判断
五、极值比较
<?php
$a = 987654321987654321123456789;
$b = 987654321987654321000000000;
var_dump($a == $b);
?>
说明对于极值比较来说,PHP的比较是由范围限制的,超过一定范围后面的数字就无法比较了
六、switch比较
<?php
$num = '2woniu';
switch($num) {
case 0:
echo "0000";
break;
case 1:
echo "1111";
break;
case 2:
echo "2222";
break;
case 3:
echo "3333";
break;
case 4:
echo "4444";
break;
case 5:
echo "5555";
break;
default:
echo "error";
}
//数字型防SQL注入的简单方法
$source = '123456 and 1=1';
var_dump((int)$source);
?>
说明 "2woniu" == 2
被判定成功了,而且SQL语法中对于字符串转整数
七、数组比较
当使用 in_array() 或 array_search() 函数时,如果 strict 参数没有设置为 true,则 in_array() 或 array_search() 将使用宽松比较
<?php
//数组比较
$array = ['a',0,1,2,'3'];
var_dump(in_array('abc',$array));
var_dump(array_search('abc',$array));
?>
根据此结果可以判定 'abc'
存在于 $array
中,而且下标还是 1 ,这是为什么
原因分析
PHP 的
in_array
函数有一个可选的第三个参数strict
,它用于指定是否严格比较(即,比较时是否考虑类型)。默认为false
,即宽松比较(只比较值,不考虑类型)。而array_search
在进行搜索时,如果找到匹配的值,会返回对应的键。检查弱类型比较
如果在使用
in_array
时没有使用严格比较,它会将'abc'
和$array
中的元素进行宽松比较。宽松比较时,PHP 可能会将'abc'
转换为一个与$array
中某个值匹配的类型,从而得到不符合预期的结果。在你的数组
$array = ['a', 0, 1, 2, '3'];
中,我们可以注意到:
'a'
是字符串。0
和1
是整数。'3'
是字符串'3'
。PHP 在宽松比较时会尝试将
'abc'
和数组中的每个值进行比较。由于'abc'
不符合任何值的类型转换,理论上它不应该匹配任何值。问题排查步骤
- 确认 PHP 配置:检查是否在 PHP 的配置中(如
php.ini
)有其他的设置或自定义函数影响了比较行为。- 代码完整性:确保没有其他代码或文件影响当前代码的输出。
- 更新 PHP 版本:尝试在不同版本的 PHP 中运行代码,以确定是否是版本特定的问题。
代码修正
如果你想确保严格比较,可以使用
in_array
的第三个参数true
:var_dump(in_array('abc', $array, true));
这样,它将进行严格比较,并且只会在值和类型都匹配时才返回
true
。总结
在你提供的代码和输出结果中,
'abc'
不应该匹配任何数组元素。请确认你的代码是否与输出一致,或检查其他潜在的干扰因素。如果依然有疑问,尝试在干净的 PHP 环境中重新运行代码以验证行为。
八、代码演练
<?php
//代码演练
$flag-"flag{xxxx-2020}";
if(empty($_GET['id'])){
show_source(__FILE__);
die();
}
else{
$a ="www.woniuxy.com";
$id = $_GET['id'];
@parse_str($id);
if($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo $flag;
}
else{
exit("no no");
}
}
?>
我们知道 QNKCDZO
经过 md5 处理后的值为 0e830400451993494058024219903391
就是0e 开头的字符串
所以我们只需要传值时传入md5处理后可以以 0e 开头的值就可以,上面列出了很多