Thinkphp3全漏洞分析

这里给大家推荐两个phpdebug的docker容器,也是我一直在用的,不用配置很复杂的环境,即开即用:

Thinkphp3全漏洞分析_第1张图片

框架介绍

基本信息
ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,遵循Apache 2开源协议发布,使用面向对象的开发结构和MVC模式,融合了Struts的思想和TagLib(标签库)、RoR的ORM映射和ActiveRecord模式。ThinkPHP可以支持windows/Unix/Linux等服务器环境,正式版需要PHP 5.0以上版本,支持MySql、PgSQL、Sqlite多种数据库以及PDO扩展。

Thinkphp 2.x、3.0-3.1版代码执行漏洞分析

此漏洞为tp框架中Lite精简模式中存在可能的漏洞,ThinkPHP 开启lite模式后,会加载Lite下的Dispacher.class.php文件去匹配URL并分发用户请求, 而在该文件中的一处使用了perg_replace函数的 /e参数,

Thinkphp3全漏洞分析_第2张图片
此函数/e模式会将其的第二个参数当作php代码执行,而在此文件中一二参数都为可控,\1与\2分别代表着第一个参数中的两个括号的位置,例如当我们传访问的url为index.php?s=1/2/3/4/5/6时,在第一次进行匹配是\1即代表1,\2即代表2,在第二次匹配时\1即代表3,\2即代表4。
本地复现:
Thinkphp3全漏洞分析_第3张图片
payload:
?s=/1/2/3/${eval($_GET[1])}&1=system(%22whoami%22);

要将我们的payload写到偶数位也就是\2处,原因是\2处为函数的第二个参数处且被双引号包裹,可以解析变量

thinkphp3.2.3 SQL注入漏洞-where分析

记得安装mysql_pdo.so。不然连接数据库会报错的

tp3中的常用方法:

A快速实例化Action类库
B执行行为类
C配置参数存取方法
D快速实例化Model类库
F快速简单文本数据存取方法
L 语言参数存取方法
M快速高性能实例化模型
R快速远程调用Action类方法
S快速缓存存取方法
U URL动态生成和重定向方法
W 快速Widget输出方法
D函数实例化的是你当前项目的Lib/Model下面的模块。如果该模块不存在的话,直接返回实例化Model的对象(意义就与M()函数相同)。而M只返回,实例化Model的对象。它的$name参数作为数据库的表名来处理对数据库的操作。
I方法是ThinkPHP众多单字母函数中的新成员,其命名来自于英文Input(输入),主要用于更加方便和安全的获取系统输入变量,可以用于任何地方
D方法和M方法,这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会自动调用M方法。通俗一点说:M实例化参数是数据库的表名。D实例化的是你自己在Model文件夹下面建立的模型文件

写个控制器:


namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
        $Dao=M('users');
        $result=$Dao->find(1);
        if ($result){
            echo"connect success";
        }
        var_dump($Dao->find(I('GET.id')));

    }
}

访问下,我们输入的是id=1’,但是还是对数据库进行了正常查询,我们先简单跟下:
Thinkphp3全漏洞分析_第4张图片
Thinkphp3全漏洞分析_第5张图片

进I方法里面看一眼,这个获取我们get传进去的值:

Thinkphp3全漏洞分析_第6张图片
为我们挑选一个过滤器,因为我们一开始对I方法,并没有传入filter,所以我们这里filter的值是null,所以派一个默认的filter给我们:

Thinkphp3全漏洞分析_第7张图片

C函数我没跟进去看,直接看的idea的返回结果,这里给的filter是:htmlspecialchars

Thinkphp3全漏洞分析_第8张图片

但是这玩意貌似不过滤单引号:
Thinkphp3全漏洞分析_第9张图片

在下面进行过滤:

Thinkphp3全漏洞分析_第10张图片
在往下走,走到后面,会回调think_filter这个函数进行过滤:
Thinkphp3全漏洞分析_第11张图片

