【CTF】PHP 的弱类型与 Hash 比较缺陷(伪 MD5 碰撞)

2018年9月28日 1 条评论 98 次阅读 1 人点赞

题目引入

先来看一个 CTF Web 题目吧,这道题目的 PHP 源代码已经给出来了,如下:

(题目来源:2018 SYSUCSA 招新赛)

<?php
$a = @$_GET['a'];
$b = @$_GET['b'];
$a_md5 = @md5($a);
$b_md5 = @md5($b);
if(isset($a) && isset($b)){
    if ($a != $b && $a_md5 == $b_md5) {
        echo "sysucsa{************}";
    } else {
        echo "Sorry, just think more";
    }
}  
else{
    echo "I guess you know how to see the source code";
}
?>

从第 7 行可以看出,该题要求输入两个不相等的变量 $a 和 $b,而其 MD5 值却相等。

 

弱类型比较

PHP 中存在 == (相等)!= (不等)=== (全等)!== (不全等)两组运算符。PHP 手册中有如下一段话:

如果用 == 或 != 比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。当用 === 或 !== 进行比较时则不进行类型转换,因为此时类型和数值都要比对。

请看以下例子:

$a1 = '123';
$a2 = 123;

echo ($a1 == $a2) ? 'Yes' : 'No';
echo ($a1 === $a2) ? 'Yes' : 'No';

结果分别为“Yes”和“No”,可以看出,整数 123 和字符串'123'是相等的,但是不全等。

 

如果字符串中含有科学记数法(e 表示法)呢?请继续阅读以下代码:

$b1 = '3e1';
$b2 = 30;
echo ($b1==$b2) ? 'Yes' : 'No';
$c1 = '3e5'; $c2 = '300000';
echo ($c1==$c2) ? 'Yes' : 'No';

结果分别为“Yes”和“Yes”。请注意,'3e1'代表 \( 3\times 10^1 \),它与整数 30 相等'3e5'代表 \(3\times 10^5 \),它与字符串'300000'也相等!到此,我们大致理解了 PHP 弱类型比较的特点了。

 

接下来,再阅读一段代码,感受一下弱类型比较与 Hash 比较缺陷之间的关系。

$d1 = '0e12345';
$d2 = '0e67890';
echo ($b1==$b2) ? 'Yes' : 'No';

是的,这段代码同样会输出“Yes”。这是因为 $d1 和 $d2 两个字符串均以 '0e' 开头,用科学记数法表示就是 \(0\times 10^n \),而 0 乘以任何数都为 0,因此它们相等就不足为怪了。

 

Hash 比较缺陷

如果采用 == 运算符比较哈希值,同样会面临上面讲的弱类型比较的问题,即任何以'0e'开头的字符串都相等

所以解决本文开头 CTF 题目的关键点就在于,找到两个不同的字符串,其 MD5 值都以'0e'开头。以下表格列出了一些 MD5 值是'0e'开头的字符串(来源于网络)

源字符串 MD5(32位)
s878926199a 0e545993274517709034328855841020
s155964671a 0e342768416822451524974117254469
s214587387a 0e848240448830537924465865611904
s214587387a 0e848240448830537924465865611904
s878926199a 0e545993274517709034328855841020
s1091221200a 0e940624217856561557816327384675
s1885207154a 0e509367213418206700842008763514
s1502113478a 0e861580163291561247404381396064
s1885207154a 0e509367213418206700842008763514
s1836677006a 0e481036490867661113260034900752
s155964671a 0e342768416822451524974117254469
s1184209335a 0e072485820392773389523109082030
s1665632922a 0e731198061491163073197128363787
s1502113478a 0e861580163291561247404381396064
s1836677006a 0e481036490867661113260034900752
s1091221200a 0e940624217856561557816327384675
s155964671a 0e342768416822451524974117254469
s1502113478a 0e861580163291561247404381396064
s155964671a 0e342768416822451524974117254469
s1665632922a 0e731198061491163073197128363787
s155964671a 0e342768416822451524974117254469
s1091221200a 0e940624217856561557816327384675
s1836677006a 0e481036490867661113260034900752
s1885207154a 0e509367213418206700842008763514
s532378020a 0e220463095855511507588041205815
s878926199a 0e545993274517709034328855841020
s1091221200a 0e940624217856561557816327384675
s214587387a 0e848240448830537924465865611904
s1502113478a 0e861580163291561247404381396064
s1091221200a 0e940624217856561557816327384675
s1665632922a 0e731198061491163073197128363787
s1885207154a 0e509367213418206700842008763514
s1836677006a 0e481036490867661113260034900752
s1665632922a 0e731198061491163073197128363787
s878926199a 0e545993274517709034328855841020

 

写在后面

要在 PHP 中比较 Hash 值,应该考虑使用 hash_equals 函数(PHP 5.6 及以上),关于该函数的更多信息请参考 PHP 手册:http://php.net/hash_equals

 

Jed

一名狂热的技术爱好者。

文章评论(1)

  • J2

    那用===比较哈希可以么

    2018年9月30日
  • 提示:有人回复时会邮件通知您