1.序列化/反序列化
序列化:对象转化为字节流
反序列化:字节流转化为对象
二者相互结合,可以轻松的存储和传输数据,使程序更具维护性
2.反序列化漏洞
原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果
反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通
3.PHP反序列化
PHP反序列化漏洞:也叫PHP对象注入,就是当程序在进行反序列化时,会自动调用一些函数,但是如果传入函数的参数可以被用户控制的话,用户可以输入一些恶意代码到函数中,从而导致反序列化漏洞。
可以理解为程序在执行unserialize()函数时,自动执行了某些魔术方法(magic method),而魔术方法的参数被用户所控制,这就会产生安全问题
漏洞利用条件:
Unserialize()函数的参数可控
存在可利用的魔术方法
4.PHP序列化:对象转化为字符
以上格式为: 类型:长度:内容 例如上例为类型O长度4内容Girl
2代表有两种属性,第一个是姓名,第二个是年龄,后面分别跟着属性值,如果是2,即应该有四部分内容,3的话则是6部分内容
常见的表示类型的字符:
O:类
a:数组(array)
b:布尔(boolean)
i:整型
s:字符串
N:Null
d:double,浮点型
5.PHP反序列化:字符转化为对象
6.利用xss
7.访问控制符public/protected/private
var和public:var 和 public 声明的字段都是公共字段,因此它们的字段名的序列化格式是相同的。公共字段的字段名按照声明时的字段名进行序列化,但序列化后的字段名中不包括声明时的变量前缀符号¥。
protected:声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的\0表示 ASCII 码为0的字符,属于不可见字符,因此该字段的长度会比可见字符长度大3。
private:声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,字段名前面会加上\0<declared class name>\0前缀。这里 <declared class name> 表示的是声明该私有字段的类的类名,而不是被序列化的对象的类名。因为声明该私有字段的类不一定是被序列化的对象的类,而有可能是它的祖先类。
8.魔术方法
常见的有:
__construct( ) //对象创建(new)时会自动调用
__destruct( ) //对象被销毁时触发
__toString( ) //把对象当作字符串使用时触发
__wakeup( ) //使用unserialize时触发
__sleep( ) //使用serialize时触发
__call( ) //在对象上下文中调用不可访问的方法时触发
__callStatic( ) //在静态上下文中调用不可访问的方法时触发
__get($key) //用于从不可访问的属性读取数据,$key就是不存在的属性
__set( ) //用于将数据写入不可访问的属性
__isset( ) //在不可访问的属性上调用isset()或empty()触发
__unset( ) //在不可访问的属性上使用unset()时触发
__clone( ) //当克隆对象是调用
__invoke( ) //当脚本尝试将对象调用为函数时触发
__autoload( ) //在代码中当调用不存在的类时会自动调用该方法。
(1)案例1
(2)案例2
(3)案例3
写入攻击口令后会自动生成一个a.php文件,我们写入phpinfo然后对该文件进行访问发现进入了后台
9.属性赋值
案例4
方法一:直接在属性中赋值(缺点是只能赋值字符串)
方法二:外部赋值(缺点只能操作public属性)
对于php7.1+的版本,反序列化对属性类型不敏感。尽管题目的类下的属性可能不是public,但是我们可以本地改成public,然后生成public的序列化字符串。由于7.1+版本的容错机制,尽管属性类型错误,php也认识,也可以反序列化成功。基于此,可以绕过诸如\0字符的过滤。
方法三:构造方法赋值