[Web 安全] PHP 反序列化毛病 —— PHP 序列化 & 反序列化 ...

打印 上一主题 下一主题

主题 1765|帖子 1765|积分 5295

关注这个专栏的其他相关笔记:[Web 安全] 反序列化毛病 - 学习笔记-CSDN博客
  0x01:PHP 序列化 — Serialize

序列化就是将对象的状态信息转化为可以存储或传输的形式的过程,在 PHP 中,通常使用 serialize() 函数来完成序列化的操纵。
下面笔者直接简要点列出各个数据范例序列化之后的效果,不信的崽崽可以自己跑一下:
  1.  echo serialize(null);       // N;
  2.  echo serialize(123);        // i:123;   => int 类型的值为 123
  3.  echo serialize(123.3);      // d:123.3; => double 类型的值为 123.3
  4.  echo serialize(true);       // b:1;     => Boolean 类型的值为 True
  5.  echo serialize("Blue17");   // s:6:"Blue17"; => String 类型的值为 Blue17
  6.  echo serialize(array("Blue17", 17, null));
  7.  // a:3:{i:0;s:6:"Blue17";i:1;i:17;i:2;N;}
复制代码
0x0101:序列化效果剖析 — Array 范例

这部分笔者就简单分析一下上面 Array 范例序列化后的效果:
  1.  echo serialize(array("Blue17", 17, null));
  2.  // a:3:{i:0;s:6:"Blue17";i:1;i:17;i:2;N;}
  3.  ​
  4.  /*
  5.  a:3               => array 中有三个元素
  6.  i:0;s:6:"Blue17"  => index 为 0 的地方是一个长度为 6 的 String 类型的元素,值为 Blue17
  7.  i:1;i:17;         => index 为 1 的地方是一个 int 类型的元素,值为 17
  8.  i:2;N;            => index 为 2 的地方是一个 Null 类型的元素。
  9.  */
复制代码
0x0102:序列化效果剖析 — 类

类的序列化效果根本大差不差,但是差别 “访问范例” 的变量序列化的效果有很大差别,这部分笔者就按照 “访问范例” 举行分类,并对每个单独举行解说。
1. 类序列化效果剖析 —— Public 型

这里我们开始进入正规,讲讲最常见的类的序列化效果,先来一个最常见的试试水:
  1.  <?php
  2.  ​
  3.  class demo {
  4.      public $var1;               // 这个变量没有赋值
  5.      public $var2 = "Blue17";    // 这个变量赋予了字符串类型的值
  6.      var $var3 = 17;          // 虽然修饰符是 var 但其实还是 public 类型的
  7.  ​
  8.      function printVar($var) {
  9.          $localVar = $var;    // 类方法中的局部变量
  10.          echo $localVar;
  11.      }
  12.  }
  13.  ​
  14.  // O:4:"demo":3:{s:4:"var1";N;s:4:"var2";s:6:"Blue17";s:4:"var3";i:17;}
  15.  echo serialize(new demo(array(123)));
复制代码
下面我们仔细分析一下效果,可以看到,它序列化的效果根本全是变量,类中方法实在是没有被序列化的,类中的局部变量也没有被序列化
  1.  O:4:"demo":3:{s:4:"var1";N;s:4:"var2";s:6:"Blue17";s:4:"var3";i:17;}
  2.  ​
  3.  // O:4:"demo":3 => Object 对象是一个 4 字的叫 demo,其中有 3 个属性(变量)
  4.  // s:4:"var1";N; => 属性名称占 4 字节,叫 var1 其值是 Null 类型
  5.  // s:4:"var2";s:6:"Blue17" => 属性名称占 4 字节,叫 var2,其值是 String 类型,长度为 6 内容是 Blue17
  6.  // s:4:"var3";i:17; => 属性名称占 4 字节,叫 var3,其值是 int 类型,值为 17。
复制代码
2. 类序列化效果剖析 —— Protected 型

