反序列化初解
字数 923 2025-08-10 19:49:08

反序列化漏洞原理与实例详解

1. PHP反序列化漏洞

漏洞成因

PHP中的反序列化漏洞通常出现在魔术方法被不当使用时。在提供的示例中,__sleep()魔术方法被用来执行危险操作:

class VulnerableClass {
    public function __sleep() {
        if (isset($_GET['cmd']) && $_GET['cmd'] == 'xiu') {
            system('calc'); // 执行Windows计算器
        }
        return [];
    }
}

关键点

  • __sleep()方法在对象被序列化时自动调用
  • 直接执行用户可控参数传入的系统命令
  • 导致任意命令执行风险

2. Java类继承与重写

类继承示例

public class Person {
    public int age;
    public String name;
    
    public void talk() {
        System.out.println("Person 说话了");
    }
}

public class Student extends Person {
    public int score;
    
    @Override
    public void talk() {
        System.out.println("Student 说话了");
    }
}

关键点

  • Student类继承Person类的属性和方法
  • 使用@Override注解显式表示方法重写
  • 子类可以扩展父类的功能

方法重写测试

public class Test {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.talk(); // 输出"Student 说话了"
        
        Person p1 = new Person();
        p1.talk(); // 输出"Person 说话了"
    }
}

关键点

  • 子类重写的方法会覆盖父类方法
  • 实现了多态性
  • 运行时根据实际对象类型调用相应方法

3. Java序列化与反序列化基础

基本序列化实现

import java.io.*;

public class Person implements Serializable {
    public int age;
    public String name;
    
    private void readObject(ObjectInputStream in) 
        throws IOException, ClassNotFoundException {
        // 默认反序列化操作
        in.defaultReadObject();
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.age = 18;
        p.name = "xiu";
        
        serialize(p, "xiu.bin");
        System.out.println("反序列化结果:" + deserialize("xiu.bin"));
    }
    
    public static void serialize(Object obj, String filePath) throws IOException {
        try (FileOutputStream fileOut = new FileOutputStream(filePath);
             ObjectOutputStream objectOut = new ObjectOutputStream(fileOut)) {
            objectOut.writeObject(obj);
        }
    }
    
    public static Object deserialize(String filePath) 
        throws IOException, ClassNotFoundException {
        try (FileInputStream fileIn = new FileInputStream(filePath);
             ObjectInputStream objectIn = new ObjectInputStream(fileIn)) {
            return objectIn.readObject();
        }
    }
}

关键点

  • 实现Serializable接口使类可序列化
  • readObject方法在反序列化时自动调用
  • 序列化使用ObjectOutputStream
  • 反序列化使用ObjectInputStream

4. Java反序列化漏洞原理

危险的反序列化实现

import java.io.*;

public class Person implements Serializable {
    public int age;
    public String name;
    
    private void readObject(ObjectInputStream in) 
        throws IOException, ClassNotFoundException {
        Runtime.getRuntime().exec("calc"); // 执行计算器
        in.defaultReadObject(); // 默认反序列化操作
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.age = 18;
        p.name = "xiu";
        
        serialize(p, "xiu.bin");
        System.out.println("反序列化结果:" + deserialize("xiu.bin"));
    }
    
    // serialize和deserialize方法同上
}

关键点

  • 重写readObject方法时插入恶意代码
  • 反序列化时会自动执行readObject中的代码
  • 导致任意命令执行漏洞
  • 攻击者可以构造恶意序列化数据触发漏洞

5. 反序列化漏洞攻击示例

漏洞类定义

import java.io.Serializable;

public class ExploitMe implements Serializable {
    private String data;
    
    public ExploitMe(String data) {
        this.data = data;
    }
    
    public String getData() {
        return data;
    }
    
    private void readObject(java.io.ObjectInputStream in) 
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        System.out.println("Executing command: " + data);
        Runtime.getRuntime().exec(data); // 执行data中的命令
    }
}

