最近,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 代码执行),这句话匹配到了
just test 中的test ,在进行替换的时候,将$_GET["h"]中的字符串当作了PHP代码执行。
进而,我们进入ThinkPHP的源码,下载2.1版(2.1以后已经被修复)。在/ThinikPHP/Lib/ThinkPHP/Util/Dispatcher.class.php的dispatch函数中,找到这句话:
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
只要是没有定义路由规则,默认情况下,这个函数都能执行。显然,这个使用了/e函数,会导致第二个参数当作函数使用。这句话的意思是
正则匹配的是 :字母开头,加上“/”分隔符,后面跟一个非"/"的元素,被替换成$var["分隔符前的字母"]=分隔符后的值;作者的本意是要将这么一对一对的
参数/值的形式的url写入到 $var[$key] = $value的数组中,第二个替换参数实际上是一个php语法,因此使用了/e参数。但是在赋值的时候,使用的是双引号。危险的地方实际上在这里,在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。可以先做如下测试:
echo "{${phpinfo()}}"; phpinfo会被成功执行了。
因此,我们将第二个变量替换成我们需要的函数,构造出来的url大致如下:
http://www.xxxx.com/index.php/Model/Action/par/${@print(THINK_VERSION)}
这个可以打印出ThinkPHP的版本,在页头的位置,我用2.1的实验了一下,成功了,这个相当于开启了一个PHP的一句话后门啊。这句话的/par/${@print(THINK_VERSION)}将匹配到正则表达式,将会执行第二个参数,具体语句是$var['par'] = "${@print(THINK_VERSION)}
"; 问题出在双引号上面,这个就会执行里面的语句,自然,想干什么就干什么了!
防范的方法就是将双引号改为单引号。
'$var[\'\\1\']='\\2';'
然后,我在谷歌上面搜索了下,受影响的站点不少,很多站点都能直接执行phpinfo()信息,熟悉tp的,应该也能轻易拿到数据库密码。那么拿到webshell甚至系统shell也不是没有可能的!
目前受影响的网站很多很多,只要找一个下使用了TP框架的网站,就能发现。所以,使用框架有好处,也有他的坏处,那就是漏洞都是公开的。