题目还是比较友好的,直接给出了源码,注释里还有提示,如下:
<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'flag';
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'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
//>=3,必须包含四种类型三种与三种以上
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
?>
稍微有点长……重点关注在哪里打印出来了flag,以及在这之前哪里会 exit(),然后着重去绕过那些会退出程序的地方就可以啦~
首先:
这里需要一个正则表达式匹配,其中 [: graph:] 表示任意一个可打印字符。也就是说,要求$password长度大于12即可;
在本地测试 '42aaaaaaaaaa' 可以绕过;
继续向下看:
这里要求$password中必须包含标点符号、数字、大写字母、小写字母等,并且被检测到6次以上才能绕过;
[:punct:] [:digit:] [:upper:] [:lower:]等正则表达式的说明见这篇博客:正则
这里说明一下preg_match_all函数的用法:preg_match_all是全局匹配,每匹配成功一次就加1,一直匹配到字符串结束,最后返回匹配成功的次数;
在本题中,只要匹配到一个标点符号、或者匹配到一个数字、或者一个大写字母、或者一个小写字母,即为匹配成功。
于是,我把payload改成了:'42aaAaa2;aaaa'
继续向下:
这里$c的值就是你的字符串种包含的类型的数目;也就是说,只要满足你的$password中包含标点符号、数字、大写字母、小写字母中三种及以上的类型,即可绕过;
看最后一个:
这里是一个弱类型比较,如果前两位是数字42,后面是字母等其它字符,比较时会强制转化成数字,于是,'42aaAaa2;aaaa' 完全可以用来绕过;
利用burpsuite发送POST请求: