PHP反序列化漏洞

漏洞利用:

反序列化漏洞(Deserialization Vulnerability)是一种安全漏洞,存在于应用程序中对数据进行反序列化操作的过程中。当应用程序接收到外部传递的恶意序列化数据并进行反序列化时,攻击者可以利用这个漏洞执行未经授权的代码或导致应用程序受到攻击。

原理:

既然是反序列化的话,首先要了解什么是序列化和反序列化以及他们的区别

序列化:就是将对象转化为字符串进行存储 class S{`    `public $test="pikachu";``}``   ``$s=new S();     //创建一个对象``   ``serialize($s);     //把这个对象进行序列化``   ``序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";}`    `O:代表object`    `1:表示该对象的类名的字节数(即类名长度为1)`    `S:对象的名称`    `1:表示该对象有 1 个属性。`    `s:数据类型`    `4:变量名称的长度`    `test:变量名称`    `s:数据类型`    `7:变量值的长度`    `pikachu:变量值
反序列化:就是将字符串转化为对象 $u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");`` ``echo $u->test; //得到的结果为pikachu (注意是echo)反序列化使用 unserialize () 函数将字符串转换为对象,序列化使用 serialize () 函数将对象转化为字符串; 反序列化不触发类的成员方法,需要调用方法后才能触发
反序列化漏洞:就是在反序列化过程中,如果恶意者可以对将要转换的字符串进行操控,从而达到任意代码执行的操作

而反序列化漏洞的主要原理是应用程序在反序列化过程中没有对传入的数据进行足够的验证和过滤,导致攻击者可以利用构造的恶意序列化数据来执行任意代码或远程代码执行攻击。

PHP面向对象的基础:

1.过程:

面向过程是一种以“整体事件”为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数;

2.对象:

面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个“对象”;对象是一个由信息及对信息进行处理的描述所组成的整体,是对现实世界的抽象;

对象的三个特征:对象的行为,对象的形态,对象的表示

3.类的定义:

类是定义了一件事物的抽象特点,它将数据的形式以及这些数据上的操作封装在一起;对象是具有类类型的变量,是对类的实例;

类的定义包括定义类名、定义成员属性、定义成员方法; 内部构成:成员属性(变量)+成员方法(函数)

4.继承:

继承性是子类自动共享父类数据结构和方法的机制,是类之间的一种关系;

    在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把一个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容;

父类:一个类被其它类继承,可将该类成为父类,或基类,超类;

子类:一个类继承其他类称为子类,也可称为派生类;

5.权限修饰符:

public:公共的,在类的内部、子类和类的外部中都可以被调用;
protected:受保护的,在类的内部和子类可以被调用,在类的外部不可调用;
private:私有的,只能在类的内部调用,在子类和类的外部不可调用;
举个栗子:class MyClass {
//成员属性
//方法
}
<?php
class wea5e1{
public $we='111';
protected $a5='222';
private $e1='333';
}
$a=new wea5e1();
$a -> we = '444';
echo serialize($a);
O:6:"wea5e1":3:{s:2:"we";s:3:"444";s:5:"%00*%00a5";s:3:"222";s:10:"%00wea5e1%00e1";s:3:"333";}
可以发现这个权限protected会在他的成员那里加上去%00*%00,而private则会加%00类的名字%00
一般格式:
变量类型:类名长度:类名:属性数量:{属性类型:属性名长度:属性名;属性值类型:属性值长度:属性值内容}

6.类型描述

a   array 数组型
b boolean 布尔型
d double 浮点型
i integer 整数型
o common object 共同对象
r object reference 对象引用
s non-escaped binary string 非转义的二进制字符串
S escaped binary string 转义的二进制字符串
C custom object 自定义对象
O class 对象
N null 空
R pointer reference 指针引用
U unicode string Unicode 编码的字符串

魔术方法:

__construct() 构造函数,当一个对象创建时被调用。(实例化时) 
__destruct() 析构函数,当一个对象销毁时被调用。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行
__toString 当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串
__wakeup() 调用unserialize()时触发,反序列化恢复对象之前调用该方法,例如重新建立数据库连接,或执行其它初始化操作。unserialize()会检查是否存在一个__wakeup()方法。如果存在,则会先调用__wakeup(),预先准备对象需要的资源。
__sleep() 调用serialize()时触发 ,在对象被序列化前自动调用,常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个E_NOTICE级别的错误
__call() 在对象上下文中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据,即在调用私有属性的时候会自动执行
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__invoke() 当脚本尝试将对象调用为函数时触发

