0x01 前言
Thinkphp 3.2.x用的也挺多的,以前的程序大部分都是用这边版本的,如果移动到2版本又挺麻烦,而且小程序不说,大程序就复杂很多了。
这次的漏洞是出现在WHERE这这个地方。
0x02 环境搭建和漏洞复现
程序下载地址:http://www.thinkphp.cn/donate/download/id/610.html PHPstudy:Apache+php7.1+MySQL 工具:PHPstorm
- 首先建立一个数据库名为:thinkphp
- 建立一个表名为:user
- 添加两个字段:name,pass
-
thinkphp3.2版本和之前的5版本略有不同,它的数据库信息文件是在 这个文件:
thinkphp/ThinkPHP/Conf/convention.php
在这里面填上我们刚才建立的数据库信息: -
打开thinkphp的调试模式: 在刚才的文件:
thinkphp/ThinkPHP/Conf/convention.php
然后修改为true:'SHOW_ERROR_MSG' => true, // 显示错误信息
-
这个3.2版本的控制器位置又是和5版本有点区别。具体的可以在本文的参考链接详细查看thinkphp3.2版本的开发手册。 文件位置:
thinkphp/Application/Home/Controller/IndexController.class.php
在里面写一个简单的update的例子:
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller
{
public function index()
{
$tj['name'] = I('name');
$data['pass'] = '111111';
$res = M("user")->where($tj)->save($data);
}
}
I函数和5版本的input助手函数差不多,具体可以看:http://document.thinkphp.cn/manual_3_2.html#input_var
因为我们仅仅执行基本的CURD操作,所以用M方法来实例化数据库对象就行了。
实例化模型可以看:http://document.thinkphp.cn/manual_3_2.html#model_instance
7. 那么现在就开始执行我们的Payload:
http://127.0.0.1/thinkphp/index.php?name[0]=bind&name[1]=0 and updatexml(2,concat(0x7e,user()),0)
0x03 漏洞分析
- 这里也是用phpstorm+debug进行动态分析,配置方法在我上一篇的5版本注入文章里面也写过了,如有不懂可以去查阅。 我们先在I函数这里下一个断点,看下I函数有没有过滤掉我们的输入。
然后访问我们payload,它会跳到入口文件,我们只要分析的是I函数执行的地方,所以我们直接F8跳到执行I函数地方:
2. F7跟进,它会跳到文件:thinkphp/ThinkPHP/Common/functions.php
这里。
判断传入的值是用什么方式:
接下来就是对我们传入的值进行过滤:
对我们传入的bind
值进行过滤:
3. 然后执行我们的数据库更新的操作:
我们F7继续跟下去,有些影响不大的函数可以直接F8跳过去,到save
函数:
执行到update
函数我们F7跟进去:
4. 我们继续往下,F7进入parseSet
函数,可以看到赋值了一个占位符0给pass
,代替我们的密码111111
。
5. parseSet
函数执行完后,我们继续F7进入parseWhere
函数。
我们再往下面的parseWhereItem
函数进行分析,这里写到,$exp
=$val[0]
也就是我们传入的bind
。
$exp = strtolower($val[0])
这里判断如果我们传入的数组[0]为bind
就进行拼接:
6. 从拼接这里也能看出我们为什么要把第二个数组构造的时候填0,因为是要对应上面parseSet
函数,赋值了一个占位符为0,用来代替上面的密码111111
,而接下来就没有这种赋值操作了,所以我们如果填其他值得话执行SQL语句的时候就会报错,到后面我会点出在哪里的。
7. 我们继续,F7进去执行预处理语句的地方:
我们可以看到这里用到了bindValue
,绑定一个值到一个参数,也就是把111111
密码绑定到:0这里。
8. 继续跟下来,到SQL语句执行报错的地方,可以很清楚看到直接了我们的报错语句updatexml
的值:
我们再往error
函数跟,可以看到替换掉占位符的语句:
- 如果我们把0换成1会是怎么样的结果呢?
访问payload,注意已经把数组第二个0改成1了。
http://127.0.0.1/thinkphp/index.php?name[0]=bind&name[1]=1 and updatexml(2,concat(0x7e,user()),0)
我们把断点直接断在SQL执行的地方:
F7进去然后F8直接走,走到执行报错的地方,我们就会看到如果为1的话那SQL就无法执行下去了。
0x04 结束
如果调试的时候觉得跟得有点乱的时候,可以耐心的重头调试多几次就清楚了。
0x05 参考
http://php.net/manual/en/book.pdo.php
http://document.thinkphp.cn/manual_3_2.html
- 原文作者: F0rmat
- 原文链接: https://xxe.icu/thinkphp-framework-3.2.x-sql-injection-vulnerability-analysis.html
- 版权声明:本作品采用 署名 - 非商业性使用 4.0 国际 (CC BY-NC 4.0)进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。