php应用-弱类型脆弱&Hash加密&Bool类型&Array数组&函数转换比较(小迪安全Day29)

==(非严格比较,弱类型转换)

PHP 的非严格比较(==)规则:

  • 若类型不同,则进行隐式类型转换

  • 字符串和数字比较时,字符串会转换成数字(如果可能的话)。

  • 无法转换成数字的字符串会变成 0,然后再进行比较。

正常情况


$type=$_GET['pay'];
if($type == 'pay'){ //这里使用双等号进行判断
    echo "正常进入支付逻辑";
}else{
    echo "退出进入支付逻辑";
}

?pay==pay 显示正常进入支付逻辑

?pay==payssss 显示退出进入支付逻辑

不正常情况


$type=$_GET['pay'];
if(0 == '0aaa'){
    echo "正常进入支付逻辑";//这里使用双等号进行判断
}else{
    echo "退出进入支付逻辑";
}

?pay==payssss 显示正常进入支付逻辑

原因

  • 0 == '0aaa',因为 PHP 先将 '0aaa' 转换为数字 0,再进行比较。

  • 0 == 0,所以判断条件成立,造成绕过。

案例

var_dump(0 == 'pay');//true
var_dump('0e123456789'==0);// bool(true)
var_dump('0e123456789'=='0');// bool(true)
var_dump('0e1234abcde'=='0');// bool(false)

当两个值的类型相等时,就会正常比较,所以最后一个为false

MD5对比缺陷

实战思考

应用场景:后台登录时,账号密码的比对,密码经常会采用MD5加密

include 'config.php';
$user=$_GET['username'];
$pass=md5($_GET['password']);//因为数据库中存储的密码都是进行加密过的,所以要把输入的值也进行加密然后代入数据库进行比对

代码逻辑

MD5 以 0e 开头时可触发弱比较


$a = $_GET['pwd'];
$password = "0e50936721341820084200876514";  //注意:这里管理员密码md5的值是以0e开头的,如果没有看到0e而直接去解md5九成是解不出来的
if(md5($a) == $password){  //注意:这里是两个等号"=="进行判断,若是"==="则不存在弱类型hash比较缺陷
    echo '恭喜进入后台';
}else{
    echo '登录失败';
}

绕过方式:

?pwd=240610708

?pwd=QNKCDZO

?pwd=s1885207154a,

传递这3个值都显示登录成功

原因 :由于 0e 开头的字符串被 PHP 解析为科学计数法 0,导致 0 == 0,从而绕过

列举MD5值开头为0e的值

QNKCDZO

0e830400451993494058024219903391

240610708

0e462097431906509019562988736854

s878926199a

0e545993274517709034328855841020

s155964671a

0e342768416822451524974117254469

s214587387a

0e848240448830537924465865611904

s214587387a

0e848240448830537924465865611904

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

s1885207154a

0e509367213418206700842008763514

函数strcmp类型比较缺陷


$password="***************";
if(isset($_GET['password'])) {
    if (strcmp($_GET['password'], $password) == 0) {
        echo "Right!!!login success";
        exit();
    } else {
        echo "Wrong password..";
    }
}

输入:?password=*************** 显示 Right!!!login success

输入:其他的 显示 Wrong password…

绕过方式:?password[]=xxxx

