PHP反序列化
PHP反序列化
xxshh0x01 PHP
在面向对象的程序设计中,类和对象是两个非常重要的概念。类是创建对象的基础,包含了对象的结构和功能。对象是类的实例,它拥有类中定义的属性和方法。
以下是一个PHP类:
|
PHP 对属性或方法的访问控制,是通过在前面添加关键字实现的。
- public:公有的类成员可以在任何地方被访问。
- protected:受保护的类成员只能被其自身以及其父类和子类访问。
- private:私有的类成员只能被其定义所在的类访问
访问控制修饰符不同,序列化后属性的长度和属性值会有所不同,如下所示:
- public:属性值会变成属性名。
- protected:属性值会变成
\x00*\x00属性名
- private:属性值会变成
\x00类名\x00属性名
其中:\x00表示空格。
|
|
PHP中把以两个下划线__
开头的方法称为魔术方法,重点关注以下5个魔术方法:
- __ construct:构造函数,当一个对象被创建时调用。
- __ destruct:析构函数,当一个对象被销毁时调用。
- __ toString:当一个对象被当作一个字符串时使用。
- __ sleep:在对象序列化的时候调用。
- __ wakeup:对象重新醒来,即由二进制串重新组成一个对象的时候(在一个对象被反序列化时调用)。
从序列化到反序列化,这几个函数的执行过程是:
__ construct() ->__ sleep() -> __ wakeup() -> __ toString() -> __ destruct()
0x02 PHP序列化和反序列化
序列化就是把一个对象变成可以传输的字符串。反序列化就是把那串可以传输的字符串再变回对象。
以序列化json来举例。有一个数组book:
如果想传输这个数组,就调用json_encode()把这个数组序列化成一串字符串:
那么,如何把一个对象序列化成一串字符串?举个例子:
这里首先创建了一个类Demo。在实例化时,改变了其属性。PHP对象是存放在内存的堆空间段上的,PHP文件在执行结束的时候会将对象销毁。那么如果之后还要用到这个实例怎么办?解决方法就是先将它序列化存起来。序列化只序列属性,不序列方法。
按顺序解释一下输出结果。
O: object;
4: 类名的长度;
2: 2个属性;
s:4 : 第一个属性名,是一个字符串string且长度为4;
s:3 : 第一个属性对应的值,是一个字符串string且长度为3;
s:3 : 第二个属性名,是一个字符串string且长度为3;
s:3 : 第二个属性对应的值,是一个字符串string且长度为3。
用的时候再将其反序列化。
0x03 实例分析
序列化和反序列化本身没有问题,但是如果反序列化内容用户可控,且不正当地使用了魔术方法,就会导致安全问题。
[极客大挑战 2019]PHP
index.php:
class.php:
|
分析代码。由于unserialize()结束时会自动调用__destruct(),所以只要满足username=admin且password=100,即可得到flag。所以要构造一个username属性是admin且password属性是100的Name对象。
注意 username 和 password 都是 private 属性,这意味着它们只能在 Name 类的内部访问,外部代码无法直接修改这些属性。因此,尝试通过 $person->username 和 $person->password 访问会导致 PHP Fatal error。
序列化代码如下:
私有属性名称的前面需加上\0
。在URL编码中,\0
表示为 %00
。
代码中__wakeup()会将username赋值为guest,所以要想办法绕过该函数。
当成员属性数目大于实际数目时可以绕过__wakeup()。
则payload为
|