加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

分析之php反序列化

发布时间:2022-07-07 15:24:29 所属栏目:PHP教程 来源:互联网
导读:1 前言 最近也是在复习之前学过的内容,感觉对PHP反序列化的理解更加深了,所以在此总结一下 2 serialize()函数 所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方
  1 前言
 
  最近也是在复习之前学过的内容,感觉对PHP反序列化的理解更加深了,所以在此总结一下
 
  2 serialize()函数
 
  “所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。”
 
  一开始看这个概念可能有些懵,但之后也是慢慢理解了
 
  在程序执行结束时,内存数据便会立即销毁,变量所储存的数据便是内存数据,而文件、数据库是“持久数据”,因此PHP序列化就是将内存的变量数据“保存”到文件中的持久数据的过程。
 
  $s = serialize($变量); //该函数将变量数据进行序列化转换为字符串
 
  file_put_contents(‘./目标文本文件', $s); //将$s保存到指定文件
 
  下面通过一个具体的例子来了解一下序列化:
 
  <?php
   
  class User
   
  {
   
    public $age = 0;
   
    public $name = '';
   
   
   
    public function PrintData()
   
    {
   
      echo 'User '.$this->name.'is'.$this->age.'years old. <br />';
   
    }
   
  }
   
  //创建一个对象
   
  $user = new User();
   
  // 设置数据
   
  $user->age = 20;
   
  $user->name = 'daye';
   
   
   
  //输出数据
   
  $user->PrintData();
   
  //输出序列化之后的数据
   
  echo serialize($user);
   
   
   
  ?>
  这个是结果:
 
  详解之php反序列化
 
  可以看到序列化一个对象后将会保存对象的所有变量,并且发现序列化后的结果都有一个字符,这些字符都是以下字母的缩写。
 
  a - array         b - boolean
   
  d - double         i - integer
   
  o - common object     r - reference
   
  s - string         C - custom object
   
  O - class         N - null
   
  R - pointer reference   U - unicode string
  了解了缩写的类型字母,便可以得到PHP序列化格式
 
  O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
 
  对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}
 
  通过以上例子,便可以理解了概念中的通过serialize()函数返回一个包含字节流的字符串这一段话。
 
  3 unserialize()函数
 
  unserialize() 对单一的已序列化的变量进行操作,将其转换回 PHP 的值。在解序列化一个对象前,这个对象的类必须在解序列化之前定义。
 
  简单来理解起来就算将序列化过存储到文件中的数据,恢复到程序代码的变量表示形式的过程,恢复到变量序列化之前的结果。
 
  $s = file_get_contents(‘./目标文本文件'); //取得文本文件的内容(之前序列化过的字符串)
 
  $变量 = unserialize($s); //将该文本内容,反序列化到指定的变量中
 
  通过一个例子来了解反序列化:
 
  <?php
   
  class User
   
  {
   
    public $age = 0;
   
    public $name = '';
   
   
   
    public function PrintData()
   
    {
   
      echo 'User '.$this->name.' is '.$this->age.' years old. <br />';
   
    }
   
  }
   
  //重建对象
   
  $user = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}');
   
   
   
  $user->PrintData();
   
   
   
  ?>
  这个是结果:
 
  详解之php反序列化
 
  注意:在解序列化一个对象前,这个对象的类必须在解序列化之前定义。否则会报错
 
  4 PHP反序列化漏洞
 
 
  __wakeup()  使用unserialize时触发
 
  __sleep()  使用serialize时触发
 
  __destruct()  对象被销毁时触发
 
  __call()  在对象上下文中调用不可访问的方法时触发
 
  __callStatic()  在静态上下文中调用不可访问的方法时触发
 
  __get()  用于从不可访问的属性读取数据
 
  __set()  用于将数据写入不可访问的属性
 
  __isset()  在不可访问的属性上调用isset()或empty()触发
 
  __unset()   在不可访问的属性上使用unset()时触发
 
  __toString()  把类当作字符串使用时触发,返回值需要为字符串
 
  __invoke()  当脚本尝试将对象调用为函数时触发
 
  这里只列出了一部分的魔法函数,
 
  下面通过一个例子来了解一下魔法函数被自动调用的过程
 
  <?php
   
  class test{
   
    
   public function __destruct(){
   
   echo "__destruct<br>";
   
   }
   
   public function __toString(){
   
   return "__toString<br>";
   
   }
   
   public function __sleep(){
   
   echo "__sleep<br>";
   
   return array('varr1','varr2');
   
   }
   $obj->echoP();  //调用echoP()方法,输出"abc"
   
  echo $obj;  //obj对象被当做字符串输出,调用__toString()方法,输出__toString
   
  $s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
   
  echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
   
  // 脚本结束又会调用__destruct()方法,输出__destruct
   
  ?>
  这个是结果:
 
  详解之php反序列化
 
  通过这个例子就可以清晰的看到魔法函数在符合相应的条件时便会被调用。
 
  5 对象注入
 
  当用户的请求在传给反序列化函数unserialize()之前没有被正确的过滤时就会产生漏洞。因为PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。
 
  对象漏洞出现得满足两个前提:
 
  一、unserialize的参数可控。
 
  二、 代码里有定义一个含有魔术方法的类,并且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数。
 
  下面来举个例子:
 
  <?php
   
  class A{
   
    var $test = "demo";
   
    function __destruct(){
   
        echo $this->test;
   
    }
   
  }
   
  $a = $_GET['test'];
   
  $a_unser = unserialize($a);
   
  ?>
  比如这个列子,直接是用户生成的内容传递给unserialize()函数,那就可以构造这样的语句
 
 
  再看一个例子:
 
  <?php
   
  class A{
   
    var $test = "demo";
   
    function __destruct(){
   
      @eval($this->test);//_destruct()函数中调用eval执行序列化对象中的语句
   
    }
   
  }
   
  $test = $_POST['test'];
   
  $len = strlen($test)+1;
   
  $pp = "O:1:"A":1:{s:4:"test";s:".$len.":"".$test.";";}"; // 构造序列化对象
   
  $test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
   
  ?>
  其实仔细观察就会发现,其实我们手动构造序列化对象就是为了unserialize()函数能够触发__destruc()函数,然后执行在__destruc()函数里恶意的语句。
 
 
  wakeup()魔法函数绕过
 
  PHP5<5.6.25
 
  PHP7<7.0.10
 
  PHP反序列化漏洞CVE-2016-7124
 
  #a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行

(编辑:云计算网_泰州站长网)

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

    热点阅读