php
反序列化
按照我学习的次序,慢慢的已经说到了反序列化了。
这个我个人觉得还是比较简单的,因为它大部分已经不是你可以操作的了,都是PHP
这个语言自已已经预定义好的函数。
那么先说说序列化
到底是个什么东西呢,在互通互联的这个时代,光纤等之间传播的是字节流,根据一定规则形成的字节流。序列化正是为了能够更加方便传输而设计的,将数据结构转变成字节进行传递,接收后还可以反序列化进行还原,虽然失去了一些信息。但只要有相同的模板
,表明的是相同的对象,一切就都还原了。
可能我说的不是很明白,为了讲明白我上面所说的东西。简单给大家说一下面向对象思想,PHP
支持这种思想的编程,因此如果不好好考虑周全,可能会出现反序列化漏洞。
面向对象程序开发思想
在我看来,这种思想相对于面向过程开发,注重的是抽离出抽象的概念,赋予它相关的属性,他的功能。对于这整个的抽象概念我们成为其类
,也叫class
。
比如,人这个概念。抽象出来,就是他有属性:年龄、身高、性别等等,他的功能是:吃饭、睡觉等等。
其中属性我们可以叫它属性
或者数据
,功能我们叫它函数(功能)
或者方法
。
下面我用PHP
写一个类,供大家观赏,理解一下。
php
<?php
class man{
public $name;
public $age;
public function eat(){
echo "eating";
}
}
当然php
也有相应的静态属性和静态变量,所不同的地方就是添加static
关键词还有静态属性,使用::
并带有$
来调用,静态方法不需要加$
但要加前者。
而动态的变量在类外面引用的时候不需要在变量前添加$
,比如:$who=new man(); $who->name="Raya";
这种方式。
对类当中的方法调用时:$who->eat();
来完成。
有了这些前置的知识,接下来要说一说序列化和反序列化了,这在网络传输当中经常使用到(或许还有其他用途,我目前知识并不清楚)。在序列化之后(以php举例,其他语言也有序列化函数,只不过有些不同),会生成一段规则的字符串,例如:
php
O:6:"person":3:{s:4:"name";s:5:"lihua";s:3:"age";i:20;s:6:"height";i:170;}
以上就是被一个初始化的person
类进行的序列化,我们会注意到里面除了可以依稀辨别的字符串和数值,还有其他的一些信息,比如o或者s
之类,它们是用于携带原信息之外的信息的(如果有兴趣可以去查以下对应表)。
描述它们的不同数据类型,后面跟着的一个整数表示后面字符串的所占的字节数(我认为是这样的),需要一一对应,不然可能无法还原成功。
其序列化使用的函数叫:serialize()
,反序列化的叫:unserialize()
。
还有一个问题就是,当我们序列化之后还要看得就是那些变量的访问权限,会有public、protected、private
以及缺省不写(会默认public级别)这几种。关于public这种没关系,剩下两种的序列化字符串当中存在一些不可打印字符:protected 修饰的属性被序列化的时候属性值会变成%00*%00属性名
;private 修饰的属性被序列化的时候属性值会变成%00类名%00属性名
。
这时候需要对其进行url编码
或者base64编码
来传输,不然会出现数据丢失,导致错误。
这里要说的还有一点就是,php当中,以及其他的某些编程语言当中会存在一些魔术方法
,它们不需要显示的调用,达到一定条件之后自动触发,程序员可自定义书写触发内容。
这也就是漏洞的成因,如果通过构造特定的类对象,就可能会出现严重的危险。
它们通常以两个下划线开头,比如php类的构造方法就是如此。
- __construct(),类的构造函数
- __wakeup(),执行unserialize()时,先会调用这个函数
- __destruct(),类的析构函数,允许在销毁一个类之前执行的一些操作或完成一些功能,通常程序结束时会触发
- __call(),在对象中调用一个不可访问方法时调用
- __get(),获得一个类的非公有成员变量时调用
- __set(),设置一个类的成员变量(非公有)时调用
- __toString(),对象被当成字符串处理时的方法
- __invoke(),调用函数的方式调用一个对象时的回应方法
- __callStatic(),用静态方式中调用一个不可访问方法时调用
- __isset(),当对不可访问属性调用isset()或empty()时调用
- __unset(),当对不可访问属性调用unset()时被调用。
- __sleep(),执行serialize()时,先会调用这个函数
- __set_state(),调用var_export()导出类时,此静态方法会被调用。
- __clone(),当对象复制完成时调用
- __autoload(),尝试加载未定义的类
- __debugInfo(),打印所需调试信息
以上是一些相关的魔术方法,大多数都有执行的条件,其中有两个是只要定义就会被执行的:__wakeup()还有__destruct()。
大部分的方法无法跳过,满足条件自动执行,但是__wakeup()函数如果反序列化出现的对象存在问题就会跳过执行,有时会使用到这个知识点。
由于php是一个弱类型语言,我们甚至可以对类中未赋值的变量赋值任何类型的值,甚至是一个类。
如果是在解这方面题,首先要去看是什么语言,遵守其序列化和反序列化的规则,其实就是构造一条长长的反序列化漏洞传递链条,先尝试找到可以自动触发的魔术方法,没有再去看其他方法有无可能,之后根据语言的操作内容构造一个以上的类对象,将其序列化然后传入相应位置,让代码执行。
我想说的就是这么多,剩下的就是做题之类的找到相应的感觉。
但这里主要是总结知识点,做题部分如果有机会我会发布一个新的文章,一定会有的。