下面我们来看看假如类的属性中混入了 Protect 型的变量它序列化的效果长啥样吧:
  1.  <?php
  2.  ​
  3.  class demo {
  4.      protected $var1;               // 这个变量没有赋值
  5.      protected $var2 = "Blue17";    // 这个变量赋予了字符串类型的值
  6.      protected $var3 = 17;          // 虽然修饰符是 var 但其实还是 public 类型的
  7.  }
  8.  ​
  9.  echo serialize(new demo()) . "\n";
  10.  // O:4:"demo":3:{s:7:"*var1";N;s:7:"*var2";s:6:"Blue17";s:7:"*var3";i:17;}
  11.  ​
  12.  echo urlencode(serialize(new demo()));
  13.  // O%3A4%3A%22demo%22%3A3%3A%7Bs%3A7%3A%22%00%2A%00var1%22%3BN%3Bs%3A7%3A%22%00%2A%00var2%22%3Bs%3A6%3A%22Blue17%22%3Bs%3A7%3A%22%00%2A%00var3%22%3Bi%3A17%3B%7D
复制代码
下面我们仔细分析一下效果,这里笔者特意对它序列化后的效果做了一个编码,因为内里实在有一些不可见的字符,不编码是看不出来的,大部分内容实在与 Public 划一,但是被 protected 修饰的变量序列化后的内容就有很大差别了:
  1.  O:4:"demo":3:{s:7:"*var1";N;s:7:"*var2";s:6:"Blue17";s:7:"*var3";i:17;}
  2.  ​
  3.  // s:7:"*var1";N; => 属性名称占 7 个字节?怎么数着只有 5 个?
  4.  ​
  5.  // 这个是 URL 编码后的内容:s%3A7%3A%22%00%2A%00var1%22%3BN%3B
  6.  // 这个是简单解码后的样子:s:7:"%00*%00var1";N;
复制代码
如上,可以发现,Protected 属性序列化后明面看只有 *var1 如许,但实在 * 双方实在还藏了两个 ASCII 码值为 0 的字符 (这也引出后面一个 Bug,在实验构造反序列化值得时间,不建议通过直接复制就篡改被 Protected 大概 Private 修饰的值,因为你复制得内容一样平常都不全,会丢掉这个特殊的 %00)。
3. 类序列化效果剖析 —— Private 型

下面我们来看看假如类的属性中混入了 Private 型的变量它序列化的效果长啥样吧:
  1.  <?php
  2.  ​
  3.  class demo {
  4.      private $var1;               // 这个变量没有赋值
  5.      private $var2 = "Blue17";    // 这个变量赋予了字符串类型的值
  6.  }
  7.  ​
  8.  echo serialize(new demo()) . "\n";
  9.  // O:4:"demo":2:{s:10:"demovar1";N;s:10:"demovar2";s:6:"Blue17";}
  10.  ​
  11.  echo urlencode(serialize(new demo()));
  12.  // O%3A4%3A%22demo%22%3A2%3A%7Bs%3A10%3A%22%00demo%00var1%22%3BN%3Bs%3A10%3A%22%00demo%00var2%22%3Bs%3A6%3A%22Blue17%22%3B%7D
复制代码
下面我们仔细分析一下效果,这里笔者特意对它序列化后的效果做了一个编码,因为内里实在有一些不可见的字符,不编码是看不出来的,大部分内容实在与 Public 划一,但是被 Private 修饰的变量序列化后的内容就有很大差别了:
  1.  O:4:"demo":2:{s:10:"demovar1";N;s:10:"demovar2";s:6:"Blue17";}
  2.  ​
  3.  // s:10:"demovar1";N; => 属性名称占 10 个字节?怎么数着只有 8 个?
  4.  ​
  5.  // 这个是 URL 编码后的内容:s%3A10%3A%22%00demo%00var1%22%3BN%3B
  6.  // 这个是简单解码后的样子:s:10:"%00demo%00var1";N; => %00 算一位,数一数,刚好 10 位
复制代码
如上,可以发现,Private 属性序列化后明面看只有 demovar1 如许,但实在类名双方实在还藏了两个 ASCII 码值为 0 的字符,所以其真实格式为(URL 编码后的哈,不编码的话 ASCII 值为 0 的实在是不可见字符) %00类名%00变量名。
0x02:PHP 反序列化 — Unserialize

以下是反序列化相关的几个特性:


  • 反序列化就是将序列化得到的字符串转化为一个对象的过程。
  • 反序列化生成的对象成员属性值由被反序列化的字符串决定,与原来类预界说的值无关。
  • PHP 中通过 unserialize() 函数举行反序列化,序列化使用 serialize() 函数。
  • 反序列化不触发类的成员方法,需要被调用方法后才会被触发。(不愿定,这个后面讲)
