加入收藏 | 设为首页 | 会员中心 | 我要投稿 四平站长网 (https://www.0434zz.com.cn/)- 云服务器、对象存储、基础存储、视频终端、数据应用!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

简单分析PHP中序列化用法介绍

发布时间:2022-07-04 09:36:05 所属栏目:PHP教程 来源:互联网
导读:0x00 序列化函数 serialize():返回带有变量类型和值的字符串 unserialize():想要将已序列化的字符串变回 PHP 的值 测试代码: ?php class test{ var $a; var $b; function __construct($a,$b,$c){ $a = $a; $this-b = $b; } } class test1 extends test{ fu
  0x00 序列化函数
 
  serialize():返回带有变量类型和值的字符串
 
  unserialize():想要将已序列化的字符串变回 PHP 的值
 
  测试代码:
 
  <?php
    class test{
       var $a;
       var $b;
       function __construct($a,$b,$c){
        $a  = $a;
        $this->b = $b;
       
       }
      }
       
      class test1 extends test{
       
        function __construct($a){
         $this->a = $a;
        }
       }
      $a = 'hello';
      $b = 123;
      $c = false;
      $d = new test('helloa','hellob','helloc');
      $e = new test1('hello');
       
      var_dump(serialize($a));
      var_dump(serialize($b));
      var_dump(serialize($c));
      var_dump(serialize($d));
      var_dump(serialize($e));
  ?>
  运行结果:
 
  string 's:5:"hello";' (length=12)
  string 'i:123;' (length=6)
  string 'b:0;' (length=4)
  string 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' (length=46)
  string 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' (length=46)
  序列化字符串格式:变量类型:变量长度:变量内容.
 
  如果序列化的是一个对象,序列化字符串格式为:
 
  变量类型:类名长度:类名:属性数量:{属性类型:属性名长度:属性名;属性值类型:属性值长度:属性值内容}
 
  将上述结果反序列化输出,执行结果:
 
  string 'hello' (length=5)
  int 123
  boolean false
  object(test)[1]
    public 'a' => null
    public 'b' => string 'hellob' (length=6)
  object(test1)[1]
    public 'a' => string 'hello' (length=5)
    public 'b' => null
  0x01 对象序列化
 
  当序列化对象时,PHP 将在序列动作之前调用该对象的成员函数 sleep()。这样就允许对象在被序列化之前做任何清除操作。类似的,当使用 unserialize() 恢复对象时, 将调用 wakeup()成员函数。
 
  在serialize()函数执行时,会先检查类中是否定义了 sleep()函数,如果存在,则首先调用 sleep()函数,如果不存在,就保留序列字符串中的所有属性。
 
  在unserialize()函数执行时,会先检查是否定义了 wakeup()函数。如果 wakeup()存在,将执行__wakeup()函数,会使变量被重新赋值。
 
  serialize()测试代码:
 
  <?php
    class test{
       var $a;
       var $b;
       function __construct($a,$b,$c){
        $this->a  = $a;
        $this->b = $b;
       
       }
       function __sleep(){
        echo "b has changed"."n";
        $this->b = 'hib';
        return $this->b;
         
       
       }
       function __wakeup(){
        echo "a has changed"."n";
        $this->a = 'hia';
       
       }
      }
       
      class test1 extends test{
       
        function __construct($a){
         $this->a = $a;
        }
       }
       
      $d = new test('helloa','hellob','helloc');
      $e = new test1('hello');
       
      serialize($d);
      serialize($e);
       
      var_dump($d);
      var_dump($e);
  ?>
  执行结果:
 
  b has changed b has changed
  object(test)[1]
  public 'a' => string 'helloa' (length=6)
  public 'b' => string 'hib' (length=3)
  object(test1)[2]
  public 'a' => string 'hello' (length=5)
  public 'b' => string 'hib' (length=3)
  unserialize()测试代码:
 
  class test{
       var $a;
       var $b;
       function __construct($a,$b,$c){
        $this->a  = $a;
        $this->b = $b;
       
       }
       function __sleep(){
        echo "b has changed"."n";
        $this->b = 'hib';
        return $this->b;
         
       
       }
       function __wakeup(){
        echo "a has changed"."n";
        $this->a = 'hia';
       
       }
      }
       
      class test1 extends test{
       
        function __construct($a){
         $this->a = $a;
        } //phpfensi.com
       }
       
          $d = 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' ;
          $e = 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' ;
       
          var_dump(unserialize($d));
          var_dump(unserialize($e));
  运行结果:
 
  a has changed
  object(test)[1]
    public 'a' => string 'hia' (length=3)
    public 'b' => string 'hellob' (length=6)
  a has changed
  object(test1)[1]
    public 'a' => string 'hia' (length=3)
    public 'b' => null
  0x02 PHP序列化的利用
 
  1、magic函数和序列化
 
  参考:php对象注入
 
  除了 sleep()和 wakeup()函数,在序列化时会执行外,还有下面几种利用方式。
 
  Class File
   {
    function __construct($var,$file1,$file2){
     $this->var = $var;
     $this->file1 = $file1;
     $this->file2 = $file2;
     echo $this->var.' and '.$this->file1.' and '.$this->file2.'defined';
    }
    function __destruct(){
     unlink(dirname(__FILE__) . '/' . $this->file1);
     echo $this->file1.'deleted';
    }
    function __toString(){
     return file_get_contents($this->file2);
   
    }
   
   
   }
   
  // $file = new File('hello','123.txt','456.php');
  // var_dump(serialize($file));
  echo unserialize('O:4:"File":3:{s:3:"var";s:5:"hello";s:5:"file1";s:7:"123.txt";s:5:"file2";s:7:"456.php";}');
  ( construct()函数,在实例化一个对象时被调用,一般用来给属性赋值, destruct()在实例化对象完成后执行,__toString()函数在echo一个对象时被调用)
 
  construct()函数内定义了三个变量,var这个没什么暖用,file1和file2,我们在序列化字符串中定义为已经服务器上已经存在的两个文件123.txt和456.php,destruct()中有一个unlink方法,是删除file1,__toString()中,读取file2的内容。
 
  执行结果:
 
  123.txtdeleted
 
  查看源码:
 
  <?php  echo 123; ?>123.txtdeleted
 
  将字符串反序列化后,由于已经对变量赋过值,那么就不会再执行 construct()函数,在 construct()中赋值的变量也是无效的。上述代码中 destruct()方法在在反序列化后,实例化对象结束后执行了, tostring()函数在echo unserialize()处,也被执行了
 
  如果说在当前页面中有request系列函数,那么就可以造成php对象注入:
 
  http://drops.wooyun.org/papers/4820
 
  2、三个白帽挑战赛第三期
 
  是一道源码审计题,题目大致是sql注入结合序列化写入文件
 
  部分源码也是在某个大神 博客 看到的(由于我没有做过题,所以我只截取了和序列化漏洞相关的部分源码):
 
  class Cache extends ArrayObject
  {
    public $path;
    function __construct($path)
    {
      parent::__construct([],ArrayObject::STD_PROP_LIST | ArrayObject::ARRAY_AS_PROPS);
      $this->path = $path;
      if(file_exists($path)){
        $this->cache = unserialize(file_get_contents($this->path));
      }
    function offset(){
    //一些不知道干嘛用的代码
    }
   
    }
   
    function __destruct()
    {
      $cache = $this->serialize();
      file_put_contents($this->path, $cache);
       
    }
   
  }
  又由于我没有做过题。。。。所以模拟了这样一个页面去实例化:
 
  include('cache.php');
  $cache = new Cache('path.txt');
  这题好像是这样的:
 
  通过SQL注入,可控一个文件,假设可控的是path.txt这个文件(在实际的题目中,SQL注入权限不够,web目录下不可写文件,但其他目录可写,已知目录下有文件md5(username).txt,文件名知道,内容可控),这段代码的意思是,判断该文件存在后,读取文件内容,并且反序列化内容,结束时再经过序列化存进文件中。所以可以在可控文件中构造序列化字符串,改变当前的path属性为我们想要的目录。
 
  path.txt:
   
  C:5:"Cache":103:{x:i:3;a:0:{};m:a:2:{s:4:"path";s:25:"F:wampwwwtestpath.php";s:5:"cache";s:18:"<?php echo 123; ?>";}}
  上述字符串是通过输出serialize(一个实例化的Cache对象)构造的,当__construct()执行时,就会将上述字符串反序列化,此时已经实例化了一个cache对象,而它的path值变成了我们定义的”F:wampwwwtestpath.php”,并且多了一个cache属性,值为 <?php echo 123; ?> ,这里的属性名cache是可以随意取的,但如果源码中:
 
  $cache = $this->serialize();
 
  变成了:
 
  $cache = serialize($this->cache);
 
  那么path.txt中的 "cache";s:18:"<?php echo 123; ?>" ;属性名就必须和源码serialize($this->cache)当中的属性名相同。
 
  所以,现在服务器上其实有两个对象,一个是 $cache = new Cache('path.txt'); 定义的$cache,它的path属性值为path.txt;另一个对象是
 
  C:5:"Cache":103:{x:i:3;a:0:{};m:a:2:{s:4:"path";s:25:"F:wampwwwtestpath.php";s:5:"cache";s:18:"<?php echo 123; ?>";}} 被反序列化后的对象,它的path属性的值为path.php。
 
  两个对象实例化结束后,会调用其__destruct()方法,将对象自身序列化,写入path属性定义的路径中。这样就将包含 <?php echo 123; ?> 的内容写进了path.php中。
 
  3、安恒ctf web3
 
  一道源码审计题,解题思路是session上传进度,和session序列化处理器漏洞相结合。
 
  session上传进度:
 
  参考: upload-progress
 
  当 session.upload_progress.enabled INI 选项开启时,在一个上传处理中,在表单中添加一个与INI中设置的 session.upload_progress.name 同名变量时,$_SESSION中就会添加一个保存上传信息的session值,它的session名是 INI 中定义的 session.upload_progress.prefix 加表单中的post的 session.upload_progress.name

(编辑:四平站长网)

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

    推荐文章
      热点阅读