PHP CTF tricks parse_str and loose comparison

0x00 题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<meta charset="utf-8">
<?php
error_reporting(0);
if (empty($_GET['str'])) {
show_source(__FILE__);
die();
}else{
include('flag.php');
$url = "www.hackfun.org";
$str = $_GET['str'];
@parse_str($str);
if ($url[0] != 'QNKCDZO' && md5($url[0]) == md5('QNKCDZO')) {
echo $flag;
}else{
exit('Try angain :-)');
}
}
?>

0x01 解题

要得到flag首先要使if ($url[0] != 'QNKCDZO' && md5($url[0]) == md5('QNKCDZO'))成立,也就是$url[0] != 'QNKCDZO'md5($url[0]) == md5('QNKCDZO')都成立,如果str正常传入情况下,$url[0]的值是’w’,满足第一个条件$url[0] != 'QNKCDZO',但是不满足md5($url[0]) == md5('QNKCDZO'),但是代码中写了@parse_str($str);,而parse_str()函数会把参数字符串当做php变量解析,也就是如果str传入的是url[0]=hello,那么就会解析成一个数组url,且数组url的第一个值为hello,此时$url[0] != 'QNKCDZO'成立,md5($url[0]) == md5('QNKCDZO')不成立,但是至少$url[0]的值可控了,仔细研究md5($url[0]) == md5('QNKCDZO'),可以发现md5('QNKCDZO')的值为字符型的’0e830400451993494058024219903391’,但是根据PHP手册的描述:如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。其中0e是科学计数法,因为涉及到数字内容,所以就会转换为数值,而’0e830400451993494058024219903391’转换为数值也就是0*(10^830400451993494058024219903391) = 0,所以我们只需要使url[0]的MD5值为类似0e开头后面全为数字的字符串就可以让md5($url[0]) == md5('QNKCDZO')成立,通过搜索发现很多字符串都满足条件,具体满足条件的列表可看。

我们选取240610708作为url[0]的值,也就是使str传入的值为url[0]=240610708,此时$url[0] != 'QNKCDZO'成立,且md5($url[0]) == md5('QNKCDZO')也成立:

flag