HECTF2020题解之ezphp
一 题目的来源
这道题的前身来源于NCTF2019的一道题名为“easyphp”,在HECTF中并没有完全复刻上述的题目,只是对其中的两个考点进行了参考,这篇文章也将对这其中的两个考点进行讲述。本文在在行文过程中同样参考了NCTF2019的官方wp,链接会放在文章最后。
二 题目描述
<?php
error_reporting(0);
highlight_file(__file__);
include('flag.php');
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
if($_GET['param1']!==$_GET['param2']&&md5($_GET['param1'])===md5($_GET['param2'])){
if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b){
echo $flag;
}
else {
die('you are close');
}
}
else {
die("md5 is wrong");
}
}
else {
die('str1 not number');
}
}
else {
die('you are wrong!');
}
?>
考点一,md5校验漏洞
PHP在进行哈希值的相关比较的时候,如果字符串以0e作为开头的话,PHP会忽略后面的字符,并将其解释成0,所以两个不同的字符串经过md5加密后所得到的字符串是0e开头的话,PHP会将其判断成相等字符串。
还有一种情况就是经典的数组方案了,如果往md5()中传入数组参数的话,函数会返回null,所以当两个传入字符均是数组的话,那么就会出现null=null的情况,两者被判定为相等。
下面是一些经过md5加密之后开头是0e的例子:
QNKCDZO
240610708//经典永流传
s878926199a
s155964671a
s214587387a
s214587387a
考点二,php弱类型
截取题中的两行来看:
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
显然,题目想让我们将两个字符串md5加密过后,使其不相等,等到将两个字符串中的“c”,“x”,“h”,“p”相应的替换成“0”,“1”,“2”,“3”之后再使其相等。从常识中我们知道md5加密过后的字符串是不会存在“x”,“h”,“p”这三种字符的。
所以,目标就转向了“c”与“0”的置换当中。从上面的分析过程我们我们可以得出一个结论,我们要找的一个关键的字符串它的开头应该是ce这样的话,程序的执行才能进入if中,至于另外一个字符串,咱们只需挑选一个开头是0e的字符串即可。
最后,求字符串脚本
import hashlib
def makemd5(s):
return hashlib.md5(s.encode('utf-8')).hexdigest()
s = '0123456789c'
for i in range(10000000000):
md5 = makemd5(str(i))
if md5[0:2] == 'ce':
if all(map(lambda x: x in s, md5[2:])):
print(str(i)+" "+md5)
break
运行脚本过后,我们会得到一个字符串“9427417”其加密过后是“ce156443c7c7c4c63366466c25317636”满足条件。随即构造payload:?param1=s878926199a¶m2=s155964671a&str1=9427417&str2=240610708
[1]: http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference
[2]: https://mermaidjs.github.io/
[3]: https://mermaidjs.github.io/
[4]: http://adrai.github.io/flowchart.js/