construct():

构造函数,当一个对象创建时被调用。(实例化时)举个栗子:
<?php
class wea5e1{
public function __construct()
{
echo 'construct方法触发';
}
}
$a = new wea5e1();
//construct 方法触发

destruct():

析构函数,当一个对象销毁时被调用。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 举个栗子:
<?php
class wea5e1{
public function __destruct()
{
echo 'destruct方法触发';
}
}
$a = new wea5e1(); //destruct方法触发

虽然这两个的方式差不多但是二者优先级不一样

举个栗子:
<?php
class wea5e1{
public function __construct(){
echo 'construct方法触发';
}
public function __destruct()
{
echo 'destruct方法触发';
}
}
$a = new wea5e1(); //construct方法触发destruct方法触发

tostring():

当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串 举个栗子:
<?php
class me{
public $name="wea5e1";
public function __toString()
{
return '__toString方法已触发';
}
}
$a = new me();
//echo $a;
echo "\n";
echo serialize($a);//O:2:"me":1:{s:4:"name";s:6:"wea5e1";}
取消echo $a的注释的话 //__toString方法已触发
O:2:"me":1:{s:4:"name";s:6:"wea5e1";}

wakeup():

调用unserialize()时触发,反序列化恢复对象之前调用该方法,例如重新建立数据库连接,或执行其它初始化操作。unserialize()会检查是否存在一个__wakeup()方法。如果存在,则会先调用__wakeup(),预先准备对象需要的资源。(这里有个绕过)举个栗子:
<?php
class me{
public $name="wea5e1";
public function __wakeup()
{
echo '__wakeup方法已触发';
}
}
$a = new me();
$b = serialize($a);
echo $b;
$b = unserialize($b); //O:2:"me":1:{s:4:"name";s:6:"wea5e1";}__wakeup方法已触发

sleep():

调用serialize()时触发 ,在对象被序列化前自动调用,常用于提交未提交的数据,或类似的清理操作。举个栗子:
<?php
class me {
public $name = "wea5e1";
public $age = 18;

public function __sleep() {
echo '__sleep方法已触发' . PHP_EOL;
return ['age']; // 指定要序列化的属性
}
}

$a = new me();
$b = serialize($a);
echo $b; //__sleep方法已触发
O:2:"me":1:{s:3:"age";i:18;}

call():

调用不可访问或不存在的方法时触发 举个栗子:
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __call($name, $age){
echo 'call触发';
echo "\n";
}
}
$a = new wea5e1();
$a -> sb();
echo serialize($a); //call触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}

这里先讲下get和set(这三个可能经常搞混)

get():

调用不可访问或不存在的属性是触发 和上面要区分开来 举个栗子:
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __get($name){
echo 'get触发';
echo "\n";
}
}
$a = new wea5e1();
$a -> sb;
echo serialize($a);//get触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}

set():

用于将数据写入不可访问的属性 举个栗子:
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __set($name,$age){
echo 'set触发';
echo "\n";
}
}
$a = new wea5e1();
$a -> sb = "nb";
echo serialize($a); //set触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}

isset():

当使用 isset 或者是 empty 来检查不存在或者不可访问的属性时触发 
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __isset($name){
echo 'isset触发';
echo "\n";
}
}
$a = new wea5e1();
isset($a->sb);
echo serialize($a); //isset触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}

unset():

使用 unset() 删除一个不存在或不可访问的属性时触发 举个栗子:
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __unset($name){
echo 'unset触发';
echo "\n";
}
}
$a = new wea5e1();
unset($a->sb);
echo serialize($a); //unset触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}

invoke()

当将一个对象像函数一样调用时触发 举个栗子:
<?php
class wea5e1{
public $name="wea5e1";
public $age = "18";
public function __invoke($name){
echo 'invoke触发';
echo "\n";
}
}
$a = new wea5e1();
echo $a('s');
echo serialize($a); //invoke触发
O:6:"wea5e1":2:{s:4:"name";s:6:"wea5e1";s:3:"age";s:2:"18";}