Web安全中的哈希函数

md5

弱碰撞

php中的MD5碰撞常与若比较进行结合

这种情况下,弱碰撞即可解决

  1. 弱比较:

    如果两个字符经MD5加密后的值为 0exxxxx形式,就会被认为是科学计数法,且表示的是0*10的xxxx次方,还是零,都是相等的。

    下面代码为具体情况,

    1
    2
    3
    if($_POST['a']!=$_POST['b'&& md5($_POST['a'])==md5($_POST['b'])){
    die("success!");
    }

    下列的字符串的MD5值都是0e开头的:

    1
    2
    3
    4
    5
    6
    QNKCDZO
    240610708
    s878926199a
    s155964671a
    s214587387a
    s214587387a

    双重MD5加密后0E开头:

    1
    2
    3
    7r4lGXCH2Ksu2JNT3BYM
    CbDLytmyGm2xQyaLNhWn
    770hQgrBOjrcqftrlaZk
  2. 强比较:

    md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。 例如 a[]=1&b[]=2。

    具体情况如下,

    1
    2
    3
    if ($_POST['a'] !== $_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
    die("success!");
    }
  3. 强比较:

    NAN和INF,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN=NAN都是false,但md5('NaN')=md5('NaN')为true。

强碰撞

加了一个string转换,导致==绕不过去(因为空数组转字符串为null,数组转换为字符串时都会变成Array)

具体情况如下,

1
2
3
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
die("success!");
}

这样我们就必须运用MD5强碰撞

比如,

1
2
3
md5("TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak")
=
md5("TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak")

当然,有时候题目或许会要求我们两个碰撞参数的前缀相同,那么此时,我们就可以定制前缀一致的MD5强碰撞(比如上面这对,前缀就是一样的)

可以有的三种方法

注意一点,post时一定要urlencode!!!

MD5爆破

有时候,还会要求根据MD5碰撞后的结果,倒推出部分字符,这时候我们就必须借助工具:

比如,

1
hashcat -a 3 -m 0 8df2643c35506af082e7a97c813b133d flag{753?h22?h6-05?hf-441?h-b?h01-822?h5f04c?h3?h}

使用4060无CUDA驱动的情况下,20秒完成,爆破出flag{75382286-055f-441d-b601-82275f04c13a}

有时候甚至会直接给你一个MD5值或者双MD5值,让你爆破出原值,

这时候我们往往采用在线网站,如:https://www.cmd5.com/

md5截断爆破

代码如下:

1
substr(md5(?),0,5)==='8ffb1'

一般使用脚本

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
27
28
29
30
31
32
33
34
35
import hashlib
from multiprocessing.dummy import Pool as ThreadPool

# MD5截断数值已知 求原始数据
# 例子 substr(md5(captcha), 0, 6)=60b7ef

def md5(s): # 计算MD5字符串
return hashlib.md5(str(s).encode('utf-8')).hexdigest()


keymd5 = '8ffb1' #已知的md5截断值
md5start = 0 # 设置题目已知的截断位置
md5length = 5

def findmd5(sss): # 输入范围 里面会进行md5测试
key = sss.split(':')
start = int(key[0]) # 开始位置
end = int(key[1]) # 结束位置
result = 0
for i in range(start, end):
# print(md5(i)[md5start:md5length])
if md5(i)[0:5] == keymd5: # 拿到加密字符串
result = i
print(result) # 打印
break


list=[] # 参数列表
for i in range(10): # 多线程的数字列表 开始与结尾
list.append(str(10000000*i) + ':' + str(10000000*(i+1)))
pool = ThreadPool() # 多线程任务
pool.map(findmd5, list) # 函数 与参数列表
pool.close()
pool.join()

SHA1

常规碰撞

与MD5大差不差

以下值在sha1加密后以0E开头:

1
2
3
4
5
6
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
0e1290633704
10932435112

如果是强比较,没有转为string,可以用数组 例如 a[]=1&b[]=2来绕过,也可以用强碰撞;

如果是强比较,但转为string,用强碰撞

截断爆破

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
27
28
29
30
31
32
33
34
35
import hashlib
from multiprocessing.dummy import Pool as ThreadPool

# sha256截断数值已知 求原始数据
# 例子 substr(sha256(captcha), 0, 6)=60b7ef

def sha256(s): # 计算sha256字符串
return hashlib.sha256(('TQLCTF'+str(s)).encode('utf-8')).hexdigest()


keysha256 = '5625f' #已知的sha256截断值
sha256start = 0 # 设置题目已知的截断位置
sha256length = 5

def findsha256(sss): # 输入范围 里面会进行sha256测试
key = sss.split(':')
start = int(key[0]) # 开始位置
end = int(key[1]) # 结束位置
result = 0
for i in range(start, end):
# print(sha256(i)[sha256start:sha256length])
if sha256(i)[0:5] == keysha256: # 拿到加密字符串
result = i
print(result) # 打印
break


list=[] # 参数列表
for i in range(10): # 多线程的数字列表 开始与结尾
list.append(str(10000000*i) + ':' + str(10000000*(i+1)))
pool = ThreadPool() # 多线程任务
pool.map(findsha256, list) # 函数 与参数列表
pool.close()
pool.join()

至于其他的碰撞,如SHA-224、SHA-256等,我们可以查找一个hash碰撞收集库:https://github.com/spaze/hashes

这里就不再详解


Web安全中的哈希函数
http://example.com/2025/07/13/28Web安全中的哈希函数/
作者
sangnigege
发布于
2025年7月13日
许可协议