经典的PHP类型混淆导致安全漏洞,主要出现在松散比较(==)和弱类型转换中。
1.问题核心
松散比较(==)的问题
// 弱类型比较示例 var_dump(0 == "0"); // true var_dump(0 == "0e123456"); // true (科学计数法) var_dump("0e123" == "0e456"); // true (都被解释为0)MD5哈希的特性
MD5哈希是32位十六进制字符串:
包含字符:0-9, a-f
格式如:
e10adc3949ba59abbe56e057f20f883e当哈希以
0e开头,后跟纯数字时,会被解释为科学计数法的0
2.漏洞原理
科学计数法解释
// PHP将"0e"开头的字符串解释为科学计数法 $hash1 = "0e462097431906509019562988736854"; $hash2 = "0e830400451993494058024219903391"; var_dump($hash1 == 0); // true var_dump($hash1 == $hash2); // true! 漏洞所在 // 因为:0e462... 和 0e830... 都被解释为 0 * 10^...实际攻击场景
// 1. 密码哈希比较漏洞 $user_input = "QNKCDZO"; // md5: 0e830400451993494058024219903391 $stored_hash = md5($user_input); // 0e830... if ($stored_hash == "0") { // 攻击者猜测 echo "绕过成功!"; } // 2. 两个不同字符串的MD5以0e开头 $a = "240610708"; // md5: 0e462097431906509019562988736854 $b = "QNKCDZO"; // md5: 0e830400451993494058024219903391 if (md5($a) == md5($b)) { // true! echo "哈希碰撞!"; }3.已知的0e哈希值
// 原始值 => MD5哈希 $vulnerable_pairs = [ "240610708" => "0e462097431906509019562988736854", "QNKCDZO" => "0e830400451993494058024219903391", "aabg7XSs" => "0e087386482136013740957780965295", "aabC9RqS" => "0e041022518165728065344349536299", "s878926199a" => "0e545993274517709034328855841020", "s155964671a" => "0e342768416822451524974117254469", ];4.漏洞利用场景
场景1:密码重置绕过
// 危险代码示例 $password = $_POST['password']; $hash = md5($password); if ($hash == $stored_hash) { // 使用 == // 攻击者输入"240610708"可能匹配"QNKCDZO"的哈希 }场景2:身份验证绕过
// 登录验证 function checkPassword($input, $stored_hash) { return md5($input) == $stored_hash; // 漏洞! } // 攻击:如果存储的哈希是0e开头的 // 输入任意0e开头的MD5原始值场景3:API签名验证
// 签名验证 $sign = $_GET['sign']; // 应该是MD5 $expected = md5($data . $key); if ($sign == $expected) { // 漏洞 // 如果$expected是0e开头,sign=0也可通过 }5.SHA1的类似问题
// SHA1也存在类似问题 $a = "aaroZmOk"; // sha1: 0e66507019969427134894567494305185566735 $b = "aaK1STfY"; // sha1: 0e76658526655756207688271159624026011393 var_dump(sha1($a) == sha1($b)); // true!6.CTF常见考点
考点1:哈希扩展攻击
// MD5长度扩展攻击 $secret = "secret"; $data = "data"; $hash = md5($secret . $data); // 攻击者可构造 $secret . $data . padding . appended_data // 而无需知道$secret考点2:JSON中的哈希比较
$data = json_decode($_POST['data'], true); if ($data['hash'] == md5($data['input'])) { // 如果data.hash=0,且md5(input)以0e开头 }考点3:数组绕过
// MD5处理数组返回null var_dump(md5([])); // NULL var_dump(md5([]) == NULL); // true代码审计检查点
查找松散比较:全局搜索
==、!=查找MD5/SHA1使用:
md5(、sha1(检查哈希比较位置:
// 危险模式 if ($_GET['hash'] == md5($data)) {...} if (md5($a) == md5($b)) {...}检查JSON/序列化数据:反序列化后可能类型混淆