下面笔者就以 Public 型的类为例,解说一下反序列化的用处(另外两种范例,流程划一,但是要特别注意 %00 到底有没有被复制已往,假如报错了,一样平常就是这个的问题)。
0x0201:反序列化 —— 正常流程

首先是比力简单的 Public 型类的反序列化,我们先创建一个类,假设叫 Note (笔记)类吧,然后我们写笔记就要实例化这个类,然后往这个类的对象里写东西,代码如下:
  1. <?php
  2. class Note {
  3.     public $title;   // 笔记标题
  4.     public $content; // 笔记内容
  5.     // 记录标题 & 内容
  6.     function write($title, $content) {
  7.         $this -> title = $title;
  8.         $this -> content = $content;
  9.     }
  10.     // 读取标题 & 内容
  11.     function read() {
  12.         echo "Title: " . $this -> title . "\n";
  13.         echo "Content: " . $this -> content . "\n";
  14.     }
  15. }
  16. // 实例化笔记类
  17. $note = new Note();
  18. // 往笔记里写东西
  19. $note -> write("Hello, World!!", "Today Is a Nice Day!!");
复制代码
如上,我们已经往笔记里写东西了,写了你要保存吧,但是你是个对象你咋保存?这时就可以使用序列化,把 $note 这个对象里的内容序列化然后存储在一个文件里:
  1. // 保存 $note 笔记里的东西
  2. echo serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
复制代码

OK,保存了你过段时间得看吧,给你看下面这个东西你又看不懂是不是:
  1. O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
复制代码
这个时间就需要用我们软件举行反序列化然后再调用 read 方法了是吧:
  1. // 保存 $note 笔记里的东西
  2. $save = serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
  3. // 查看保存的内容
  4. $raw = unserialize($save); // 对序列化的内容进行反序列化
  5. $raw -> read();
复制代码

如上,可以看到,通过反序列化被保存的值,我们乐成还原了用户笔记里写的东西。下面是整个测试的源码(这里笔者特别提醒,反序列化的环境中要有序列化的那个类,否则纵然反序列化了也是无法执行类的方法的):
  1. <?php// 创建笔记类class Note {    public $title;   // 笔记标题    public $content; // 笔记内容    // 记录标题 & 内容    function write($title, $content) {        $this -> title = $title;        $this -> content = $content;    }    // 读取标题 & 内容    function read() {        echo "Title: " . $this -> title . "\n";        echo "Content: " . $this -> content . "\n";    }}// 实例化笔记类$note = new Note();// 往笔记里写东西$note -> write("Hello, World!!", "Today Is a Nice Day!!");// 保存 $note 笔记里的东西
  2. $save = serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
  3. // 查看保存的内容
  4. $raw = unserialize($save); // 对序列化的内容进行反序列化
  5. $raw -> read(); // 执行类的成员方法
复制代码
0x0202:反序列化 —— 非常流程

我们继续假设,我们刚刚是本地的,假设你写了笔记,然后你要上传,那你上传服务端的序列化的内容就是下面这个:
  1. O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
复制代码
假设,攻击者截获了这个内容,按照 PHP 序列化的格式自己改了一下(重要是改内容和长度):
  1. O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:25:"Today Is NOT a Nice Day!!";}
复制代码
如上,我们添加了一个单词 Not ,然后修改了长度,然后我们 “发送” 到服务端,让他读一下,代码如下:
  1. <?php
  2. // 创建笔记类
  3. class Note {
  4.     public $title;   // 笔记标题
  5.     public $content; // 笔记内容
  6.     // 记录标题 & 内容
  7.     function write($title, $content) {
  8.         $this -> title = $title;
  9.         $this -> content = $content;
  10.     }
  11.     // 读取标题 & 内容
  12.     function read() {
  13.         echo "Title: " . $this -> title . "\n";
  14.         echo "Content: " . $this -> content . "\n";
  15.     }
  16. }
  17. // 接收的信息
  18. $receive = 'O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:25:"Today Is NOT a Nice Day!!";}';
  19. $raw = unserialize($receive); // 对序列化的内容进行反序列化
  20. $raw -> read(); // 调用读方法
复制代码

如上,可以看到,效果就如许被修改了。这就是前面先容的反序列化的一个特性 “反序列化生成的对象成员属性值由被反序列化的字符串决定,与原来类预界说的值无关。”,也是我们后面 “反序列化毛病的依据”。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

魏晓东

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表