web:
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){ $d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c))); if($d){ highlight_file('hint.php'); if(filter_var($url[1],FILTER_VALIDATE_URL)){ $host=parse_url($url[1]); print_r($host); if(preg_match('/ctfshow\.com$/',$host['host'])){ print_r(file_get_contents($url[1]));
我们只需要通过这三个if语句即可;
第一个if语句:
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0)
有一个判断它是否为数字的函数,这里提供两种绕过方法:
16进制绕过:
is_numeric() 会对「16进制」(0x开头)返回 true 。数值型和字符型都可以。
var\_dump(is\_numeric(0x7e)); var\_dump(is\_numeric('0x7e'));
上面的都会显示true;
科学计数法绕过:
is_numeric() 会对「科学计数法」(0e开头)返回 true 。数值型和字符型都可以。
并且,0e开头的值,强制转换成int类型后,都是1。
但是它的要求是a!=0但是a的平方等于0,直接逻辑错误。正常的数字是不可能满足的。看了一下XiLitter师傅的wp,这里有一个PHP的小知识:
在php语言运算中。小数点后超过161位做平方运算时会被截断,但是超过323位又会失效。 也就是说,小数点位数在这个范围内的,就会舍弃掉小数部分。
也就是当我们的小数点在161位至323位的时候,都会把小数点后面的视为0.
所以这一层的payload为:
?a=1e-200
采用科学计数法的同时,还让他为0的负200次。
第二个if语句:
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)))
这里,他提示了md5的碰撞,如果我们直接去碰撞的话,几乎是不可能的
这是他给出了提示,所以我们只需要遍历即可,这里我们书写我们的PHP脚本:
前面要加上0e,所以这一层的payload为:
b=0e652024452&c=0e603448399
$m == hash("md2", $m)
为什么会为真,即 $d 的值为 1,而当直接计算哈希值时,哈希值与原始字符串并不相同
。
原因在于 PHP 的字符串比较机制和特定哈希值的特性。$m 和 $n 是以 "0e" 开头的字符串,它们看起来像科学记数法的数字表示(例如 "0e652024452" 看起来像是 0 * 10^652024452)。当 PHP 比较这些字符串时,它们被解释为数字 0。这种情况称为“松散比较
”,在 PHP 中,字符串被解释为数字时,如果是 "0e..." 开头的字符串,就会被视为零。
因此,$m == hash("md2", $m) 的比较实际上是在比较两个零,这就解释了为什么结果为 1,即真。
第三个if语句:
if(filter_var($url[1],FILTER_VALIDATE_URL)){ $host=parse_url($url[1]); print_r($host); if(preg_match('/ctfshow\.com$/',$host['host']))
这里的漏洞在于filter_var这个函数,我们需要使用ssrf的来绕过。
这段代码的逻辑是在于,我要上传一个参数,这个参数必须满足是url的形式,并且需要他对返回的【host】部分是匹配的
/ctfshow\.com$/
,之后才可以读取a的参数。
所以,我们需要绕过filter_var()的函数,payload为:
1://example.com
但是同时需要对他进行正则匹配:
1://ctfshow.com
这里还有一个提示,所以,我们直接使用目录遍历的方法·来读取这个,最终payload:
url=b://ctfshow.com/../../../../../../../../fl0g.txt