MD5()函数

MD5函数漏洞

1
2
$_GET['param1'] != $_GET['param2']
md5($_GET['param1']) == md5($_GET['param2'])

PHP在处理哈希字符串时,它把每一个以“0E”开头且后面都是数字的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以“0E”开头的,那么PHP将会认为他们相同,都是0。

原理:“0e111″==”0e222”相互比较的时候,会将0e这类字符串识别为科学技术法的数字,0乘以10的无论多少次方都是零,所以相等。

示例(moectf 2023 meo图床)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

highlight_file(__FILE__);

if (isset($_GET['param1']) && isset($_GET['param2'])) {
$param1 = $_GET['param1'];
$param2 = $_GET['param2'];

if ($param1 !== $param2) {

$md5Param1 = md5($param1);
$md5Param2 = md5($param2);

if ($md5Param1 == $md5Param2) {
echo "O.O!! " . getenv("FLAG");
} else {
echo "O.o??";
}
} else {
echo "o.O?";
}
} else {
echo "O.o?";
}

?>

那么只要param1和param2分别传输两个不一样但是MD5加密后是“0E”开头的值即可。

弱口令

1
2
$_GET['param1'] != $_GET['param2']
md5($_GET['param1']) === md5($_GET['param2'])

在php中“===”可以理解为“强相等”,也就是值和类型都一样。这样的话使用“QNKCDZO”“240610708”就不可行了。

但是,在PHP中,对数组直接使用md5()函数会返回NULL。也就是任何数组的md5哈希都是NULL,所以它们是强相等的。

1
[1] != [2]

因此数组[1]不等于数组[2](内容不同),但是要是进行md5的话确是强相等。

因此只需要让param1[]=1&param2[]=2即可。

MD5碰撞

1
2
(string)$_GET['param1'] !== (string)$_GET['param2']  // 两个字符串不同
md5($_GET['param1']) === md5($_GET['param2'])

要求传入两个不同的字符串param1param2,并且强制转换成了字符串!!这就意味着不能用数组了。但它们的 MD5 哈希值又要必须相同。

理论上不同的输入应该有不同的哈希值。但由于 MD5 已被证明存在碰撞漏洞(即不同的输入可以产生相同的 MD5 值),我们可以构造这样的数据。

1
2
3
param1 = %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2

param2 = %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

这两个数据只有 几个字节不同,但 MD5 计算后结果相同。(这种碰撞数据通常由 专门的 MD5 碰撞生成工具(如 fastcoll)生成。)