瞅一眼过滤了啥,这正则对我们当前payload的作用几乎为。。。:
Thinkphp3全漏洞分析_第12张图片

回到过去,最后返回的还是1’ ,没过滤掉根本:

Thinkphp3全漏洞分析_第13张图片
接着进了find方法,这里给options赋值,赋成了二维数组:
Thinkphp3全漏洞分析_第14张图片
我这里重新换了个pyload:1’and%201=1%23,打个断点直接到这:
Thinkphp3全漏洞分析_第15张图片
接着往下走,会有些对id参数是否是数组的判断,我们这里都进不去。就往下跟我们发现在这下面这个地方,我们的1’and%201=1%23变成了1:
Thinkphp3全漏洞分析_第16张图片
对这个函数打个断点,进去瞅下发现有个parseType函数:
Thinkphp3全漏洞分析_第17张图片

跟进去看看,这个函数是判断了我们数据库里的这个字段的类型,然后进行相应的处理,我这里用的是id参数,是int型,所以他直接用intval做了个强转,我们的1’and%201=1%23就是在这里变成了1:
Thinkphp3全漏洞分析_第18张图片
我们进数据库把id类型改成string,然后在断点定位到这,跳过了这个parseType函数,接着走到了查询的地方,跟进下:
Thinkphp3全漏洞分析_第19张图片
发现了在buildSelectSql处单引号被转意:

Thinkphp3全漏洞分析_第20张图片
打个断点跟进buildSelectSql处:
Thinkphp3全漏洞分析_第21张图片

跟进parsesql函数:
Thinkphp3全漏洞分析_第22张图片
跟进去where那个:

Thinkphp3全漏洞分析_第23张图片
在跟进去这个解析相关的函数:
Thinkphp3全漏洞分析_第24张图片
在跟下这个parsevalue:
Thinkphp3全漏洞分析_第25张图片
第一个if通过了,这里执行了这个escapeString,跟进一下这个escapeString函数,可以看到就是在这里对我们的单引号进行了转意:
Thinkphp3全漏洞分析_第26张图片

数字,字符都不行,只能尝试数组,看看在有些条件判断处能否进行一个绕过

payload1

我们传入的参数,是在_parseType()中被强转为int,如果我传?id[where]=1 and 1=1时根本不会进入这个if,更不用说进入强转的那个函数:

Thinkphp3全漏洞分析_第27张图片
我们不传数组时的,到这的options中的where是个数组,所以会进到那个强转的函数:
Thinkphp3全漏洞分析_第28张图片

再往下走,因为这回$where变成了字符串,所以在parseWhere这里直接拼接成了语句,依次返回执行:
Thinkphp3全漏洞分析_第29张图片
Thinkphp3全漏洞分析_第30张图片

Thinkphp3全漏洞分析_第31张图片
结果:
Thinkphp3全漏洞分析_第32张图片

thinkphp3.2.3 SQL注入漏洞-exp注入分析

payload:?name[0]=exp&name[1]==1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)
控制器:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index()
    {
        $User = D('Users');
        $map = array('name' => $_GET['name']);
        $user = $User->where($map)->find();
        var_dump($user);
    }

}

这里的写法可以类比一下where注入的那个控制器,where注入的传参方式是I(‘GET.id’),而这里是 $map = array(‘name’ => $_GET[‘name’])与where($map)结合,这个原因放到最后说,这可以打个断点,看看where函数主要做了什么:

Thinkphp3全漏洞分析_第33张图片

主要在where函数里就做了个赋值操作,主要是赋值以后的find操作,我们跟进去看看,跟进以后的大多数过程都和之前比较类似,直到这里parseSql里的parseWhere方法:
Thinkphp3全漏洞分析_第34张图片

跟进去parseWherer以后,主要是parseWhereItem方法,跟进去看看

