PHP反序列化漏洞

什么是序列化与反序列化

seriallization 序列化 : 将对象转化为便于传输的格式, 常见的序列化格式:二进制格 式,字节数组,json字符串,xml字符串。

deseriallization 反序列化:将序列化的数据恢复为对象的过程。

序列化和反序列化:是为了保障脆弱的数据再互联网上完整正确传输。

序列化和反序列化这个行为原理上不存在漏洞。

开发者在使用序列化和反序列化自己添加某些功能、或是使用某些组件,若只使用官方提供的原生反序列化就不会出现反序列化漏洞。

反序列化成因:攻击者可以控制序列化后的字符串,

PHP反序列化

serialize(); 将对象序列化成字符串

unserialize(); 将字符串反序列化回对象

魔术方法

魔术方法是以双下划线(__)开头的特殊方法,它们允许对象对某些操作做出反应。

常用方法

__construct():构造函数,在创建对象时自动调用

__destruct():析构函数,在对象被销毁时调用

__get() 和 __set():在访问不可访问属性时调用

__call() 和 __callStatic():在调用不可访问方法时调用

__toString():将对象转换为字符串时调用

__isset() 和 __unset():对不可访问属性使用isset()或unset()时调用

__wakeup():unserialize()时会自动调用这个函数

__sleep():serialize()时会自动调用这个函数

魔术方法示例

<?php
class animal{
public $name;
public $age;
public function __sleep(){
if(@$_GET['cmd']=="abc"){
a();
}
}
}
$an1=new animal;
$an1->name="dog";
$an1->age=3;
//var_dump($an1);
@serialize($an1);
?>

PHP反序列化漏洞

序列化和反序列化本身是为了实现数据在网络上完整高效的传输,但是由于反序列化过程中,对象的魔术方法会自动调用,魔术方法本身调用了别的方法,最终呈现一种链式调用,直到执行任意的代码或者命令,整个过程中涉及的函数参数要前端可控

利用链: 反序列化利用过程

漏洞挖掘:反向挖掘,先从任意执行函数开始往前查找

commons-collections(JAVA大部分反序列化成因)

1、使用php声明以自己姓名缩写为例子的类

class wyh {
public $name;
public $sex;
private $age;
public $address;
}

2、实例化一个对象,使用序列化,反序列化函数操作自己的对象输出看看结果

$intf = new wyh();
$intf -> name = "wangyuhan";
$intf -> sex = "male";
//$intf -> age = "secret";
$intf -> address = "tianjin";
var_dump($intf); // 原始对象
echo "<hr >";
$ser = serialize($intf); // 进行序列化
echo $ser;
echo "<hr >";
var_dump(unserialize($ser)); // 进行反序列化

image-20250902160806221

3、根据笔记尝试使用四种魔术方法

__sleep()方法

serialize()时会自动调用这个函数

public function __sleep() {
if(@$_GET['cmd'] == 'hello') {
echo "顺利接收到参数进行序列化,开始执行 __sleep 函数" . "<br>";
}
return array('name', 'sex', 'age', 'address');
}

__wakeup()方法

unserialize()时会自动调用这个函数

public function __wakeup() {
if(@$_GET['dmc'] == 'world') {
echo "顺利接收到参数进行反序列化,开始执行 __wakeup 函数" . "<br>";
}
//return array('name', 'sex', 'age', 'address');
}

__construct()方法

在创建对象时自动调用

public function __construct() {
if(@$_GET['dmc'] == 'world') {
echo "创建了一个对象,开始执行 __construct 函数" . "<br>" ;
}
//return array('name', 'sex', 'age', 'address');
}

__destruct()方法

在销毁对象时自动调用

public function __destruct() {
if(@$_GET['dmc'] == 'world') {
echo "删除了一个对象,开始执行 __destruct 函数" . "<br>" . "<hr>";
}
//return array('name', 'sex', 'age', 'address');
}

方法使用测试

<?php
header('Content-Type: text/html; charset=utf-8');
class wyh {
public $name;
public $sex;
private $age;
public $address;

public function __sleep() {
if(@$_GET['cmd'] == 'hello') {
echo "顺利接收到参数进行序列化,开始执行 __sleep 函数" . "<br>";
}
return array('name', 'sex', 'age', 'address');
}

public function __wakeup() {
if(@$_GET['dmc'] == 'world') {
echo "顺利接收到参数进行反序列化,开始执行 __wakeup 函数" . "<br>";
}
}

public function __construct() {
echo "创建了一个对象,开始执行 __construct 函数" . "<br>" ;
}

public function __destruct() {
echo "删除了一个对象,开始执行 __destruct 函数" . "<br>" . "<hr>";
}
}