原理: 低版本的`strcmp比较的是字符串内容大小,如果强行传入其他类型参数,会出错,出错后返回值0,strcmp正常比较两值结果相等时返回的值为0,正是利用这点进行绕过

json_decode()和 unserialize()的 bool 类型漏洞

原理: 在使用 json_decode() 函数或 unserialize() 函数时,部分结构被解释成 bool 类型,某些输入会被解析成 true,导致绕过。

json_decode()

//可以当作是后台登录的验证
<?php
$str=$_GET['s'];
$data = json_decode($str,true);
if ($data['user'] == 'xiaodi' && $data['pass']=='xiaodisec')
{
    print_r(' 登录成功! '."\n");
}else{
    print_r(' 登录失败! '."\n");
}

绕过方式:?s={“user”:true,“pass”:true} 显示登录成功

unserialize()


$str=$_GET['s'];
$data = unserialize($str);
if ($data['user'] == 'root' && $data['pass']=='xiaodi')
{
    print_r(' 登录成功! '."\n");
} else{
    print_r(' 登录失败! '."\n");
}

正常输入:?s=a:2:{s:4:“user”;s:4:“root”;s:4:“pass”;s:6:“xiaodi”;}

PHP 的序列化格式解析:

  • a:2;数组(array),包含 2 个元素
  • {...}数组的内容
  • s:4:"user";键 “user”,是一个长度为 4 的字符串
  • s:4:"root";值 “root”,是一个长度为 4 的字符串
  • s:4:"pass";键 “pass”,是一个长度为 4 的字符串
  • s:6:"xiaodi";值 “xiaodi”,是一个长度为 6 的字符串

绕过方式:?s=a:2:{s:4:“user”;b:1;s:4:“pass”;b:1;}

(其中 b:1 表示为 布尔类型 ,为true)

函数switch 类型比较缺陷

$num =$_GET['n'];
switch ($num) {
    case 0:
        echo "say none hacker ! ";
        break;
    case 1:
        echo "say one hacker ! ";
        break;
    case 2:
        echo "say two hacker ! ";
        break;
        default;
        echo "I don't know ! ";
}

绕过方式:?n=1safafafaf

原理:当在 switch 中使用 case 判断数字时,switch 会将其中的参数转换为 int 类型进行计算,只要第一值对应上了,后面的就不判断了

in_array()和 array_search() 的类型转换


$array=[0,1,2,'3'];
var_dump(in_array('abc', $array));//true
var_dump(array_search('abc', $array));//0: 下标
var_dump(in_array('1dsdsdsbc', $array));//true
var_dump(array_search('1bc', $array));//1: 下标

php应用-弱类型脆弱&Hash加密&Bool类型&Array数组&函数转换比较(小迪安全Day29)_第1张图片

原理: 当使用in_array()或array_search()函数时,如果第三个参数没有设置为true,则in_array()或array_search()将使用松散比较来判断

当加了第三个参数,那么就是强对比===

===强对比数组比较缺陷

注意此时遇到的是 “===” ,不过也不是代表无从下手。在md5()函数传入数组时会报错返回NULL,当变量都导致报错返回NULL时就能使使得条件成立。


error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
    if ($_GET['username'] == $_GET['password'])
        print 'Your password can not be your username.';
    else if (md5($_GET['username']) === md5($_GET['password']))
        die('Flag: '.$flag);
    else
        print 'Invalid password';
}

关键逻辑

  • 第一步$_GET['username'] == $_GET['password']
    • 这里使用的是 ==,所以会进行类型转换
    • 但我们需要 usernamepassword 不相等,否则会触发第一个 if,导致失败。
  • 第二步md5($_GET['username']) === md5($_GET['password'])
    • 这里使用了 ===,意味着 要求两者完全相等(包括数据类型和内容)
    • 由于 MD5 是一个哈希函数,通常不同输入不会生成相同的哈希值,所以很难直接找到普通的 MD5 碰撞

绕过点:md5() 处理数组的特殊情况

在 PHP 中:

var_dump(md5([])); // NULL
  • md5() 处理数组时,会报错并返回 NULL
  • 由于 NULL === NULL成立的,我们就可以让 md5($_GET['username'])md5($_GET['password']) 都等于 NULL,从而绕过 ===

绕过方式:

?username[]=1&password[]=0

总结

要绕过 ===,必须让两个变量:

  1. 都返回 NULL(如 md5([]) === md5([]))。
  2. 都返回 false(如 strcmp([], []) === strcmp([], []))。
  3. 都返回 0(如 intval([]) === intval([]))。
  4. 都返回 ""(如 json_encode([]) === json_encode([]))。
  5. 都是空数组 [] === []

这些方法通常依赖 PHP 函数返回值的特殊行为

代码审CMS:

https://mp.weixin.qq.com/s/k1hRg7cmRwwJJyyX04Ipmg

是会员登录和后台登录,条件是必须是密文为0e所以没什么用

CTF题目:

https://ctf.bugku.com/challenges/detail/id/72.html
https://ctf.bugku.com/challenges/detail/id/94.html

你可能感兴趣的:(小迪安全,php,安全,网络安全,web安全)