Training: RegexMini (Training, Regex)
题目描述
We are trying hard to make a secure site.
So far, we got a secure definition of a username.
Unless you prove otherwise.Again you are given the source, also as highlighted version.
我们正在尝试努力打造一个安全的网站
到目前为止,我们得到一个用户名的安全定义
除非你能证明并非如此。
再次为你提供源码,同时也提供高亮版本
解
直接上源码
<?php
# Show src
if (isset($_GET['show']))
{
# http://en.wikipedia.org/wiki/Quine_%28computing%29
header('Content-Type: text/plain');
die(file_get_contents('index.php'));
}
# Header
chdir('../../../');
define('GWF_PAGE_TITLE', 'Training: RegexMini');
require_once('challenge/html_head.php');
if (false === ($chall = WC_Challenge::getByTitle(GWF_PAGE_TITLE))) {
$chall = WC_Challenge::dummyChallenge(GWF_PAGE_TITLE, 2, 'challenge/training/regex2/index.php', false);
}
$chall->showHeader();
# Info box
echo GWF_Box::box($chall->lang('info', array('index.php?show=source', 'index.php?highlight=christmas')), $chall->lang('title'));
# Show highlighted src
if (isset($_GET['highlight']))
{
$source = '[PHP title=regex2/index.php]'.file_get_contents('challenge/training/regex2/index.php').'[/PHP]';
echo GWF_Box::box(GWF_Message::display($source, true, false));
}
# Submitted?
if (isset($_POST['submit']))
{
# Check it!
$error = ludde_is_satisfied($chall);
# Oooops!
if ($error === true)
{
$chall->onChallengeSolved(GWF_Session::getUserID());
}
# All normal and ok
elseif ($error === false)
{
echo GWF_HTML::message(GWF_PAGE_TITLE, $chall->lang('msg_ok', array($_POST['username'])), false);
}
# Error!
else
{
echo GWF_HTML::error(GWF_PAGE_TITLE, $error, false);
}
}
# Check it!
function ludde_is_satisfied(WC_Challenge $chall)
{
# Missing POST var?
if (!isset($_POST['username']))
{
return $chall->lang('err_missing_var');
}
# Submitted a string?
if (!is_string($_POST['username']))
{
return $chall->lang('err_var_type');
}
# Valid username?
if (!preg_match('/^[a-zA-Z]{1,16}$/', $_POST['username']))
{
return $chall->lang('err_illegal_username', array(1, 16));
}
# WTF! WTF! WTF!
if (strlen($_POST['username']) > 16)
{
return true;
}
# Normal, OK and no error :)
return false;
}
?>
<div id="EUISM" class="box box_c">
<form id="Every User Input Seems Malicious" action="index.php" method="post">
<label for="username"><?php echo $chall->lang('username'); ?></label>: <input type="text" name="username" value="" size="16" />
<input type="submit" name="submit" value="<?php echo $chall->lang('submit'); ?>" />
</form>
</div>
<?php
# Copyright + Footer
echo $chall->copyrightFooter();
require_once('challenge/html_foot.php');
?>
代码不是很复杂直接看关键部分
# Submitted?
if (isset($_POST['submit']))
{
# Check it!
$error = ludde_is_satisfied($chall);
# Oooops!
if ($error === true)
{
$chall->onChallengeSolved(GWF_Session::getUserID());
}
# All normal and ok
elseif ($error === false)
{
echo GWF_HTML::message(GWF_PAGE_TITLE, $chall->lang('msg_ok', array($_POST['username'])), false);
}
# Error!
else
{
echo GWF_HTML::error(GWF_PAGE_TITLE, $error, false);
}
}
# Check it!
function ludde_is_satisfied(WC_Challenge $chall)
{
# Missing POST var?
if (!isset($_POST['username']))
{
return $chall->lang('err_missing_var');
}
# Submitted a string?
if (!is_string($_POST['username']))
{
return $chall->lang('err_var_type');
}
# Valid username?
if (!preg_match('/^[a-zA-Z]{1,16}$/', $_POST['username']))
{
return $chall->lang('err_illegal_username', array(1, 16));
}
# WTF! WTF! WTF!
if (strlen($_POST['username']) > 16)
{
return true;
}
# Normal, OK and no error :)
return false;
}
当我们提交用户名时首先就会执行ludde_is_satisfied()函数然后判断返回值是否是true,是就解决了。而想要ludde_is_satisfied()函数返回true必须满足4个条件,post提交的username不为空、username是字符串、preg_match('/^[a-zA-Z]{1,16}$/', $_POST['username'])
而且strlen(username) > 16
,前两个比较简单,关键就是后面两个,似乎如果preg_match()成立strlen()就不可能成立。
但是,preg_match()可以用特殊方法来绕过,网上一大堆这里就不细说了,这里使用换行符来进行绕过。直接上图
当然是不能直接输入换行的,在因特网上传送URL,只能采用ASCII字符集,只有字母和数字[0-9a-zA-Z]
、一些特殊符号$-_.+!\*'()
(不包括双引号)、以及某些保留字(空格转换为+
),才可以不经过编码直接用于URL,想要传入换行符应该使用URL编码后的字符,
$_POST[‘username’]应该传入aaaaaaaaaaaaaaaa%0a
才对。但是直接提交,并没有解决,直接抓包分析
可以看到传入的aaaaaaaaaaaaaaaa%0a
被编码成了aaaaaaaaaaaaaaaa%250a
,直接修改数据包编码后的username为aaaaaaaaaaaaaaaa%0a
即可。