Training: RegexMini (Training, Regex)

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>:&nbsp;<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()可以用特殊方法来绕过,网上一大堆这里就不细说了,这里使用换行符来进行绕过。直接上图
Training: RegexMini (Training, Regex)
当然是不能直接输入换行的,在因特网上传送URL,只能采用ASCII字符集,只有字母和数字[0-9a-zA-Z]、一些特殊符号$-_.+!\*'()(不包括双引号)、以及某些保留字(空格转换为+),才可以不经过编码直接用于URL,想要传入换行符应该使用URL编码后的字符,
Training: RegexMini (Training, Regex)
$_POST[‘username’]应该传入aaaaaaaaaaaaaaaa%0a才对。但是直接提交,并没有解决,直接抓包分析
Training: RegexMini (Training, Regex)
可以看到传入的aaaaaaaaaaaaaaaa%0a被编码成了aaaaaaaaaaaaaaaa%250a,直接修改数据包编码后的username为aaaaaaaaaaaaaaaa%0a即可。

上一篇:ES2018 新特征之:正则表达式反向(lookbehind)断言


下一篇:正则表达式