PHP代码审计05之正则使用不当

根据红日安全写的文章,学习PHP代码审计的第五节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完题目会用一道CTF的题目和实例来加深巩固。这是之前写的,有兴趣可以去看看:
PHP代码审计01之in_array()函数缺陷
PHP代码审计02之filter_var()函数缺陷
PHP代码审计03之实例化任意对象漏洞
PHP代码审计04之strpos函数使用不当

漏洞分析

下面看题目,代码如下:

PHP代码审计05之正则使用不当


题目漏洞是正则使用不严谨导致任意文件删除的漏洞,现在来具体分析,引起漏洞的地方在上面代码的21行,这里用到了preg_replace()函数,我们打开PHP手册来看看对这个函数的定义如下:

PHP代码审计05之正则使用不当


了解了函数的用法,看上面代码,[^a-z.-_] 表示匹配除了 a 字符到 z 字符和. 字符到 _ 字符之间的所有字符,但是没有考虑到目录路径字符。这就直接可以任意删除文件,例如构造如下参数:

action=delete&data=../../config.php
将删除config.php文件。

CTF练习

通过上面的讲解,来用一道CTF题目来练习一下,也是关于正则的问题,先看代码:

//index.php <?php include 'flag.php'; 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;   } } highlight_file(__FILE__); ?> //flag.php <?php $flag = "HRCTF{Pr3g_R3plac3_1s_Int3r3sting}";?>

这道题目考察了是否熟悉PHP正则表达的字符类,大体是下面这个表格:

alnum 字母和数字 header
alpha 字母    
ascii 0 - 127的ascii字符    
blank 空格和水平制表符    
cntrl 控制字符    
digit 十进制数(same as \d)    
graph 打印字符, 不包括空格    
lower 小写字母    
print 打印字符,包含空格    
punct 打印字符, 不包括字母和数字    
space 空白字符 (比\s多垂直制表符)    
upper 大写字母    
word 单词字符(same as \w)    
xdigit 十六进制数字    

想要更加详细的了解,建议翻阅PHP手册,了解了字符类,下面来分析代码,上面一共三处正则表达,第一处如下:

if (0 >= preg_match('/{12,}$/', $password))
它表示的含义是匹配到可打印字符12往上包含12,^表示必须某类字符开头,$表示必须某类字符结尾。
第二处正则如下:
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
它表示的含义是,把连续的字符,数字,大写,小写作为一段,最少分成六段,比如Test+0He 会分为T est + 0 H e六段。
下面看第三处正则:
$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;
这里的含义是输入的字符必须包含字符,数字,大写,小写其中的三种往上。最后与42进行弱类型比较,都符合就输出flag,现在都解读清楚了,让咱们构造payload结果如下:

PHP代码审计05之正则使用不当

实例分析

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwwsf.html