攻击者构造恶意序列化数据

import java.io.*;
import java.nio.file.*;

public class Exploit {
    public static void main(String[] args) throws Exception {
        // 创建包含恶意命令的对象
        ExploitMe maliciousObject = new ExploitMe("calc.exe");
        
        // 序列化恶意对象
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(maliciousObject);
        objectOutputStream.close();
        byte[] serializedData = byteArrayOutputStream.toByteArray();
        
        // 存储到文件中
        Files.write(Paths.get("malicious_data.bin"), serializedData);
        
        // 模拟目标系统反序列化
        byte[] receivedData = Files.readAllBytes(Paths.get("malicious_data.bin"));
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(receivedData);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        ExploitMe deserializedObject = (ExploitMe) objectInputStream.readObject();
    }
}

关键攻击流程

  1. 攻击者创建包含恶意命令的ExploitMe对象
  2. 序列化该对象为字节序列
  3. 将序列化数据发送到目标系统(如通过文件、网络等)
  4. 目标系统反序列化数据时自动执行恶意命令

6. 防御措施

  1. 输入验证:不要反序列化不受信任的数据
  2. 白名单验证:使用ObjectInputFilter限制可反序列化的类
  3. 避免危险方法:不要在readObject中执行危险操作
  4. 使用替代方案:考虑使用JSON等更安全的序列化格式
  5. 更新依赖:及时修复已知漏洞的第三方库

总结

反序列化漏洞是一种严重的安全威胁,攻击者可以通过构造恶意序列化数据在目标系统上执行任意代码。理解其原理对于开发安全应用至关重要。开发者应当遵循安全编码实践,避免直接反序列化不受信任的数据,并对序列化操作实施严格的安全控制。

反序列化漏洞原理与实例详解 1. PHP反序列化漏洞 漏洞成因 PHP中的反序列化漏洞通常出现在魔术方法被不当使用时。在提供的示例中, __sleep() 魔术方法被用来执行危险操作: 关键点 : __sleep() 方法在对象被序列化时自动调用 直接执行用户可控参数传入的系统命令 导致任意命令执行风险 2. Java类继承与重写 类继承示例 关键点 : Student 类继承 Person 类的属性和方法 使用 @Override 注解显式表示方法重写 子类可以扩展父类的功能 方法重写测试 关键点 : 子类重写的方法会覆盖父类方法 实现了多态性 运行时根据实际对象类型调用相应方法 3. Java序列化与反序列化基础 基本序列化实现 关键点 : 实现 Serializable 接口使类可序列化 readObject 方法在反序列化时自动调用 序列化使用 ObjectOutputStream 反序列化使用 ObjectInputStream 4. Java反序列化漏洞原理 危险的反序列化实现 关键点 : 重写 readObject 方法时插入恶意代码 反序列化时会自动执行 readObject 中的代码 导致任意命令执行漏洞 攻击者可以构造恶意序列化数据触发漏洞 5. 反序列化漏洞攻击示例 漏洞类定义 攻击者构造恶意序列化数据 关键攻击流程 : 攻击者创建包含恶意命令的 ExploitMe 对象 序列化该对象为字节序列 将序列化数据发送到目标系统(如通过文件、网络等) 目标系统反序列化数据时自动执行恶意命令 6. 防御措施 输入验证 :不要反序列化不受信任的数据 白名单验证 :使用 ObjectInputFilter 限制可反序列化的类 避免危险方法 :不要在 readObject 中执行危险操作 使用替代方案 :考虑使用JSON等更安全的序列化格式 更新依赖 :及时修复已知漏洞的第三方库 总结 反序列化漏洞是一种严重的安全威胁,攻击者可以通过构造恶意序列化数据在目标系统上执行任意代码。理解其原理对于开发安全应用至关重要。开发者应当遵循安全编码实践,避免直接反序列化不受信任的数据,并对序列化操作实施严格的安全控制。