Thinkphp3全漏洞分析_第35张图片

首先会对$exp进行赋值,赋的值就是我们传入的 name[0]=exp
Thinkphp3全漏洞分析_第36张图片
之后会进行一个判断,拼接这一步是比较重要的:
Thinkphp3全漏洞分析_第37张图片
拼接以后会逐步递归返回,最后进行执行:
Thinkphp3全漏洞分析_第38张图片
Thinkphp3全漏洞分析_第39张图片

再说一下,这里为什么不能用I(‘GET.id’)进行传参,在我们调试where注入的时候,用I(‘GET.id’)进行传参时,会有一个think_filter进行过滤如下图,正则里写了"exp“:

Thinkphp3全漏洞分析_第40张图片
结果:

Thinkphp3全漏洞分析_第41张图片

thinkphp3.2.3 SQL注入漏洞-bind注入分析

控制器:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index()
    {
        $User = M("users");
        $user['id'] = I('id');
        $data['PWD'] = I('PWD');
        $valu = $User->where($user)->save($data);
        var_dump($valu);
    }
}

payload : id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&PWD=
这里的id[0]=bind可以参考exp注入中的name[0]=exp,主要是为了进到那个if判断:

Thinkphp3全漏洞分析_第42张图片
而我们这里对比exp注入,为什么要加一个id[1]=0呢,我们先输入id[0]=bind&id[1]=abc&PWD= 试试:

Thinkphp3全漏洞分析_第43张图片

主要进入的是update方法,并且bind注入会产生绑定参数。就是假如我们输入的payload是id[0]=bind&id[1]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)&PWD=,那么最后没进行替换之前编译出的语句是:UPDATE users SET PWD =:0 WHERE id = :1 and updatexml(1,concat(0x7e,user(),0x7e),1),如果payload是id[0]=bind&id[1]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)&PWD=那么最后没进行替换之前编译出的语句是:UPDATE users SET PWD =:0 WHERE id = :0 and updatexml(1,concat(0x7e,user(),0x7e),1).

这是在这里完成的:

Thinkphp3全漏洞分析_第44张图片

在接着用id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&PWD=这个payload进行bind注入调试,可以看到我们最终拼接的没有进行替换的语句是这样的:

UPDATE `users` SET `PWD`=:0 WHERE `id` = :0 and updatexml(1,concat(0x7e,user(),0x7e),1)

Thinkphp3全漏洞分析_第45张图片
但是我们最终执行的时候,在execute函数里的strtr函数是会对:0进行参数替换的:
Thinkphp3全漏洞分析_第46张图片

Thinkphp3全漏洞分析_第47张图片
Thinkphp3全漏洞分析_第48张图片
上面红框圈出来的大概的意思是,对我们$this->bind中的数组的每个键的值先加上个单引号,然后再进行替换,可以看看strtr函数的作用:

Thinkphp3全漏洞分析_第49张图片
这里的$this->bind是我们可以控制的,所以我们要我们的payload中构造一个有 “:0” => “xxxxx” 的这样的一个键值对,来达到对他的编译出的语句中的 :0进行替换的一个目的,我们要id[1]=0 and xxx就是为了要他先编译出的语句中含有:0不含有 :1、 :2、 :3 、:u 、:e 、:a这种其他字符,这就是我们的目的,在$this->bind中产生这样一个值的这步是在这里完成的,并且$val参数的值,就是我们PWD传入的值:

Thinkphp3全漏洞分析_第50张图片

Thinkphp3全漏洞分析_第51张图片
因为我这里的PWD输入的为空,所以经过bindParam后$this中是这样的结果:

Thinkphp3全漏洞分析_第52张图片

最后在update方法中的execute进行先替换后执行:

替换:
Thinkphp3全漏洞分析_第53张图片

执行:

Thinkphp3全漏洞分析_第54张图片

你可能感兴趣的:(php,安全,web安全,thinkphp)