浅谈md5弱类型比较和强碰撞
字数 1015 2025-08-15 21:33:44
MD5弱类型比较与强碰撞技术详解
一、PHP比较运算符基础
在PHP中,存在两种比较运算符:
-
==(弱类型比较):
- 比较时会进行类型转换
- 数字和字符串比较时,字符串会被转换为数值
0e开头且都是数字的字符串,弱类型比较等于0
-
===(强类型比较):
- 先比较类型是否相同
- 类型不同直接返回false
- 类型相同再比较值
二、MD5弱类型比较漏洞
漏洞原理
当使用==比较两个MD5哈希值时,PHP会进行类型转换,导致特定格式的哈希值会被视为相等。
利用方法
测试代码示例:
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b']) {
if (md5($_POST['a']) == md5($_POST['b']))
echo 'flag';
else
echo 'you are wrong';
} else
echo "请输入不同的a,b值";
}
解法1:利用数组特性
- MD5无法加密数组,加密数组会返回NULL
- 可以传入两个不同的数组参数:
a[]=1&b[]=2
解法2:利用0e开头的哈希值
- 寻找两个不同字符串,其MD5值都以
0e开头且后面全是数字 - 这类哈希值在弱比较时会被视为科学计数法的0
已知0e开头的MD5值示例:
s1502113478a -> 0e861580163291561247404381396064
s1885207154a -> 0e509367213418206700842008763514
s1836677006a -> 0e481036490867661113260034900752
s155964671a -> 0e342768416822451524974117254469
s1184209335a -> 0e072485820392773389523109082030
生成脚本:
for($a=1; $a<=1000000000; $a++) {
$md5 = md5($a);
if(preg_match('/^0e\d+$/', $md5)) {
echo $a . "\n" . $md5 . "\n";
}
}
三、MD5强碰撞技术
漏洞原理
当使用===严格比较时,需要找到两个不同的输入,产生完全相同的MD5哈希值。
利用方法
测试代码示例:
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b']) {
if (md5($_POST['a']) === md5($_POST['b']))
echo 'flag';
else
echo 'you are wrong';
} else
echo "请输入不同的a,b值";
}
解法1:利用数组特性(同上)
解法2:使用MD5碰撞生成器
-
使用工具
fastcoll生成碰撞:fastcoll_v1.0.0.5.exe -p source.txt -o 1.txt 2.txt -
验证碰撞:
function readmyfile($path) {
$fh = fopen($path, "rb");
$data = fread($fh, filesize($path));
fclose($fh);
return $data;
}
echo '二进制md5加密 '. md5((readmyfile("1.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("1.txt"));
echo "</br>";
echo '二进制md5加密 '.md5((readmyfile("2.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("2.txt"));
echo "</br>";
- 由于生成的二进制文件包含不可见字符,需进行URL编码后使用
四、进阶挑战与解决方案
案例1:限制只能传入字符串
题目代码:
if((string)$_GET['a'] !== (string)$_GET['b'] && md5($_GET['a'])===md5($_GET['b'])) {
echo "you are right";
} else {
echo "you are wrong";
}
解决方案:
必须使用MD5碰撞生成器生成的两个不同但MD5相同的字符串
案例2:HECTF ezphp
题目代码:
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
if($_GET['param1']!==$_GET['param2'] && md5($_GET['param1'])===md5($_GET['param2'])) {
if(is_numeric($string_1)) {
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2) {
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b) {
echo $flag;
}
} else {
die("md5 is wrong");
}
} else {
die('str1 not number');
}
}
解决方案:
- param1和param2传入两个数组
- str1必须是数字
- 需要找到两个字符串,其MD5值:
- 不相等
- 经过strtr转换后(c→0, x→1, h→2, p→3)变为以0e开头的数字字符串
优化查找脚本:
for($a = 1; $a <= 100000000; $a++) {
$md5 = strtr(md5($a), 'cxhp', '0123');
if(preg_match('/^0e\d+$/', $md5)) {
echo $a . "\n" . $md5 . "\n";
}
}
案例3:随机数参与的弱比较
题目代码:
function random() {
$a = rand(133,600)*78;
$b = rand(18,195);
return $a+$b;
}
$r = random();
if((string)$_GET['a'] == (string)md5($_GET['b'])) {
if($a.$r == $b) {
print "Yes,you are right";
} else {
print "you are wrong";
}
}
解决方案:
- 使
$_GET['a']以0e开头 $_GET['b']的MD5值也以0e开头- 由于
$r是数字,$a.$r仍会被解析为0
五、防御措施
- 始终使用
===进行严格比较 - 避免直接比较哈希值,可比较原始数据
- 对用户输入进行严格过滤和类型检查
- 考虑使用更安全的哈希算法如SHA-256
六、工具与资源
- MD5碰撞生成器:fastcoll
- 在线MD5碰撞数据库
- 哈希值生成与验证工具
通过掌握这些技术,可以更好地理解MD5哈希函数的安全特性,并在CTF比赛和安全测试中有效应用。