加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > PHP > 正文

ThinkPHP v5.1.x POP 链分析

发布时间:2020-05-26 01:36:33 所属栏目:PHP 来源:互联网
导读:环境:MacOS 10.13 MAMAP Prophp 7.0.33 + xdebugVisual Studio Code前言我所理解的 POP Chain:利用魔术方法并巧妙构造特殊属性调用一系列函

环境:MacOS 10.13 MAMAP Prophp 7.0.33 + xdebugVisual Studio Code前言我所理解的 POP Chain:利用魔术方法并巧妙构造特殊属性调用一系列函数或类方法以执行某种敏感操作的调用堆栈反序列化常用魔法函数


前言
我所理解的 POP Chain:
利用魔术方法并巧妙构造特殊属性调用一系列函数或类方法以执行某种敏感操作的调用堆栈

反序列化常用魔法函数

  1.  1 __wakeup, unserialize() 执行前调用
     2 __destruct, 对销毁的时候调用
     3 __toString, 类被当成字符串时的回应方法
     4 __construct(),当对象创建(new)时会自动调用,注意在
     5 unserialize()时并不会自动调用
     6 __sleep(),serialize()时会先被调用
     7 __call(),在对象中调用一个不可访问方法时调用
     8 __callStatic(),用静态方式中调用一个不可访问方法时调用
     9 __get(),获得一个类的成员变量时调用
    10 __set(),设置一个类的成员变量时调用
    11 __isset(),当对不可访问属性调用isset()或empty()时调用
    12 __unset(),当对不可访问属性调用unset()时被调用。
    13 __wakeup(),执行unserialize()时,先会调用这个函数
    14 __toString(),类被当成字符串时的回应方法
    15 __invoke(),调用函数的方式调用一个对象时的回应方法
    16 __set_state(),调用var_export()导出类时,此静态方法会被调用。
    17 __clone(),当对象复制完成时调用
    18 __autoload(),尝试加载未定义的类
    19 __debugInfo(),打印所需调试信息

phar 文件通过 phar:// 伪协议拓宽攻击面 因为 phar 文件会以序列化的形式存储用户自定义的meta-data,所以在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作,深入了解请至:https://paper.seebug.org/680/

如果对反序列化没有了解的话建议先学习下相关内容ThinkPHP v5.1.x POP 链分析安装这里使用的是官方 ThinkPHP V5.1.38composer 部署composer create-project topthink/think=5.1.38 tp5.1.38利用链全局搜索函数 __destruct

来到 /thinkphp/library/think/process/pipes/Windows.php

  1.  1 public function __destruct()
     2     {
     3         $this->close();
     4         $this->removeFiles();
     5     }
     6     . . .  . . . 
     7     /**
     8      * 删除临时文件
     9      */
    10     private function removeFiles()
    11     {
    12         foreach ($this->files as $filename) {
    13             if (file_exists($filename)) {
    14                 @unlink($filename);
    15             }
    16         }
    17         $this->files = [];
    18     }

看下 file_exists 的描述f

  1. 1 ile_exists ( string $filename ) 
复制代码

: bool如果传入的 $filename 是个反序列化的对象,在被 file_exists 当作字符串处理的时候就会触发其 __toString 方法(如果有的话)所以下面就是找含 __toString 方法的类

来到 /thinkphp/library/think/model/concern/Conversion.php

  1. 1 public function toJson($options = JSON_UNESCAPED_UNICODE)
    2     {
    3         return json_encode($this->toArray(),$options);
    4     }
    5     . . .   . . . 
    6     public function __toString()
    7     {
    8         return $this->toJson();
    9     }

可以看到,在 toJson() 函数中又调用了 toArray() 函数如果 toArray() 函数中存在并使用某个可控变量的方法,那么我们就可以利用这点去触发其他类的 __call 方法下面是 toArray() 函数的定义,$this->append 作为类属性是可控的,所以 $relation 和 $name 也就可控了,于是 $relation->visible($name); 就成了这个 POP 链中的中间跳板

