首先还是介绍一下关于md5的基本信息:
MD5(Message Digest Algorithm 5)是一种常用的哈希函数,用于产生128位(16字节)的哈希值,通常以32个十六进制数字表示。MD5广泛用于计算文件或文本数据的校验和,以验证数据的完整性或唯一性。它由Ronald Rivest设计于1991年,是MD4算法的改进版本。
MD5的特点包括:
-
固定长度输出:MD5生成的哈希值长度固定为128位,无论输入数据的大小。
-
快速计算:MD5的计算速度相对较快,适用于对大量数据进行哈希运算。
-
不可逆性:MD5是一种单向哈希函数,即从哈希值无法还原出原始数据。
-
碰撞概率:尽管MD5具有广泛的应用,但由于其算法设计存在漏洞,导致在一些情况下可能出现碰撞(即不同的输入数据产生相同的哈希值)。这种碰撞风险降低了MD5在一些安全性要求较高的场景中的适用性。
尽管MD5曾经被广泛应用于数据校验、密码存储等领域,但随着计算技术的发展和MD5算法的漏洞被发现,其安全性逐渐受到质疑。因此,在对安全性要求较高的应用中,建议使用更安全的哈希算法,如SHA-256等。
然后本文,会对md5,强弱比较,碰撞,做出个人的总结。
首先,是强弱比较:
强比较:使用三个 ''==='' 比较,值和类型都比较
弱比较:使用两个 ''=='' 比较,只比较值,不比较类型
举个例子:
a==b 将a,b的值转换成同类型再比较值
a===b 先判断a,b类型,若相同,则比较值,若不相同,则返回false
以ctf的角度来说,主要的问题,还是去绕过这些比较,然后比较常见的方法有两种:一种是0e绕过,一种是数组绕过
1.0e绕过原理:(一般只用于弱比较)
第一点,
科学记数法是一种记数的方法。
计算器表达10的幂一般是用E或e
如:2 760 000 = 2.76×10^6 = 2.76e6
所以0e,无论后面跟什么值,都是0
其次,PHP在处理字符串时会出现缺陷,如果字符串为’5e2’,本来只是一个正常字符串,但PHP会认为这是科学计数法里的e,那么PHP进行比较时会将这个字符串按照科学计数法计算,即5e2=5*10^2=500,因此0e100被认为和0相等。md5加密后的哈希值是一串16进制数,因此需要哈希值第一位为0,第二位为e即可,后面不论是什么都认为和0相等。
然后这里是一些常见的0e绕过可能会用到的值:(原值在上,md5值在下)
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
这种类型呢,可能还会有md5多次加密的题目,因为大佬写的很好,所以这里不再过多赘述,这里是大佬的博客
2.数组绕过原理:(强弱比较通吃)
无论是PHP弱比较还是强比较,md5()函数无法处理数组,如果传入的是数组,会返回NULL,两个数组经过加密后返回值均为NULL,形成相等,虽然会报错,但是null=null,逻辑关系为True。所以可以输出flag
比如传入md5(a[]=1)==md5(b[]=2),实际上是null==null,所以数组进行md5弱比较时,结果相等
这个的payload构造就相对简单很多了,如下(以get传参为例)
payload:?a[]=1&b[]=2
然后,还有一种强比较的绕过方法:
md5值完全相同的字符绕过:
array1=%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%a2array2=%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%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
还有一个
3.万能通式ffifdyop 绕过
实例:
select * from 'admin' where password=md5($pass,true)
输入md5('ffifdyop',true)绕过
即
select * from 'admin' where password=md5 ( ' ffifdyop ' ,true)
md5(string,raw)
绕过原理:
ffifdyop 这个字符串被 md5 哈希了后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是' or '6
而 Mysql 刚好又会把 hex 转成 ascii 解释,因此拼接之后的形式是 select * from 'admin' where password= ' ' or '6xxxxx ',等价于 or 一个永真式,因此相当于万能密码,可以绕过md5()函数。
where password=正确的密码or 1 ,代表永真,那么前面有没有密码都无所谓了
简单说,
select * from admin where password= ' ' or'6<乱码>'
就相当于
select * from admin where password= ' ' or 1 实现sql注入
当两个条件中有任一个条件满足,“逻辑或”的运算结果就为“真”
逻辑与”相当于生活中说的“并且”,就是两个条件都同时成立的情况下“逻辑与”的运算结果才为“真”。
4.md5碰撞
简单来讲,就是基于MD5的工作原理,得出以下属性的MD5算法:给定两个输入M和N,如果MD5(M) = MD5(N),也就是说,MD5哈希的M和N是相同的,那么对于任何输入T,MD5(M || T)= MD5(N || T),其中||表示连接。也就是说,如果输入M和N具有相同的散列值,那么向它们添加相同的后缀T将得到两个具有相同散列值的输出。这个属性不仅适用于MD5哈希算法,也适用于许多其他哈希算法。
因此,可以给生成的两个不同的文件加上相同的后缀,导致的结果就是一个文件的两数组内容相同,另一个则不同,但两文件的MD5哈希值依然相同。
关于md5碰撞的实验解释,也可以看一下,这个大佬的博客。
然后呢,是一道我觉得比较经典的md5题目实战:
[BJDCTF 2020]easy_md5
进来以后,就只有一个输入框,查看源代码,也并没有发现可疑信息
这里使用bp抓包,看看有没有哪些有限信息,在重放器界面,发现有一个hint
发现和我写到的第三个知识点有关联,尝试注入,成功进入了下一个界面、
看源码
注释中有提示, 解释一下这段代码:
-
$a = $GET['a'];
:这一行尝试从GET请求的参数中获取名为'a'的值,并将其赋给变量a。但是,代码中有一个错误,应该是`_GET而不是
GET`,因此应该是`a = $_GET['a'];`。 -
$b = $_GET['b'];
:类似地,这一行尝试从GET请求的参数中获取名为'b'的值,并将其赋给变量$b。 -
if($a != $b && md5($a) == md5($b)){
:这是一个条件语句,如果a不等于b,并且它们的MD5哈希值相等,则执行条件块内的代码。这里的逻辑是要确保两个变量的值不相等,但它们的MD5哈希值相等。 -
header('Location: levell14.php');
:如果条件满足,即a不等于b但它们的MD5哈希值相等,那么就会将用户重定向到levell14.php页面。
简单来说,弱比较
两种方法,数组绕过
0e绕过
可以进入到下一个界面,根据这个界面,我们可以获得一些代码的信息
是关于强比较
以下是对代码的解读
-
error_reporting(0);
:设置错误报告级别为0,即关闭PHP的错误报告。这样做可能是为了隐藏任何潜在的错误消息,增加代码的安全性。(对于信息获取没什么用) -
include "flag.php";
:包含了一个名为flag.php的文件。根据文件名,这个文件可能包含了一些敏感信息,比如一个或多个标志(flag)。 -
highlight_file(__FILE__);
:这行代码使用PHP的内置函数highlight_file()来显示当前文件的源代码。这可能是为了方便开发者查看当前脚本的代码。 -
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
:这是一个条件语句,如果参数param1不等于参数param2,且它们的MD5哈希值相等,就执行条件块内的代码。(三个等号,强比较标志) -
echo $flag;
:如果条件满足,即参数param1不等于参数param2但它们的MD5哈希值相等,那么就会输出变量$flag的值。这意味着如果条件成立,就会泄露flag.php文件中的敏感信息。(flag的输出)
数组绕过 :
成功获得了flag
然后呢,是知识点和现实攻防的关联,也是我找了一些材料,这里作为补充:
强比较的应用:
-
用户认证系统:
- 在用户认证系统中,当用户登录时,通常会将用户提供的密码与数据库中存储的哈希值进行比较。使用强比较确保即使用户提供的密码与数据库中存储的密码不同,但其哈希值相同,也不会导致认证通过,从而提高了系统的安全性。
-
文件完整性校验:
- 在软件发布或文件传输过程中,可以使用MD5哈希值来验证文件的完整性。通过强比较,可以确保即使文件内容不同,但其哈希值相同,也不会导致错误的通过验证,从而保证文件的完整性。
弱比较的应用:
-
简单的数据验证:
- 在一些简单的场景中,比如表单提交后端验证,可能会使用MD5哈希值来比较两个值是否相等,而不考虑其安全性。例如,验证用户输入的验证码是否正确,可以比较其MD5哈希值,这样可以避免直接暴露原始验证码。
-
故障排除和调试:
- 在开发和调试过程中,可能会使用MD5哈希值来比较文件或数据的内容,以便检查是否有更改或损坏。在这种情况下,弱比较可能足够满足需求,因为安全性不是主要关注点。
需要注意的是,虽然弱比较在某些情况下可能是合适的,但在安全关键的场景下应避免使用MD5进行比较,因为MD5存在碰撞的风险。在这种情况下,应选择更安全的哈希算法,如SHA-256。
举个例子:
强比较:
假设一个网站使用MD5来存储用户密码。当用户注册时,网站会将用户提供的密码经过MD5哈希后存储在数据库中。
- 用户Alice注册,她的密码是"password123",网站将其哈希为MD5值:"482c811da5d5b4bc6d497ffa98491e38",然后存储在数据库中。
- 用户Bob注册,他的密码也是"password123",网站同样将其哈希为MD5值:"482c811da5d5b4bc6d497ffa98491e38",并且存储在数据库中。
在这个场景中,MD5的强比较指的是即使两个用户使用相同的密码,它们在数据库中存储的哈希值也是相同的。这意味着即使密码相同,实际存储的是其MD5哈希值,不会直接暴露用户的原始密码。但是,这也增加了碰撞的可能性,因为如果两个用户使用相同的密码,它们的哈希值也会相同,这可能导致安全性问题。
弱比较
假设一个软件发布公司发布了一个软件,并提供了软件文件的MD5哈希值,以供用户下载后验证文件的完整性。
- 公司发布软件的文件的MD5哈希值为:"a3b4c5d6e7f8g9h0i1j2k3l4m5n6o7p8"
- 用户下载软件文件后,计算其MD5哈希值为:"a3b4c5d6e7f8g9h0i1j2k3l4m5n6o7p8"。
在这个场景中,MD5的弱比较指的是用户通过比较两个MD5哈希值来验证文件的完整性。虽然MD5存在碰撞的风险,但在这种情况下,攻击者要找到另一个文件,使其MD5哈希值与原始文件相同,并且实际相同的哈希值对应于原始文件的概率较低。因此,MD5在这种用途下仍然可以提供基本的完整性校验。
以上就是个人对MD5的理解,纯小白,如有错误,欢迎指正