对强网杯2018“Web签到”的思考
题目地址: http://39.107.33.96:10000/
题目共分为三层
<!-- |
这里PHP在处理哈希字符串时,利用”==”来对哈希值进行比较,把每一个以”0E”开头的哈希值都解释为0,因此如果两个不同的密码经过哈希以后,其哈希值都是以”0e”开头的,那么PHP将会认为它们都是0。
原理
在PHP中,e可以指幂,因此
0e123456 等价于 0*10^123456
0乘任何数等于零,因此0e开头的md5全部等于0,也就做到了这里的
md5($_POST['param1'])==md5($_POST['param2']
这里我使用的是
s878926199a md5: 0e545993274517709034328855841020
s155964671a md5: 0e342768416822451524974117254469
第二层
<!-- |
这里变成了===操作符,严格等于,因此我们无法利用0e
因此我们需要符合的条件为构建两个参数 使得他们类型或者值不相同 但哈希值相同
这里我们考虑到,PHP的md5函数用法如下
md5(string,raw)
string 必需。规定要计算的字符串。
raw
可选。规定十六进制或二进制输出格式:
TRUE - 原始 16 字符二进制格式
FALSE - 默认。32 字符十六进制数
返回值: 如果成功则返回已计算的 MD5 散列,如果失败则返回 FALSE。
因此,如果md5中的参数不是字符串(string),则会返回null
所以如果我们传入两个值不同的数组,就会出现
null===null
符合条件
第三层
<!-- |
这次PHP将两个参数强行转成string.
没有漏洞,我们只能考虑md5碰撞
原理(个人理解,如有错误请指正)
2005年王小云提出了md5哈希碰撞,公式如下
f(f(s, M), M') = f(f(s, N), N')
先将明文进行64bit分组,不足的则填充,这样它的长度将是64字节的倍数。
接下来将其分为单个的64bit字节块M0, M1, …, Mn-1。
而md5的hash是由16字节的序列s0, …, sn来计算。计算规则为:si+1 = f(si, Mi) ,(i为角标), f是一个固定函数。s0是初始化向量,sn是最终状态,即计算的md5 hash
对于给定的初始化向量s,找出两组M,M’和N,N’,使得f(f(s,M),M’)= f(f(s,N),N’) ,重要的是这里不仅仅适用于标准初始化矢量s0,而对于任何初始化矢量s都适用。
也就是说对于两个不同的消息分组 M、N,s 相同,经过两次 f 函数后得到的结果是一样的。
因此可以找到任意长度的对文件,除了文件中间的128个字节,它们是相同的,而且它们具有相同的MD5散列。
m0 m1经过两次f函数所得的hash是相同的
如果对这个有兴趣,推荐下面的网站
md5相关知识: http://www.unixwiz.net/techtips/iguide-crypto-hashes.html
可视化md5碰撞演示: http://www.links.org/?p=6
用相同的MD5散列创建两个PHP文件: https://natmchugh.blogspot.com/2014/10/how-i-made-two-php-files-with-same-md5.html
Are there two known strings which have the same MD5 hash value?: https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value
我们使用md5碰撞软件fastcoll生成两个md5相同的文件,经url编码后上传即可得到flag。