PHP代码审计中的MD5比较陷阱:从BugKu题目看数组绕过与科学计数法漏洞
在CTF竞赛和实际安全审计中,PHP的MD5比较漏洞一直是高频考点。这类漏洞往往源于开发者对PHP弱类型比较机制的理解不足,特别是在使用==运算符时的类型转换行为。让我们通过一道经典的BugKu题目,深入剖析两种最常见的MD5比较漏洞:数组绕过和科学计数法漏洞。
1. PHP弱类型比较机制解析
PHP作为弱类型语言,其比较运算符==在进行比较时会自动进行类型转换,这为安全漏洞埋下了隐患。当比较两个不同类型的值时,PHP会尝试将它们转换为相同类型后再比较。
php复制var_dump(0 == "a"); // bool(true)
var_dump("123" == 123); // bool(true)
这种隐式类型转换在MD5比较时尤为危险。考虑以下代码:
php复制if (md5($input1) == md5($input2)) {
// 敏感操作
}
当$input1和$input2满足特定条件时,即使它们的原始值不同,也可能通过这个检查。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数组绕过:当MD5遇到NULL
第一种漏洞利用方式是向MD5函数传入数组参数。根据PHP官方文档,md5()函数处理数组时会返回NULL:
php复制$arr = array('foo', 'bar');
var_dump(md5($arr)); // NULL
在BugKu题目中,开发者要求两个不同的key具有相同的MD5值:
php复制if(md5($key1) == md5($key2) && $key1 !== $key2){
echo $flag;
}
利用数组特性,可以构造如下Payload:
code复制?kekeyy1[]=a&kekeyy2[]=b
此时:
md5($key1)和md5($key2)都返回NULLNULL == NULL为true$key1 !== $key2也为true(两个不同数组)
注意:这种漏洞在PHP 8.0+中已部分修复,直接传入数组会产生警告,但在7.x及以下版本仍然有效。
3. 科学计数法漏洞:0e开头的魔法数字
第二种漏洞利用科学计数法比较的特性。PHP会将形如"0e1234"的字符串视为科学计数法(0×10^1234),其值为0。
已知一些字符串的MD5值恰好以"0e"开头:
| 原始字符串 | MD5哈希值 |
|---|---|
| 240610708 | 0e462097431906509019562988736854 |
| QNKCDZO | 0e830400451993494058024219903391 |
| s878926199a | 0e545993274517709034328855841020 |
构造Payload:
code复制?kekeyy1=240610708&kekeyy2=QNKCDZO
比较时:
md5("240610708") == "0e462097431906509019562988736854"md5("QNKCDZO") == "0e830400451993494058024219903391"- PHP将两边都转换为科学计数法的0,因此
0 == 0为true - 原始字符串明显不同,满足
$key1 !== $key2
4. 防御措施与安全编码实践
要避免这类漏洞,开发者应采取以下措施:
-
使用严格比较运算符
===:php复制if (md5($key1) === md5($key2)) { // 安全比较 } -
优先使用
hash_equals()函数(防时序攻击):php复制if (hash_equals(md5($key1), md5($key2))) { // 更安全的比较方式 } -
输入类型检查:
php复制if (!is_string($key1) || !is_string($key2)) { die("Invalid input type"); } -
现代PHP版本升级:
- PHP 8.0+对类型转换更加严格
- 考虑使用类型声明:
php复制function compareHashes(string $key1, string $key2): bool { return md5($key1) === md5($key2); }
5. 实际审计中的扩展思考
在真实业务代码审计时,除了MD5比较,还应注意:
- 松散比较链:多个
==比较组合可能放大风险 - 哈希函数选择:MD5已被证明不安全,应考虑SHA-2或SHA-3系列
- 上下文相关漏洞:如JSON解码后的类型转换问题
一个典型的审计checklist应包含:
- 全局搜索
==和md5(的组合 - 检查所有用户输入是否经过类型验证
- 确认是否使用了足够强的哈希算法
- 检查是否存在哈希比较时序漏洞
在最近的一次企业级代码审计中,我们发现一个后台系统使用if (md5($_POST['admin_token']) == $stored_hash)进行权限验证,通过科学计数法漏洞,攻击者可以无需知道真实token即可获得管理员权限。修复方案是将其改为hash_equals(md5($_POST['admin_token']), $stored_hash),同时增加了输入类型检查。
