一、ThinkPHP解读
1.1.web框架
众所周知,web上的框架有很多,这些框架的目的是为了让程序员快速地搭建起来一个web站点,这个称为框架,不论是python的Django框架还是JAVA的Spring框架,这些都是为了轻松的建起一个web站点而存在,而ThinkPHP便也是这样的一个框架,CVE-2018-20062这是该框架的另一款漏洞,主要以这个RCE为准。
1.2.ThinkPHP的基本知识点
ThinkPHP是啥,前一段文字已经详细的解释过了,想要进行该框架的复现,那么必须要知到该ThinkPHP的框架是怎么进行运行的,数据在各个文件夹中间是怎么进行传输的,用户进行输入参数是从哪里到数据库文件查询的,这些都是我们需要进行搞懂的,不然,复现将变得毫无意义,只是一味的粘贴被人的命令,这样不是复现,而是走马观花。下面进行ThinkPHP框架的简单了解。
但是ThinkPHP框架如何使用呢,怎么使用该框架进行一个web站点的快速搭建呢?这便是我们要解决的问题之一,TinkPHP时基于MVC框架生成的,MVC即模型(Model)是处理数据、视图(view)负责显示、控制器(Controller)负责业务逻辑,那么我们很清楚了,视图是渲染HTML页面的,控制器相当于后端的功能特性,用来一系列的业务逻辑和请求处理。还有就是路由与功能特性,该功能便是需要谁找谁要东西的一个思想。这便是ThinkPHP的大概处理思路。
二、环境搭建
2.1ThinkPHP框架的搭建
复现该环境有两种方法,一是使用自己进行手动本地搭建,另一种是使用docke进行拉取镜像从而来构建环境的复现,该文中使用本地进行下载来构建环境。在官方GitHub仓库上进行下载,版本为5.0.15版本,主要现在thinkphp和framework文件进行构建。自行在官方仓库下载。下载完成之后,把framework文件夹改为thinkphp,目的是为了使得文件找到thinkphp,来加载一些内置的框架。
2.2修改文件内容
在进行配置完成之后,使用Vscode或者其他的ide进行编辑,默认访问的为/public/index.php所以在进行访问时应该访问它,他是内置的公共访问页面。application\index\controller\index.php进行修改类的参数,目的是为了接收到我们GET传参到的username。修改配置文件如下所示:
php
<?php
namespace app\index\controller;
class Index
{
public function index()
{
// 1. 获取用户名参数
$username = request()->get('username/a');
// 2. 写入数据库
$res = db('users')->insert(['username' => $username]);
// 3. 打印结果
var_dump($res);
// 如果还需要显示原有的欢迎页面,可以保留这一行,否则可以删掉
return '正在处理注册...';
}
}
2.3数据库配置文件的修改
在/application/database.php中进行数据库文件的修改,从而使得数据库和Thinkphp框架进行相通,否则该框架只是框架,没有数据库的辅佐,根本没有数据的查询。在该文件中修改,使得用户名、数据库名、用户、端口和本地数据库进行一致。如下代码所示:
php
return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => '本地数据库',
// 用户名
'username' => '用户',
// 密码
'password' => '自己密码',
// 端口
'hostport' => '3306',
// 连接dsn
'dsn' => '',
2.4Thinkphp的config.php配置
在我们进行调试的时候,必须依照报错才能进行查看,所以,在/application/config.php中将app_debug和app_trace的false改为true。如下代码所示:
php
return [
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
// 应用调试模式
'app_debug' => true,
// 应用Trace
'app_trace' => true,
// 应用模式状态
'app_status' => '',
// 是否支持多模块
'app_multi_module' => true,
// 入口自动绑定模块
'auto_bind_module' => false,
// 注册的根命名空间
综上所述,我们的环境进行搭建完毕。进行访问即可。
三、具体复现
3.1命令使用
先进性指令输入,来查看我们的环境搭建是否完成。使用以下的命令查看,是否进行报出当前用户,假如是,那么恭喜你,环境搭建没有问题,假如不是,查看出错的相应位置。
php
http://127.0.0.1/test/public/?username[0]=inc&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=1
如下图3-1所示:

图3-1(环境搭建确认图)
3.2具体成因
如上所示,数据库的当前用户名进行报出,那么出现该漏洞的具体原因是啥,什么是导致该漏洞执行的具体原因,那么让我们来进行使用PHPdebug来逐步进行调试代码,看到数据在Thinkphp中如何进行传递。
在浏览器进行Get传参时,Thinkphp的框架做了这么几件事,在用户进行输入时,到web服务器,从web服务器到达think PHP的入口文件,然后会有框架的引导,从而创建request对象,再进到路由解析,假设有路由时,会按照路由进行解析,假如没有路由时,使用默认的解析为/模块/控制器/操作这样来进行解析。再解析完成之后,会进行控制器的执行,从而与数据库进行交互,最终将结果进行输出。
那么这样看似一个没有符合逻辑的思路,到底是哪里出了问题,导致的SQL注入,原因是我们进行构造的命令在经过Thinkphp的内置方法后,该过滤的方法对构造的命令不生效,直接进入this--\>builder的insert方法,这里的this-->builder方法继承了/think/db/builder/mysql中,但是在insert确实有parseData的方法和用户的数据$val进行拼接返回,即使我们的参数经过pareKey处理,但是没有任何作用。所以在这里绕过从而在数据库中直接拼接查询。整体的思路图3-2如下所示:

3-2(整体思路图)
3.3debug的详细调试
使用debug进行代码跟随,进行参数跟随,传入的参数会被分割数组,从数组进行传入,而inc、updatexml(1,concat(0x7e,user(),0x7e),1)、1分别是我们传入的参数,经过函数被分割成数组进行传递。参数可以看到参数进来了,如下图3-3所示:
图3-3(参数跟随图)
随着debug的调试,结果发现inc,那么会被当做一中条件,从而来进行传入我们的恶意参数,如下图3-4所示:

图3-4(恶意代码拼接)
命令进入之后,使用debug继续调试,进到builder.php中对该数据进行过滤,进行判断是否为空,如下图3-5所示:

图3-5(过滤判断)
进行查看,key和values,的传参,具体参数如下图3-6所示:

图3-6(key和values的值)
在sql语句中传参,具体的查询如下图所示,进行拼接之后的代码如下图3-7所示:

图3-7(查看插入语句)
那么这样的语句是否在数据库中报错,能否报出我们需要的东西,我们使用本地数据库进行模拟,来查看是否报错,如下图3-8所示:

图3-8(检验语句图)
如上图所示,那么在数据库中进行查询能够查询到数据,则在网页使用get报错也能得到我们数据。,使用其他的查询语句从而来获得我们需要的数据库的信息。
Think PHP的框架漏洞到此结束,假如有问题,评论区见!!!