昨天安全宝搞了一个情人节活动,闯关模式的,最后一关有一个代码审计的CTF。题目如下:
从代码上可以看出来,用户的输入需要先经过三个正则判断,最后符合$password=="42"
,才能拿到FLAG。
三个正则比较简单,就不细说了,分别是:
- 可见字符超过12个
- 字符串中,把连续的大写,小写,数字,符号作为一段,至少分六段,例如
a12SD+io8
可以分成a
12
SD
+
io
8
六段 - 大写,小写,数字,符号这四种类型至少要出现三种
符合这三个要求的字符串很容易构建,关键是最后还要使$password=="42"
。
要让一个字符串等于42
,我首先想到的是16进制。
试一下
var_dump("42"=="0x2A");
输出为bool(true)
。而且0x2A
已经符合了正则3,要让它符合正则1也很容易,在前面补0就可以了。例如0x000000002A
。但是正则2就不太好办了,在16进制前后加符号和小数点都会破坏在判断相等时进行的转换。
试了几次之后感觉这条路是走不通了,CTF不像开发中遇到的问题可以在Google上找答案,搜了几个关键词结果什么都没找到。只好找出PHP Manual,重新读一下里面的Comparison Operators一章,看看PHP在判断相等的时候会做什么转换。结果在第一个例子中就看到答案了。
var_dump("1" == "01"); // 1 == 1 -> true
var_dump("10" == "1e1"); // 10 == 10 -> true
var_dump(100 == "1e2"); // 100 == 100 -> true
在这题里既可以用4.2e1
也可以用420e-1
,不过第二种可以额外提供一个-
符号,在第二个里加上小数点,420.0e-1
,就满足正则2和正则3了。为了满足正则1,在里面再加几个零,就是我最后的答案:
420.00000e-1
最后顺便一说,看到有朋友试图使用ASCII转义来构建字符串,写的是\x34\x32\x2E
,在本地可以通过测试:
var_dump("\x34\x32\x2E"=="42");//bool(true)
但是放到服务器上可以通过三次正则判断,却无法通过最后一步的$password=="42"
,开始他以为是本地和服务器PHP版本不同导致的。但其实是因为通过POST提交的数据和在本地直接赋值的数据是不同的。
在PHP中,如果在双引号里放转义字符,在赋值的时候就会转义。例如
$a = "\x34\x32\x2E";
echo $a;//42.
echo strlen($a);//3
var_dump($a=="42");//bool(true)
而通过POST提交,浏览器会对转义字符做转换,服务器拿到的相当于在单引号里赋值的字符串(值为%5Cx34%5Cx32%5Cx2E
):
$b = '\x34\x32\x2E';
echo $b;//\x34\x32\x2E
echo strlen($b);//12
var_dump($b=="42")//bool(false);
可以看出来这道题目考的是使用==
运算符进行比较时发生的转换,而不是使用=
运算符赋值时发生的转换。
最后推荐两个网站:3v4l.org,可以使用各种版本的PHP为你运行脚本,下次认为是PHP版本问题的时候上去跑一下就可以验证了。CTF训练营,IDF实验室做的CTF在线解题网站,喜欢CTF的同学可以多刷刷题,组个队去参加真正的CTF比赛。
附文字版代码:
<?php
$flag = "THIS IS FLAG";
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
?>
\x34\x32\x2E 好像无法通过第一个正则的
我试了一下应该没问题。http://3v4l.org/JhJHk
http://3v4l.org/eLuX7
把代码中第3行的双引号换成单引号,才是POST过去的值
这也能看到你 🙂
(⊙ˍ⊙) 龚老师好
Pingback引用通告: PHP 那些“坑” R11; 企业网站欣赏,HTML5模板下载,JQUERY特效,网页素材下载
Pingback引用通告: PHP 那些“坑” R11; 企业网站欣赏,HTML5模板下载,JQUERY特效,网页素材下载
Pingback引用通告: PHP 那些“坑” R11; 企业网站欣赏,HTML5模板下载,JQUERY特效,网页素材下载
Pingback引用通告: PHP 那些“坑” - 奇奇问答
Pingback引用通告: PHP 那些“坑” | 程式前沿
Pingback引用通告: PHP 那些“坑” - 程序員的後花園
Pingback引用通告: PHP代码审计分段讲解[转载] | AnCoLin's Blog|影风博客
Pingback引用通告: PHP 那些“坑” - 算法网
Pingback引用通告: PHP 那些“坑” - 算法网
Pingback引用通告: PHP 那些“坑” _ 脚本宝典
Pingback引用通告: PHP 那些“坑” R11; FIXBBS
Pingback引用通告: PHP 那些“坑” R11; FIXBBS
Pingback引用通告: PHP 那些“坑” R11; FIXBBS
Pingback引用通告: PHP 那些“坑” R11; FIXBBS
Pingback引用通告: PHP 那些“坑” R11; FIXBBS
Pingback引用通告: PHP 那些“坑” R11; FIXBBS