php中,md5(),用法和绕过姿势 作者: ynnddddd 时间: 2024-08-09 分类: 网络安全,RCE,函数漏洞 1.首先是函数 ------- md5( $str, raw) - $str :需要计算的字符串 - raw :指定十六进制或二进制输出格式 raw 参数控制输出的「格式」: true :16个字符的「二进制格式」 false :(默认)32个字符的十六进制格式 如果你在项目中遇到MD5「加密结果不一致」的问题,可以观察两个加密结果的长度是否相同,比如一个结果是16位,而另一个结果是32位,这种情况就可以考虑更换输出格式来解决。 2.科学计数法(0e绕过) ------------- 由于php中,md5()的性质-----先计算里面的,再md5加密,所以,由于科学计数法,0的多少次幂都是0,所以,**0e开头的任何数,其MD5都是相同的。** 比如 md5('0e1234'),会先运算成 md5(0),再计算MD5值。 输入: echo md5(0).PHP_EOL; echo md5(0e123).PHP_EOL; echo md5(0e456).PHP_EOL; echo md5(0E456); 输出: cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da 所以,遇到比较( md5(a)==md5(b) )时,可以使用 0e绕过。 0e绕过还有一种变体:如果某个字符串的MD5值是0e开头的,在比较时,PHP也会先把它计算成 0,再参与比较。 一些加密后以0e开头的字符串 QNKCDZO => 0e830400451993494058024219903391 240610708 => 0e462097431906509019562988736854 s878926199a => 0e545993274517709034328855841020 s155964671a => 0e342768416822451524974117254469 s214587387a => 0e848240448830537924465865611904 s214587387a => 0e848240448830537924465865611904 https://blog.csdn.net/weixin_46578840/article/details/119569862?spm=1001.2014.3001.5506 3.数组类型(数组绕过) ------------ md5() 不能处理数组,数组都返回null。同时会报一个Warning,不影响执行,不用管。 实例: var_dump(md5([1,2])); var_dump(md5([3,4])); 输出: Warning: md5() expects parameter 1 to be string, NULL Warning: md5() expects parameter 1 to be string, NULL 绕过思路:遇到强比较(a===b)时,可以使用数组绕过。GET传参时,以 a[]=1&b[]=2 这种形式传递数组。 实例: $a = array(1,2,3); $b = array(4,5,6); var_dump(md5($a)===md5($b)); 输出: Warning: md5() expects parameter 1 to be string, Warning: md5() expects parameter 1 to be string, bool(true) 4.暴力破解脚本 -------- 1.破解最后几位字符的python脚本 import hashlib def find_matching_number(target_suffix): number = 0 while True: # 将数字转换为字符串并计算其MD5哈希值 hash_value = hashlib.md5(str(number).encode()).hexdigest() # 检查后六位是否匹配目标值 if hash_value[-n:] == target_suffix: return number, hash_value number += 1 target_suffix = "your_replace_word" result, hash_value = find_matching_number(target_suffix) print(f"匹配的数字是: {result}, 对应的MD5值是: {hash_value}") **使用前修改n和your_replace_word两个参数** import hashlib import itertools import string def crack_sha1(target_hash, max_length): chars = string.ascii_lowercase + string.digits # 可选字符集 for length in range(1, max_length + 1): for attempt in itertools.product(chars, repeat=length): guess = ''.join(attempt) if hashlib.sha1(guess.encode()).hexdigest() == target_hash: return guess return None target_hash = 'your_sha1_hash_here' # 替换为目标SHA-1哈希 max_length = 5 # 设置最大长度 result = crack_sha1(target_hash, max_length) if result: print(f'原字符串是: {result}') else: print('未找到匹配的原字符串。') 穷举法破解sha1() 5.sql注入漏洞 select * from usera where username = 'admin' and password = md5($pass,true) 若我们可找到字符串,在对该字符串进行md5后能够得到 'or’加上一个非0的字符就可以绕过。这里我们可以用到的字符串为:**ffifdyop**。它的md5结果是:276f722736c95d99e921722cf9ed621c 。通过这个结果我们可以发现得到16字节的二进制被解析为字符的结果是:'or’6后面接乱码 (27是单引号的16进制编码,67是字母o的16进制…)这样后构造的sql语句就位 select * from user where password=' 'or'6xxx' 原理解释: ![2024-10-08T12:01:49.png][1] 32位16进制字符串的意思是:将MD5加密得到的128 位长度的"指纹信息",以每4位为一组,分为32组,每组以转换为16进制,进行转换得到一个32位的字符串。总的来说就是我们平时看到的MD5加密的结果 16位原始二进制格式的字符串的意思是:将128 位长度的"指纹信息"分组转化为16位的一个字符串,也就是说有'true'时返回值就是8位一个字符的字符串 而对于32位的字符串来讲,是以4位一个字符的, 所以如果想通过32位字符串推出16位原始二进制格式的字符串的话,必须两个字符一组分别转化为二进制后拼接起来组成一个字符也就是说如果32位可控,则16位必可控 这里咱们sql注入,需要'or'开头的话,先将'or'转化为ascll码,分割后变为16进制即可 ![2024-10-08T12:41:16.png][2] 经过前人的推理,字符串 **ffifdyop**可以实现276f7227开头,可以实现'or'开头 [1]: http://ynndboke.top/usr/uploads/2024/10/432397006.png [2]: http://ynndboke.top/usr/uploads/2024/10/1953445456.png 标签: 绕过姿势, md5, php