$intf = new wyh();
$intf -> name = "wangyuhan";
$intf -> sex = "male";
//$intf -> age = "secret";
$intf -> address = "tianjin";
echo "输出原始对象:" . "<br>";
var_dump($intf); // 原始对象
echo "<hr >";
echo "输出序列化后的信息" . "<br>";
$ser = serialize($intf); // 进行序列化
echo $ser;
echo "<hr >";
echo "输出反序列化后的对象" . "<br>";
$fint = unserialize($ser);

var_dump($fint); // 进行反序列化
echo "<br>" . "<hr>";


?>

image-20250902163638972

4、安装idea和jdk,新建一个文件,使用java输出自己的名称

package test_java;

public class helloworld {

public helloworld() {
// TODO Auto-generated constructor stub
}

public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("welcome");
print_hello();
}

public static void print_hello() {
System.out.println("你好王雨晗");
}

}

image-20250902165836056

5、定义一个函数,调用结果就是输出自己的名称

public class Main {
public static void main(String[] args) {

System.out.printf("Hello and welcome!");

for (int i = 1; i <= 5; i++) {
print_hello();
}
}

public static void print_hello() {
System.out.println("你好王雨晗");
}
}

image-20250902170541074

扩展:尝试在java中定义父类和子类,让子类使用父类的任意方法

package test_java;

public class helloworld {

public helloworld() {
// TODO Auto-generated constructor stub
}

public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("welcome");
print_hello();

fa_class fath = new fa_class();
fath.set_name("John Wick");
fath.set_numb(1234);
System.out.println("默认年龄" + fath.get_age() + ";姓名" + fath.name + ";编号" + fath.st_numb);

// fa_class fth = new fa_class(24, "brother", 1145);
son fa_son = new son(24, "brother", 1145);
fa_son.set_numb(1919);
System.out.println("年龄" + fa_son.get_age() + ";姓名" + fa_son.name + ";编号" + fa_son.st_numb);
}

public static void print_hello() {
System.out.println("你好");
}

}

class fa_class {
private int age;
public String name;
protected int st_numb;

public fa_class() {
age = 18;
name = "java";
st_numb = 20250902;
}

public fa_class(int def_age, String def_name, int def_numb) {
this.age = def_age;
this.name = def_name;
this.st_numb = def_numb;
}

public int get_age() {
return age;
}

public void set_name(String new_name) {
name = new_name;
}

public void set_numb(int number) {
st_numb = number + 1;
}
}

class son extends fa_class {
public son(int def_age, String def_name, int def_numb) {
super(def_age, def_name, def_numb);
}

@Override
public void set_numb(int number) {
st_numb = number - 1;
}
}

输出

image-20250902191944297

扩展:回到如下问题:

什么是反序列化漏洞,php的反序列化漏洞是什么

反序列化漏洞

反序列化过程中,攻击者可控的恶意数据被程序信任并还原为对象。当反序列化逻辑未对输入进行严格验证时,攻击者可构造恶意数据,触发非预期的代码执行(篡改)或数据篡改。

php反序列化漏洞

序列化和反序列化本身是为了实现数据在网络上完整高效的传输,但是由于反序列化过程中,对象的魔术方法会自动调用,魔术方法本身调用了别的方法,最终呈现一种链式调用,直到执行任意的代码或者命令,整个过程中涉及的函数参数要前端可控

了解java涉及的文件后缀名称分别是什么意思

.java后缀文件

存储 java 代码的源文件,这些文件可以被文本编辑器打开并查看或修改代码。

.class后缀文件

.java后缀文件经过 javac 编译后的字节码文件, .class 文件包含Java虚拟机(JVM)可以执行的指令,它们是平台无关的,这意味着它们可以在任何安装了JVM的系统上运行。

它在平台无关性方面的任务是:为Java程序提供独立于底层主机平台的二进制形式的服务。

.jar后缀文件

JAR(Java ARchive,Java 归档)是一种与平台无关的文件格式,用于将多个文件合成一个文件。JAR 文件通常用于聚合大量的 Java 类文件、相关的元数据和资源(如文本、图片等),以便开发 Java 平台应用软件或库。

JAR 文件格式以流行的 ZIP 文件格式为基础。与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR。

.war后缀文件

Web 应用归档文件。类似 .jar,但专门用于打包 Java Web应用程序(如 Servlet, JSP, HTML, CSS, JS 等)。它的内部结构遵循 Servlet 规范,包含 WEB-INF/ 目录、web.xml 等,用于部署到 Servlet 容器(如 Tomcat, Jetty)。

.ear后缀文件

企业级应用归档文件。用于 J2EE/Java EE 应用程序,可以包含一个或多个 .war 文件、.jar 文件以及企业级相关的配置文件(如 application.xml)。用于部署到完整的 Java EE 应用服务器(如 WebLogic, WebSphere)。

Author: wickt42
Link: http://example.com/2025/09/02/PHP反序列化漏洞/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.