ThinkPHP3.2.3框架exp注入
这个注入与bind注入差不多
环境搭建
直接在IndexController.class.php
中创建一个demo
public function index(){
$map=array();
$map['id']=$_GET['id'];
$data=M('users')->where($map)->find();
dump($data);
}
数据库配置:
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE' => 'mysql',
'DB_HOST' => 'localhost',
'DB_NAME' => 'thinkphp',
'DB_USER' => 'root',
'DB_PWD' => 'root',
'DB_PORT' => '3306',
'DB_FIELDS_CACHE' => true,
'SHOW_PAGE_TRACE' => true,
);
漏洞分析
payload:
http://127.0.0.1/thinkphp32/index.php?id[0]=exp&id[1]==1%20and%20updatexml(1,concat(0x7,user(),0x7e),1)
indexController.class.php
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$map=array();
$map['id']=$_GET['id'];
$data=M('users')->where($map)->find();
dump($data);
}
}
可以看到这里并没有使用I函数
(使用I函数
则不会产生exp注入),原因我们在之后分析,我们先来分析这个注入。直接跟入where函数:
可以看到跟以前一样,由于传入的是数组,直接将id数组赋值给了$this->options['where']
,然后返回。
继续跟入find,还是跟以前一样,需要关注的两个位置是:
$options = $this->_parseOptions($options);
$resultSet = $this->db->select($options);
先来跟入$this->_parseOptions($options);
/**
* 分析表达式
* @access protected
* @param array $options 表达式参数
* @return array
*/
protected function _parseOptions($options=array()) {
if(is_array($options))
$options = array_merge($this->options,$options);
if(!isset($options['table'])){
// 自动获取表名
$options['table'] = $this->getTableName();
$fields = $this->fields;
}else{
// 指定数据表 则重新获取字段列表 但不支持类型检测
$fields = $this->getDbFields();
}
// 数据表别名
if(!empty($options['alias'])) {
$options['table'] .= ' '.$options['alias'];
}
// 记录操作的模型名称
$options['model'] = $this->name;
// 字段类型验证
if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {
// 对数组查询条件进行字段类型检查
foreach ($options['where'] as $key=>$val){
$key = trim($key);
if(in_array($key,$fields,true)){
if(is_scalar($val)) {
$this->_parseType($options['where'],$key);
}
}elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){
if(!empty($this->options['strict'])){
E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');
}
unset($options['where'][$key]);
}
}
}
// 查询过后清空sql表达式组装 避免影响下次查询
$this->options = array();
// 表达式过滤
$this->_options_filter($options);
return $options;
}
可以看到由于传入数组所以在if(is_scalar($val))处并没有进入_parseType进行类型转换,最后返回,接下分析$this->db->select($options)
:
继续进入$this->buildSelectSql($options);
:
然后是$this->parseSql($this->selectSql,$options);
:
之后重点在这个parseWhere
中的parseWhereItem
函数,和bind基本一样。
可以看到在parseWhereItem
直接进行了拼接,拼接结果为:
`id` =1 and updatexml(1,concat(0x7,user(),0x7e),1)
最后返回最终拼接结果为:
造成了sql注入。
最后回到一开始的I函数
问题,在I函数
内部的think_filter
中,对exp进行了正则匹配,如果匹配到了会在其后面加空格,从而使得后面的将会对其进行处理无法正确拼接sql注入语句。
修复
在取出传入参数时使用I函数
即可避免产生exp注入。