学习参考视频:
【PHP反序列化漏洞学习】 https://www.bilibili.com/video/BV1R24y1r71C/?p=9&share_source=copy_web&vd_source=fa79ddb9fdac5181b0802bd89381beb6
面向对象与面向过程
面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个“对象”;对象是一个由信
息及对信息进行处理的描述所组成的整体,是对现实世界的抽象。
面向过程是一种以“整体事件”为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用
函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。
类的定义
类是定义了一件事物的抽象特点,它将数据的形式以及这些数据上的操作封装在一起。
对象是具有类类型的变量,是类的实例化。类是对象的抽象。
内部构成:成员变量(属性)+成员函数(方法)
继承:
继承性是子类自动共享父类数据结构和方法的机制,是类之间的一种关系。
在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把一个已经存在的类所
定义的内容作为自己的内容,并加入若干新的内容。
父类:一个类被其它类继承,可将该类成为父类,或基类,超类
子类:一个类继承其他类称为子类,也可称为派生类。
结构:
类:定义类名、定义成员变量(属性)、定义成员函数(方法)
实例化与赋值
类的修饰符:
在类中直接声明的变量称为成员属性(也可以成为成员变量)
可以在类中声明多个变量,即“对象”中可以有多个成员属性,每个变量都存储“对象”不同的属性信息。
访问权限修饰符:对属性的定义
常用访问权限修饰符:
public:公共的,在类的内部、子类中或者类的外部都可以使用,不受限制;
protected:受保护的,在类的内部、子类中可以使用,但不能在类的外部使用;
private:私有的,只能在类的内部使用,在类的外部或者子类中都无法使用。
序列化的作用
序列化(Serialization)是将对象的状态信息(属性)转换为可以存储或传输的形式的过程。
serialize 将对象格式化成有序的字符串
将对象或者数组转化为可储存/传输的字符串。
在php中使用函数serialize()来将对象或者数组进行序列化,并返回一个包含字节流的字符串来表
示。
表达方式
可以用下面这段来代码来查看
所有格式第一位都是数据类型的英文字母简写。
对象的序列化
private私有属性序列化时在变量名前加“%00类名%00”
protected受保护属性序列化时在变量名前加“%00*%00”
反序列化
unserialize 将字符串还原成原来的对象
特性
1.反序列化之后的内容为一个对象:
2.反序列化生成的对象里的值,由反序列化里的值提供;与原有类预定义的值无关:
3.反序列化不触发类的成员方法;需要调用方法后才能触发:
作用
将序列化后的参数还原成实例化的对象。
成因
反序列化漏洞的成因:反序列化过程中,unserialize()接收的值(字符串)可控;通过更改这个值
(字符串),得到所需要的代码,即生成的对象的属性值。
魔术方法
触发前提:魔术方法所在类(或对象)被调用
魔术方法:一个预定义好的,在特定情况下自动触发的行为方法。
魔术方法在特定条件下自动调用相关方法,最终导致触发代码。
动作不同,触发的魔术方法也不同
一些特殊魔术方法会传参
1 __construct(),类的构建函数
构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法;
功能:提前清理不必要内容
在序列化和反序列化过程中不会触发。
2 __destruct(),类的析构函数
析构函数,在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法,
3 __call(),在对象中调用一个不可以访问方法时调用
触发时机:调用一个不存在的方法
参数:2个参数传参$arg1,$arg2
返回值:调用的不存在的方法的名称和参数
$arg1:调用的不存在的方法的名称
$arg2:调用的不存在的方法的参数
4 __callStatic(),用静态方式中调用一个不可以访问方法时调用
触发时机:静态调用或调用成员常量时使用的方法不存在
参数:2个参数传参$arg1,$arg2
返回值:调用的不存在的方法的名称和参数
5 __get(),获得一个类的成员变量时调用
触发时机:调用的成员属性不存在
参数:传参$arg1
返回值:不存在的成员属性的名称
6 __isset(),当对不可访问属性调用isset()或empty()时调用
触发时机:对不可访问属性使用isset()或empty0时,isset)会被调用。
参数:传参$arg1
返回值:不存在的成员属性的名称
7 __set(),设置一个类的成员变量时调用
触发时机:给不存在的成员属性赋值
参数:传参$arg1,$arg2
返回值:不存在的成员属性的名称和赋的值
8 __unset(),当对不可访问属性调用unset()时被调用
9 __sleep(),执行serialize()时,先会调用这个函数
序列化serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组
如果该方法未返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误。
10 __wakeup(),执行unserialize()时,先会调用这个函数
unserialize()会检查是否存在一个wakeup()方法。
如果存在,则会先调用wakeup()方法,预先准备对象需要的资源。
预先准备对象资源,返回vod,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。
11 __toString(),类被当成字符串时的回应方法
常用于构造POP链接
把对象被当成字符串调用
把类User实体化共赋值给Stest,此时test是个对象调用对象可以使用print_r或者var_dump
如果使用echo或者print,只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发__toString()
12 __invoke(),调用函数的方式调用一个对象时的回应方法
字符串被当成对象调用
13 __set_state(),调用var_export()导出类时,此静态方法被调用
14 __clone(),当对象复制完成时调用
触发时机:当使用clone关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法__clone()
15 __autoload(),尝试加载未定义的类
16 __debugInfo(),打印所需调试信息
分隔符
反序列化以;}结束,后面的字符串不影响正常的反序列化
pop链
在反序列化中:我们能控制的数据就是对象中的属性值(成员变量),所以在PHP反序列化中有一种漏洞利用方法叫"面向属性编程",即POP(Property Oriented Programming).
POP链就是利用魔术方法在里面进行多次跳转然后获取敏感数据的一种payload。
通常我们是使用逆向分析的方法来构造pop链
POC(全称:Proof of concept)。在安全界可以理解成漏洞验证程序。PoC是一段不完整的程序,仅仅是为了证明提出者的观点的一段代码。
字符串逃逸
属性逃逸:一般在数据先经过一次serialize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候有可能存在反序列化属性逃逸。
字符减少
反序列化字符串减少逃逸:多逃逸出一个成员属性
第一个字符串减少,吃掉有效代码,在第二个字符串构造代码
字符减少就是后端对我们输入的序列化后的字符进行替换称为长度更短的字符
如下:
前面需要被吃掉20个字符,吃掉一个system()8个,加上abc最少要吃掉3个system()一共吃掉27个字符,多吃掉7个,所以还需要在V2里补充7个字符。
字符增多
反序列化字符串增多逃逸:构造出一个逃逸成员属性
第一个字符串增多,吐出多余代码,把多余位代码构造成逃逸的成员属性
wakeup魔术方法绕过
如果存在__wakeup()方法,调用unserilize()方法前则先调用__wakeup()方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过__wakeup()的执行
引用
类似于c语言里的指针,但又有所不同,删掉指针后,里面的内容也会消失。但删掉引用,相当于只删掉了一个快捷方式。
session反序列化
当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp)。
常用的存储方式有三种,默认情况下用php格式储存
session反序列化漏洞产生原理:写入格式和读取格式不一致
Phar反序列化
PHAR("Php ARchive'")是PHP里类似于JAR的一种打包文件。
对于pHP5.3或更高版本,Phar后缀文件是默认开启支持的,可以直接使用它。
文件包含:phar伪协议,可读取phar文件。
结构
Phar协议解析文件时,会自动触发对manifest字段的序列化字符串进行反序列化
漏洞原理
manifest压缩文件的属性等信息,以序列化存储;存在一段序列化的字符串;
调用phar伪协议,可读取.phar文件;
Phar协议解析文件时,会自动触发对manifest字段的序列化字符串进行反序列化。
Phar需要PHP>=5.2在php.ini中将phar.readonly设为of(注意去掉前面的分号)
使用条件
1.phar文件能上传到服务器端;
2.要有可用反序列化魔术方法作为跳板;(__wakeup()或__destruct())
3.要有文件操作函数,如file_exists(),fopen(),file_get_contents()
4.文件操作函数参数可控,且:、/、pha等特殊字符没有被过滤