#前言根据红日安全写的文章,学习PHP代码审计的第五节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完题目会用一道CTF的题目和实例来加深巩固。这是之前写的,有兴趣可以去看看:PHP代码审计01之in_array()函数缺陷PHP代码审计02之filter_var()函数缺陷PHP代码审计03之实例化任意对象漏洞PHP代码审计04之strpos函数使用不当#漏洞分析下面看题目,代码如下:题目漏洞是正则使用不严谨导致任意文件删除的漏洞,现在来具体分析,引起漏洞的地方在上面代码的21行,这里用到了preg_replace()函数,我们打开PHP手册来看看对这个函数的定义如下:了解了函数的用法,看上面代码,[^a-z.-_] 表示匹配除了 a 字符到 z 字符和. 字符到 _ 字符之间的所有字符,但是没有考虑到目录路径字符。这就直接可以任意删除文件,例如构造如下参数:
action=delete&data=../../config.php
将删除config.php文件。#CTF练习通过上面的讲解,来用一道CTF题目来练习一下,也是关于正则的问题,先看代码:
//index.php<?phpinclude \'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 | 字母和数字 || ————- | —— || 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(\'/^[[:graph:]]{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结果如下:#实例分析通过例题和CTF题目的讲解,是不是感觉棒棒的,现在咱们来分析实例吧,实例是LvyeCMS3.1,是基于ThinkPHP3.2.3框架。这个实例存在的漏洞也是函数使用不规范被绕过,导致任意文件删除。下面来具体分析:先查看入口文件index.php可以看到公共目录,应用目录等一些信息。接下来再看看目录结构:而漏洞在
Application/Template/Controller/StyleController.class.php
文件中,具体如下:看代码第117行,这里是获取目录路径,参数也是我们可以控制的,再向后看,用到了str_replace()函数,它是个字符串替换函数,具体说明如下:再这里起到的作用就是将\’..\\\’, \’../\’, \’./\’, \’.\\\’替换为空。但是这里是可以绕过的,如果我们输入…..///呢,会发生什么?是不是正好构造成了../,举个小例子会更清楚,如下:构造出../我们就可以穿越目录了,现在访问install.php文件会提示已安装,如下图:然后尝试删除lvyecms/Application/Install/目录下的 install.lock 文件,构造payload如下:http://www.xxx.com/index.php?g=Template&m=Style&a=delete&dir=…..///Application/Install/&file=install.lock现在访问install.php,发现确实删除了,如下图:#小结通过这篇文章的学习与讲解,是不是对PHP的正则了解的更多了呢,下一篇文章会对parse_str函数缺陷进行学习和讲解。一起加油吧!