phper在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的(点击→)我的官方群677079770https://jq.qq.com/?_wv=1027&k=5BoEMVl

  1.  1 public function toArray()
     2 {
     3     $item       = [];
     4     $hasVisible = false;
     5     . . .   . . .
     6     // 追加属性(必须定义获取器)
     7     if (!empty($this->append)) {
     8         foreach ($this->append as $key => $name) {
     9             if (is_array($name)) {
    10                 // 追加关联对象属性
    11                 $relation = $this->getRelation($key);
    12                 if (!$relation) {
    13                     $relation = $this->getAttr($key);
    14                     if ($relation) {
    15                         $relation->visible($name);
    16                     }
    17                 }
    18                 $item[$key] = $relation ? $relation->append($name)->toArray() : [];
    19             } elseif (strpos($name,'.')) {
    20            . . .   . . .   
    21             } else {
    22                 $item[$name] = $this->getAttr($name,$item);
    23             }
    24         }
    25     }
    26     return $item;
    27 }

那我们在这里应该传入怎么样的值以及什么数据呢,先看下 $relation 是如何处理得到的

跟进 getRelation,在 /thinkphp/library/think/model/concern/RelationShip.php 中找到函数定义

  1.  1 trait RelationShip
     2 {
     3     . . .   . . . 
     4     /**
     5      * 获取当前模型的关联模型数据
     6      * @access public
     7      * @param  string $name 关联方法名
     8      * @return mixed
     9      */
    10     public function getRelation($name = null)
    11     {
    12         if (is_null($name)) {
    13             return $this->relation;
    14         } elseif (array_key_exists($name,$this->relation)) {
    15             return $this->relation[$name];
    16         }
    17         return;
    18     }
    19     . . .   . . . 
    20 }

由于 getRelation 最终都会 return; 导致返回 NULL,所以 下面的 if (!$relation) 一定成立所以直接跟进后面的 getAttr,在 /thinkphp/library/think/model/concern/Attribute.php 找到其定义

  1.  1 trait Attribute
     2 {
     3     . . .   . . .
     4     public function getData($name = null)
     5     {
     6         if (is_null($name)) {
     7             return $this->data;
     8         } elseif (array_key_exists($name,$this->data)) {
     9             return $this->data[$name];
    10         } elseif (array_key_exists($name,$this->relation)) {
    11             return $this->relation[$name];
    12         }
    13         throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
    14     }
    15     . . .   . . . 
    16     public function getAttr($name,&$item = null)
    17     {
    18         try {
    19             $notFound = false;
    20             $value    = $this->getData($name);
    21         } catch (InvalidArgumentException $e) {
    22             $notFound = true;
    23             $value    = null;
    24         }
    25         . . .  . . . 
    26     }
    27 }

从 getAttr ---> getData 返回 data 数组中同名键值的元素值,即 $relation <---- $this->data[$name],我们需要的 $data 和 $append 分别位于 Attribute 和 Conversion,且两者都是 trait 类型Trait 可以说是和 Class 相似,是 PHP 5.4.0 开始实现的一种代码复用的方法,可以使用 use 加载

详情可以看官方手册 PHP: Trait - Manual

所以接下来是寻找一个同时使用了 Attribute 和 Conversion 的类

发现只有 /thinkphp/library/think/Model.php 满足条件

  1. 1 abstract class Model implements JsonSerializable, ArrayAccess
    2 {
    3     use modelconcernAttribute;
    4     use modelconcernRelationShip;
    5     use modelconcernModelEvent;
    6     use modelconcernTimeStamp;
    7     use modelconcernConversion;
    8     . . .   . . .
    9 }

下面就需要找到一个没有 visible 方法却有 __call 方法的类作为执行点找到 /thinkphp/library/think/Request.php 中的 Request 类

  1.  1 class Request
     2 {
     3     . . .   . . . 
     4     /**
     5      * 扩展方法
     6      * @var array
     7      */
     8     protected $hook = [];
     9     . . .   . . . 
    10     public function __call($method,$args)
    11     {
    12         if (array_key_exists($method,$this->hook)) {
    13             array_unshift($args,$this);
    14             return call_user_func_array($this->hook[$method],$args);
    15         }
    16         throw new Exception('method not exists:' . static::class . '->' . $method);
    17     }
    18     . . .   . . .
    19 }

这里的回调参数来源于 $hook 数组,而且方法名和参数都是可控的,不过 array_unshift 函数会把若干元素前置到数组的开头

  1.  1  $queue = array("orange","banana");
     2 array_unshift($queue,"apple","raspberry");
     3 print_r($queue);
     4 ///
     5 Array
     6 (
     7     [0] => apple
     8     [1] => raspberry
     9     [2] => orange
    10     [3] => banana
    11 )

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读