PHP反序列化漏洞是指在PHP应用程序中由于不正确地处理用户提供的序列化数据而导致的安全漏洞。这种漏洞通常出现在接受用户输入并将其反序列化为对象或数据结构的地方,比如通过unserialize()
函数。
攻击者可以利用这种漏洞在服务器上执行恶意代码,进行远程代码执行(RCE)攻击,获取系统权限,甚至完全控制受影响的服务器。
1.面向对象与面向过程的优缺点
面向对象:
优点:不仅关注眼前的事件实现,也关注未来可能发生的事件。具有高度的拓展性和复用性,特点是继承、封装、多态。
缺点:如果只是单一的功能实现,面向对象的设计思路会较为繁琐
面向过程:
优点:根据事情的目的分解出过程,再一步步实施。对于不复杂的事件执行效率快。
缺点:只关注眼前事件的实现,开发复杂功能的时候繁琐。
2.类和对象的关系
类和对象之间有密切的关系,可以简单地概括为“类是对象的蓝图,对象是类的实例”。
类(Class) 是一种抽象的数据类型,它定义了一组属性(成员变量)和方法(成员函数)。类是一种模板或蓝图,描述了对象应该具有的属性和行为。
对象(Object) 是类的实例化。当一个类被实例化时,就创建了一个对象。对象是类的具体实体,具有类定义的属性和方法。
类定义了对象的属性和方法,而对象则是这些属性和方法的实际实例。类是一种抽象的概念,它描述了对象应该有什么样的属性和行为,而对象则是具体的实例,它实际上拥有这些属性和行为。
举个例子:
class Car { // 属性 public $color; public $brand; // 方法 public function start() { echo "Car started!"; } public function stop() { echo "Car stopped!"; } } // 创建Car类的实例(对象) $myCar = new Car(); $myCar->color = "red"; $myCar->brand = "Toyota"; // 调用对象的方法 $myCar->start();
在这个例子中,Car 是一个类,它定义了汽车的属性(color 和 brand)以及行为(start() 和 stop() 方法)。当我们创建 $myCar 对象时,它就是 Car 类的一个实例,拥有类定义的属性和方法。
3.php访问修饰符
在php类中不写访问修饰符默认的访问权限为public
在 PHP 中,访问修饰符用于定义类的属性和方法的访问级别。这些修饰符包括:
-
public(公共):公共成员可以在任何地方被访问,没有访问限制
-
protected(受保护):受保护的成员只能被定义它们的类或继承该类的子类访问。外部类不能直接访问受保护成员
-
private(私有):私有成员只能被定义它们的类访问。外部类和子类都不能直接访问私有成员
这些访问修饰符可以用来控制类的封装性,确保类的内部数据不被外部类或代码直接访问和修改,从而提高代码的安全性和可维护性。在 PHP 中,访问修饰符使用关键字 public
、protected
和 private
来定义。例如:
class MyClass {
public $publicVar;
protected $protectedVar;
private $privateVar;
public function __construct() {
$this->publicVar = "Public variable";
$this->protectedVar = "Protected variable";
$this->privateVar = "Private variable";
}
public function getPrivateVar() {
return $this->privateVar;
}
}
$obj = new MyClass();
echo $obj->publicVar; // 可以正常访问
// echo $obj->protectedVar; // 无法直接访问,会导致错误
// echo $obj->privateVar; // 无法直接访问,会导致错误
echo $obj->getPrivateVar(); // 可以通过公共方法访问私有属性的值
publicVar
是公共属性,可以在任何地方访问。protectedVar
是受保护属性,只能在该类或其子类中访问。privateVar
是私有属性,只能在该类内部访问
4.php序列化和反序列化
在 PHP 中,序列化是将数据结构或对象转换为字符串的过程,而反序列化则是将这个字符串重新转换为原始的数据结构或对象的过程。这种转换通常用于在不同的应用程序之间或在不同的请求之间传递数据,或者用于将数据持久化存储
PHP 中有两个主要的函数来执行序列化和反序列化操作:
1.序列化(Serialization): 使用 serialize()
函数将数据结构或对象转换为字符串。
$data = array('name' => 'John', 'age' => 30, 'city' => 'New York');
$serializedData = serialize($data);
echo $serializedData;
在上面的例子中,$data
数组被序列化为一个字符串,并存储在 $serializedData
变量中。
2.反序列化(Unserialization): 使用 unserialize()
函数将序列化后的字符串重新转换为原始的数据结构或对象。
$serializedData = 'a:3:{s:4:"name";s:4:"John";s:3:"age";i:30;s:4:"city";s:8:"New York";}';
$unserializedData = unserialize($serializedData);
print_r($unserializedData);
在上面的例子中,$serializedData
字符串被反序列化为原始的数组,并存储在$unserializedData
变量中。
5.php魔术方法
PHP的魔术方法(Magic Methods)是一组特殊的方法,以双下划线(__)开头和结束命名的。
它们在对象的生命周期中被自动调用,用于执行特定的操作。这些魔术方法可以让开发者更好地控制和定制对象的行为
__construct(), 类的构造函数,创建对象时进行初始化操作
__destruct(), 类的析构函数,在对象被销毁(即失去对对象的所有引用)之前执行一些清理操作
__call(), 在对象中调用一个不可访问或不存在方法时调用
__callStatic(), 调用一个不可访问或不存在的静态方法时调用
__get(), 访问一个对象的不可访问或不存在属性时调用
__set(), 对不可访问属性进行赋值时调用
__isset(), 当对不可访问或不存在属性调用isset()或empty()时调用
__unset(), 当对不可访问或不存在属性调用unset()时被调用。
__sleep(), 执行serialize()之前,先会调用这个函数
__wakeup(), 执行unserialize()之后调用这个函数
__toString(), 类被当成字符串时的回应方法
__invoke(), 用于将一个对象作为函数直接调用时的行为定义。
__set_state(),设置对var_export() 函数所产生的字符串的进行反序列化操作时行为。
__clone(), 当clone 关键字复制一个对象时调用
__autoload(), 尝试加载未定义的类
__debugInfo(), 打印所需调试信息
php允许在变量后面加括号来调用方法
5.靶场
靶场环境搭建
网站:GitHub - mcc0624/php_ser_Class: php反序列化靶场课程,基于课程制作的靶场
这里用docker搭建
docker pull mcc0624/ser:1.8 #拉取镜像
docker images #显示当前存储在系统本地的镜像
docker run -d -p 8002:80 c81 #运行镜像
docker ps #列出所有正在运行的docker容器
访问IP地址:8002
192.168.168.128:8002
搭建完成
1.反序列化漏洞例题
该代码接收一个名为benben的参数然后将参数的值反序列化为对象再调用生成对象的displayVar方法
<?php highlight_file(__FILE__); error_reporting(0); class test{ public $a = 'echo "this is test!!";'; public function displayVar() { eval($this->a); } } $get = $_GET["benben"]; $b = unserialize($get); $b->displayVar() ; ?>
test对象的displayVar方法会将自身的a变量传入eval方法中,造成代码执行
只要控制a的值,即可达到任意代码执行
而题目没有对序列化过程做任何干预,直接构造a的值进行序列化后传入即可
执行1.php
PHP在反序列化的时候,它只会反序列化属性,不会反序列化行为,因为我们可以发现在类实例化到对象的过程中,只有我们的属性产生了赋值,我们的方法是不变的
2.析构函数例题
执行1.php
3.__wakeup()例题
执行1.php