0x01 前言
前天在公众号看到石大神发的一篇审计thinkphp的文章,就想写一个分析流程,delay到了今天。昨天在先知也看到了chybeta发的一篇分析文章感觉也不错。分析过程,我也会做thinkphp部分功能的解析。 废话不多说,开始吧!
0x02 环境搭建和漏洞复现
程序下载地址:http://www.thinkphp.cn/down/1126.html PHPstudy:Apache+php5.6+MySQL 工具:PHPstorm
- 首先建立一个数据库名为:thinkphp
- 建立一个表名为:user
- 添加三个字段:id,name,password
- 在thinkphp的数据库文件填上刚才我们建立的数据库信息:
文件位置:
\thinkphp\application\database.php
- 打开thinkphp的调试模式:
文件位置:
\thinkphp\application\config.php
- 简单写一个update功能,石大神用到了模型,这里就简单写一个例子就行了。
文件位置:
\thinkphp\application\index\controller\Index.php
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$password = input('get.password/a');
db('user')->where(['id'=> 1])->update(['password'=>$password]);
}
}
这里用到了thinkphp的助手函数input(),是专用来接收get,post等的值。具体可以看:https://www.kancloud.cn/manual/thinkphp5/118044
还有就是thinkphp的数据库操作,框架本身写好了我们调用就比较方便。所以为什么那么多人用框架去开发程序,快捷而且安全,不过也会有安全问题,就像今天这个sql漏洞,不过如果是新手的话总比自己写的好对吧哈。 具体可以看链接:https://www.kancloud.cn/manual/thinkphp5/135178 7. 现在就可以访问我们的payload了:
http://thinkphp.test/thinkphp/public/index.php?password[0]=inc&password[1]=updatexml(2,concat(0x7e,user()),0)&password[2]=1
0x03 漏洞分析
- 这里用phpstorm+debug来动态分析,有不懂配置的可以访问我写一篇配置文章:利用phpstorm+xdebug进行断点调试 我们在主函数下一断点:
然后访问我们payload,它会跳到入口文件,我们只要分析的是sql执行的地方,所以我们直接F8跳到执行sql地方:
2. 我们继续跟进到loader.php
它会包含thinkphp的Db.php文件,
接下还会包\thinkphp\library\think\db\connector\Mysql.php
文件,主要是连接数据库的操作,这里就直接跳过了。别分析分析把自己绕进去了,我只是在这里讲诉下过程,我们还是直接分析sql执行的部分吧。
3. 我们跳到update执行的部分,文件位置:\thinkphp\library\think\db\Query.php
继续往下看,这句是执行我们sql的地方:
4. 我们F7跟进去,跳到文件位置\thinkphp\library\think\db\Builder.php
parseTable
函数直接F8往下执行了,这函数是处理table分析的,主要还是parseData函数
,我们继续F7跟进
5. 我们继续往下跟进
我们看到了这里如果传入的值为数组形式的话,并且第一个参数为inc
就执行switch所对应的的语句。
可以看到这里函数对我们传入的值没有做任何处理,返回内容仍然是我们的语句:
跟到后面返回的执行sql语句:
执行完,我们跟进到报错的地方,说明我的语句执行成功:
6.到这里就差不多结束了,有人问,为什么要这样给数组三个字段?
我们可以看到我们刚才传入数组的地方,分别有三个数组
到后面返回$result
的时候就组合在一起了:
下面是parseData
的代码:
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;
}
0x04 结束
如果看不懂的,可以先去了解一下thinkphp这个框架,其他框架大同小异。如果里面一些PHP代码看不懂的话,可以去复习下PHP。
0x05 参考
https://www.kancloud.cn/manual/thinkphp5/
- 原文作者: F0rmat
- 原文链接: https://xxe.icu/thinkphp-framework-_-5.0.16-sql-injection-vulnerability-analysis.html
- 版权声明:本作品采用 署名 - 非商业性使用 4.0 国际 (CC BY-NC 4.0)进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。