MD5特征
MD5由于0-9,a-f组成的最低16位,最高32位的哈希字符串。
md5本质也是哈希函数,是一种单向密码体制,在上面提到的MD5不可逆性,主要是体现在任意值可以转换成MD5,但是MD5却不能直接逆转成一个准确的字符串,就算是100位字符串转成md5,也是会得到一串32位的哈希字符串,在某乎看了一些大佬的解释,普通字符串在加密的md5过程中,只会挑取一些有用的信息进行计算,过程中丢弃了大部分的信息,这也是不可逆的原因,就好比一盆泼出去的水,怎么可能完整收的回来。一个字符串可能就只是对应一个md5值,但是算出来的md5值可能会对应多个值,虽然这看起来不可逆,但是通过MD5碰撞还是有可能逆向出来的。
现在最大的md5查询网站还是md5在线解密破解,md5解密加密 (cmd5.com),可以解出来比较多常见的md5编码。
"=="和"==="的区别
==
是等于运算符,而===
是全等运算符,两者之间最大的区别在于如何处理等号两边的字符串。首先==
是一种常见于弱类型题目中,之所以常用于弱类型,是因为在进行比较时,不同类型的,会被转换成数字,如下所示:
var_dump('1' == 1); //true
var_dump('1a' == 1); //true
var_dump('1a1' == 1); //true
var_dump('a123' == 0); //true
var_dump('1aa' == 0); //false
同时还要特别提到一点,就是0exxxxxxxxxxxxxxxxxxxxxxxxxxxx
,php在处理0e开头的字符串时,会当成科学计数法进行处理,0e123222222等于0,0e66666666666666666666等于0,0e后面跟任何数字都等于0,php手册的解释如下
翻译:这种比较是正确的,因为两个md5()散列都以“0e”开头,所以PHP类型杂耍将这些字符串理解为科学符号。根据定义,任何幂的零都是零。
弱类型比较
show_source(__FILE__);
include('flag.php');
\$username = \$_GET['username'];
\$password = \$_GET['password'];
if(\$username != \$password){
if(md5(\$username) == md5(\$password)){
echo 'GET_FLAG:'.\$flag;
}else{
echo 'md5校验出错...';
}
}else{
echo '用户名密码不能相等!';
}
上面的例题中,要求两个字符串不能相等,但是又要求他们的MD5相等,看着无理取闹,实际上一切皆有可能。
上面我们补充了一个点,就是0e后面无论跟多大的数字都等于0,而且代码中的第四行md5($key_1) == md5($key_2)
用的是==
,也就是说,这里存在弱类型比较,由于md5几乎很难找得到相等的,除非被加密的两个值相等,还真的想不出不同值如何相等,但是如果让两个0e开头的字符串进行比较呢?这里任意挑了四个经过md5加密后是0e开头的字符串来进行对比,结果都等于true。
说明两个0e开头的值进行比较,会被当成科学计数法,0e永远等于零,所以就比较成功了。所以这一题的题解意见很明显了,就是找两个值加密后0e开头,且0e后面是纯数字的字符串即可,这里列出来一些常见的:
字符串 | 所对应的MD5值 |
---|---|
QNKCDZO | 0e830400451993494058024219903391 |
s878926199a | 0e545993274517709034328855841020 |
s155964671a | 0e342768416822451524974117254469 |
---|---|
s1502113478a | 0e861580163291561247404381396064 |
s1885207154a | 0e509367213418206700842008763514 |
---|---|
s214587387a | 0e848240448830537924465865611904 |
还有很多的相近的值转换各位感兴趣的师傅可以在网上自己去找一下
另外:强比较不仅比较值,还比较类型,0e会被当做字符串,所以不能用0e来进行
但是我们可以用MD值完全相同的字符来进行绕过,也可用md5的空值来绕过,如使用加密数组进行绕过
例题
[SWPUCTF 2021 新生赛]easy_md5
打开得到如上php代码,进行分析后发现是md5弱类型比较,只要满足:
就可以得到flag,这里规范了传参方式,要求name使用GET传参password使用POST传参这里使用hackbar进行传参
这里是由于两个md5值转换都为0e开头会转为0故会使其md5值相等,找到漏洞的得到flag
[BJDCTF 2020]easy_md5
打开后得到一个空白只有输入框的界面:
对其进行分析寻找在响应头中发现相应的线索:
这里发现其实是一个md5结合sql的绕过题目:即先MD5绕过再sql注入进入数据库中
百度看一下md5($pass,true)的相关知识:
string 必需: 要计算的字符串。
raw 可选: 默认不写为FALSE。32位16进制的字符串 TRUE。16位原始二进制格式的字符串 进行post传参
这是一个典型md5ffifdyop绕过,这个点的原理是 ffifdyop
这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是 ‘ or ‘而 Mysql 刚好又会把 hex 转成 ascii 解释,因此拼接之后的形式是select * from ‘admin’ where password=’’ or ‘6xxxxx’等价于 or 一个永真式,因此相当于万能密码,可以绕过md5()函数
绕过过程解释:首先我们要找到一个字符串,这个字符串经过md5得到的16位原始二进制的字符串能帮我们实现sql注入。首先or这个字符串是必要的,同时为了配对原先sql语句里面有的单引号,在or的两边要有单引号,使它变成 password=‘xxx’or‘xxx’ 的形式,所以我们需要的字符串的原始二进制格式的字符串里要包含 'or' ,如果根据原始二进制来找到我们要的字符串可能会比较麻烦,那么可以根据32位16进制的字符串来查找,根据上面介绍的, 'or' 对应的16进制是 276f7227 ,所以我们的目标就是要找一个字符串取32位16进制的md5值里带有276f7227这个字段的,接着就是要看关键的数字部分了,在276f7227这个字段后面紧跟一个数字,除了0,1-9,对应的asc码值是49-57,转化为16进制就是31-39,也就是我们需要有276f7227+(31-39)这个字段,就可以满足要求。比如xxxxxxxxxxxxxxxx276f7227(31-39)xxxxxx
得到:
查源码:
进行正常的md5 get数组绕过
pyload:
得到代码:
再次同上进行数组绕过:
pyload:param1[]=1¶m2[]=2
得到flag