Ucubug

序列化的原因。我记得好像是解决传输问题,加上不让你看懂。

学了开发之后,这些太简单了。

.

反序列化可以覆盖属性,利用原有的东西触发函数。

反序列化只能调用有的代码对不对,但是为什么可以修改原来内里面的属性?是不是相当于创建实例的时候直接修改?

反序列化的核心功能不是"修改",而是"还原对象状态"------"修改属性值""新增属性"只是还原过程的"附带效果",而非其设计初衷,具体拆解如下:

一、反序列化的本质功能:还原对象状态

序列化( serialize() )的作用是将内存中对象的状态(属性名+属性值)转化为结构化字符串(便于存储、传输);

反序列化( unserialize() )的对应功能是将这个结构化字符串,还原为内存中可操作的对象实例------它的设计目标是"精准复现对象原本的状态",而非"主动修改对象"。

简单说:序列化是"给对象拍快照",反序列化是"按快照恢复对象","恢复"才是核心,"修改"只是恢复时的可选操作(比如快照本身是"修改后"的状态)。

二、"修改"效果的本质:用"自定义快照"替代"默认状态"

你感受到的"修改",本质是攻击者构造了"篡改后的快照"(恶意序列化字符串),反序列化时按这个快照恢复对象,表现为"修改":

  1. 属性值覆盖:快照里的属性值(如 isVip=true )替代了类的默认值( isVip=false ),看似"修改",实则是恢复了快照中的状态;

  2. 新增属性:快照里包含类定义中没有的属性,反序列化时为了完整恢复快照状态,才动态新增属性------这是PHP动态类型特性的体现,而非反序列化的核心功能。

三、关键区分:功能 vs 利用场景

反序列化的核心功能 常见利用场景(表现为"修改")

还原对象的属性状态 构造恶意快照,覆盖关键属性值(如 isVip=true )

重建可操作的对象实例 新增动态属性,辅助触发类中已有代码

总结:"修改"是攻击者利用反序列化"还原状态"的功能,构造恶意快照后的"利用结果",而非反序列化本身的核心功能------它的本质是"恢复",而非"修改"。

需要我帮你梳理一份「反序列化核心功能 vs 常见利用场景」的对比表,明确两者的边界和关联吗?

这就是覆盖。

由石油改为公有是可以的。公有改成私友不知道。

这个也可以添加元素。

不是的,PHP序列化( serialize() )并非只能显示属性,序列化字符串会包含类的基础信息、属性数量与属性值,而反序列化后的对象还能调用类中的方法------你看到的输出只有属性相关内容,是因为序列化的核心是保存对象的状态(属性值),不会存储类的方法代码,具体拆解如下:

一、序列化字符串的组成(不只是属性)

从你截图的输出能看到,序列化字符串包含三类关键信息,而非仅属性:

  1. 类的基础标识: O:4:"user" 表示这是 user 类的对象( O 是Object标识, 4 是类名长度, user 是类名);

  2. 属性数量:第一行 :3: 表示对象有3个属性,第二行 :4: 表示有4个属性(含动态新增的属性);

  3. 属性详情:每个属性的"类型+长度+名称+值"(如 s:10:"username" 是私有属性名, s:6:"xiaodi" 是属性值)。

二、为什么看不到方法?方法不会被序列化

你定义的 ccc() 方法属于类的静态逻辑,而非对象的"状态":

  • 序列化的设计目标是"保存对象的属性值"(比如 name=xiaodi 、 age=31 ),而非保存方法的代码;

  • 方法是类定义时就固定的,反序列化后的对象会自动关联类的方法,所以你能调用 $demo->ccc() ,但序列化字符串里不会出现方法的任何内容。

三、反序列化后的对象能做的远不止"显示属性"

以你截图的 user 类为例,反序列化后的对象可以:

  1. 访问/修改属性:包括类定义的属性( name / sex / $age )和动态新增的属性(如截图里的 ddd );

  2. 调用类的方法:比如执行 demo-\>ccc() ,能正常运行方法内的逻辑( a=1; return 0; );

  3. 动态新增属性/方法:PHP是弱类型语言,还能给反序列化后的对象新增属性(如 $demo->newAttr = 'test' )或临时绑定方法。

四、验证:调用反序列化对象的方法(代码示例)

在你现有代码基础上添加一行,就能看到方法可正常调用:

php

<?php

class user{

private $name='xiaodi';

public $sex='man';

public $age=31;

public function ccc(){

$a=1;

return $a; // 修改返回值方便查看

}

}

$demo = new user();

s = serialize(demo);

u = unserialize(s);

// 调用方法并输出结果

echo "调用ccc()方法的返回值:" . $u->ccc(); // 输出1,证明方法可执行

?>

需要我帮你写一份序列化后调用方法+动态新增属性的完整测试代码,让你直观看到序列化不止能处理属性吗?

方法不能被序列化。

游戏内置的方法它会自动执行

不是没用,你框起来的 __construct() (构造方法)在实例化 ctfShowUser 类时会自动执行,直接改变了 $class 属性的类型和值,进而影响序列化的结果,是后续代码执行的关键铺垫,具体作用如下:

一、构造方法的核心作用

  1. 覆盖原有属性值

类中原本定义 public class = 'backDoor'; ( class 是字符串类型),而 __construct() 执行后, this-\>class = new backDoor(); 会把 class 属性从字符串改成 backDoor 类的对象实例------这是本质变化,序列化时保存的将是 backDoor 对象,而非原字符串。

  1. 为代码执行埋下入口

backDoor 类中定义了 code = "system('tac flag.php');" (执行系统命令的代码),当 class 变成 backDoor 对象后,后续只要触发相关魔术方法(如 __destruct() / __call() ),就能调用 $code 中的命令,实现代码执行。

二、验证:构造方法执行前后的属性差异

在代码中添加输出就能看到变化:

php

<?php

class ctfShowUser{

public $class = 'backDoor';

public function __construct(){

$this->class=new backDoor();

}

}

class backDoor{

public $code="system('tac flag.php');";

}

// 实例化时自动执行构造方法

$obj = new ctfShowUser();

var_dump($obj->class); // 输出:object(backDoor)#2 (1) { ["code"]=> string(20) "system('tac flag.php');" }

// 对比:如果注释构造方法,$class是字符串"backDoor"

?>

三、对序列化的影响

执行 a = serialize(new ctfShowUser()); 后,序列化字符串中 class 属性对应的是 backDoor 对象的序列化内容,而非原字符串 'backDoor' ------这是后续反序列化利用代码执行的核心前提,没有这个构造方法, $class 只是普通字符串,无法触发命令执行逻辑。

简单说,这个构造方法是**把"静态字符串"变成"可执行的对象实例"**的关键,绝非无用代码。

需要我帮你补全代码,添加触发命令执行的魔术方法,让你直观看到这个构造方法如何配合实现代码执行吗?

这个我自己实验的时候倒是提前知道了。

后面的都不会解析成字符串。

我懂他的意思了,但一开始会导致我们解析失败(如果不匹配的话从5→6会多解析导致失败,如果匹配的话,后面的东西就释放出来了。

你*的我都没注意到后面还有。

黑客直接把282位给挤占了。

逃走的那一部分就是执行的部分,后面还是得要按部就班。

后面才是攻击的东西吧,前面是自带的东西。

第1个看第1位拼接(因为额度执行完了,所以后面直接执行),第2个看第2位(固定值吞吃),他拼接执行的东西一个是1一个是2,有个东西是自带的。

这方法太奇葩了,直接可以修改属性。

这个感觉是原生类种调用这个魔术方法的方法。

可以创造已经有的类

反序列化既不能创造类/方法,也不是只能在非类的代码中使用;能否调用类方法,核心取决于目标环境中是否存在该类,以及是否满足方法的触发条件,和代码是否为类结构无关。

一、核心结论:反序列化"不能创造,只能触发已有"

  1. 无法创造类/方法

反序列化的唯一作用是将序列化字符串还原为对象实例,这个过程不会生成新的类,也不会新增/修改类的方法。所有被调用的方法,都是类提前定义好的(要么是PHP原生内置类的自带方法,要么是目标代码中自定义类的方法)。

  • 比如调用 Exception 类的 __toString ,是因为PHP环境默认自带这个类和方法;

  • 比如调用自定义 Test 类的 __destruct ,是因为目标服务器的代码里已经定义了 Test 类和该方法。

若目标环境中没有对应的类,反序列化只会生成 __PHP_Incomplete_Class 对象,无法调用任何方法。

  1. 调用方法的前提是"触发条件"

反序列化本身不会主动调用方法,而是还原对象后,满足了类方法的触发规则,才会执行方法:

  • 魔法方法( __wakeup / __destruct / __toString )有固定触发规则(如对象销毁触发 __destruct 、对象转字符串触发 __toString );

  • 普通方法需要业务逻辑主动调用(如 $obj->func() )。

这些触发规则和"代码是否是类结构"无关,只和对象的使用方式有关。

二、"不是只能在非类的代码中使用"

反序列化的执行和触发,不受代码是否为类结构的限制,只要满足条件,在类的方法里、全局脚本里、函数里都能触发类方法:

  1. 在全局脚本中触发

php

// 全局代码(非类)

class Test {

public function __destruct() {echo "触发方法";}

}

unserialize('O:4:"Test":0:{}'); // 反序列化后对象销毁,触发__destruct

  1. 在类的方法中触发

php

class Demo {

public function run() {

// 在类的方法里执行反序列化

unserialize('O:4:"Test":0:{}');

}

}

class Test {

public function __destruct() {echo "在类方法中触发";}

}

$demo = new Demo();

$demo->run(); // 同样触发Test类的__destruct

  1. 在函数中触发

php

function testFunc() {

unserialize('O:4:"Test":0:{}');

}

class Test {

public function __destruct() {echo "在函数中触发";}

}

testFunc(); // 依然触发__destruct

简单说:反序列化调用类方法的关键,是目标有这个类+满足方法触发条件,和代码是否是类结构没有关系。

需要我帮你写一个不同代码场景下反序列化触发类方法的测试代码,让你直观看到全局、类方法、函数中的触发效果吗?

框架反序列化的核心流程是 "找到入口→构造链路→触发执行→达成攻击",全程依赖框架的类机制、PHP反序列化规则和漏洞环境,以下是结合ThinkPHP/laravel等主流框架的详细分步流程(从攻击方视角拆解,含技术细节和框架特性):

一、前置准备:明确攻击目标与环境探查

在开始流程前,需先掌握目标框架的关键信息,这是后续构造利用链的基础:

  1. 确认框架类型与版本:通过网站响应头(如 X-Powered-By: ThinkPHP/5.1.41 )、报错页面、robots.txt等,确定框架是ThinkPHP、Laravel、Yii等,以及具体版本(不同版本的可利用类和漏洞点差异极大);

  2. 探查PHP环境配置:通过探针、报错信息或盲测,了解PHP版本(如PHP 7.4支持更多原生类)、 disable_functions (是否禁用 system / eval 等危险函数)、 allow_url_include 等关键配置(影响后续攻击方式);

  3. 收集框架核心类信息:下载对应版本的框架源码,梳理核心类的继承关系、魔术方法( __destruct / __wakeup / __toString )、危险方法(含 eval / file_put_contents / system 的方法)------框架的"自动加载机制"会帮攻击者自动加载这些类,无需手动引入。

二、第一步:寻找反序列化点(触发入口)

这是流程的"敲门砖",没有反序列化点,后续所有操作都无法触发。核心目标是找到 "用户可控参数 + 直接传入 unserialize() " 的调用位置:

  1. 反序列化点的3种常见来源:
  • 「开发者不安全编码」:最常见,开发者为实现数据传输/存储,直接将用户输入反序列化,如:

php

// ThinkPHP控制器中示例(开发者写的反序列化点)

public function test() {

data = _GET['payload']; // 用户可控参数

unserialize($data); // 直接反序列化,无过滤

}

  • 「框架历史漏洞」:框架自身的参数处理逻辑存在缺陷,隐式调用 unserialize() ,如:

  • ThinkPHP 5.0.x的 cookie 反序列化漏洞:框架默认对cookie值进行反序列化,且cookie参数用户可控;

  • Laravel 5.4.x的 session 反序列化漏洞:session存储的序列化字符串被未过滤反序列化。

  • 「第三方组件漏洞」:网站使用的插件/库(如支付组件、缓存组件)中存在反序列化点,如Phar反序列化(利用 file_exists() 等函数触发Phar文件的反序列化)。

  1. 验证反序列化点有效性:

构造简单的序列化字符串(如包含 echo 的测试类),传入疑似反序列化点,若能触发预期输出(如弹窗、日志记录),则确认该点可利用。

三、第二步:分析框架可利用类与构造POP链

这是流程的核心,目标是找到框架中 "魔术方法 + 危险函数"的调用链路(即POP链),框架的价值在于提供了大量"现成的类和方法",无需攻击者自建:

  1. 筛选框架中的"关键类":

从框架源码中筛选满足以下条件的类(重点看 think / Illuminate 等核心命名空间下的类):

  • 包含触发型魔术方法( __destruct / __wakeup / __toString / __call );

  • 魔术方法中调用了其他类的方法(可衔接链路);

  • 最终链路中存在危险函数( eval / system / file_put_contents / exec 等)。

框架典型可利用类示例(ThinkPHP):

  • think\template\driver\Php : __destruct() → display() → 调用 eval() 执行PHP代码;

  • think\model\Pivot : __toString() → toArray() → 调用 array_merge() (可触发异常或数据读取);

  • think\config\Config : set() 方法可写入配置,结合其他方法触发配置生效。

  1. 构造POP链的逻辑的链路:

POP链的核心是"链式调用",即: 反序列化触发魔术方法 → 调用框架中间方法 → 触发危险函数 ,以ThinkPHP执行系统命令为例,完整POP链如下:

plaintext

  1. 反序列化触发类A的__destruct() → 2. __destruct()调用类B的display()方法 → 3. display()调用eval() → 4. eval()执行system('whoami')

具体代码逻辑拆解:

  • 类A(攻击者构造的触发类):

php

class Trigger {

private $template;

public function __construct() {

// 实例化框架的危险类Php

$this->template = new think\template\driver\Php();

// 给Php类的template属性赋值(恶意代码)

$this->template->template = '<?php system("whoami"); ?>';

}

// 反序列化后脚本结束时触发__destruct()

public function __destruct() {

// 调用Php类的display()方法,衔接链路

$this->template->display();

}

}

  • 框架类 think\template\driver\Php 的核心代码(自带逻辑):

php

class Php {

public $template; // 存储模板内容(攻击者可控)

public function display() {

// 危险函数:直接执行template属性的代码

eval('?>'.$this->template);

}

}

  1. 利用框架自动加载机制:

构造POP链时,无需手动引入框架类(如 think\template\driver\Php ),因为框架的自动加载器(如ThinkPHP的 Loader::autoload() )会根据命名空间自动从磁盘加载对应的类文件------这是框架反序列化的关键便利之处,原生PHP反序列化需手动引入类。

四、第三步:处理序列化字符串的格式与转义

框架的参数处理逻辑(如过滤、转义)会破坏序列化字符串结构,需针对性处理,否则反序列化会失败:

  1. 确保序列化格式合规:

严格遵循PHP序列化格式(如对象格式 O:类名长度:"类名":属性个数:{属性内容}; ),重点检查:

  • 类名的命名空间转义:框架类的命名空间含 \ ,需转义为 \\ (若通过URL传参,需再次转义为 \\\\ ),如 think\\template\\driver\\Php ;

  • 属性长度匹配:字符串属性的长度标识必须与实际内容一致(如 s:18:"<?php system('whoami'); ?>"; );

  • 结构符号完整: {} / ; / : 等分隔符不能缺失,否则框架解析时会截断。

  1. 处理框架的输入过滤:

目标框架可能对用户输入做过滤(如 addslashes() 转义 " / ' / \ ),需针对性规避:

  • 若框架用 addslashes() ,序列化字符串中的 " 需转义为 \" ,或用单引号包裹属性值;

  • 若框架限制参数长度,需精简POP链(如减少不必要的属性),确保序列化字符串长度不超限。

  1. 生成最终payload:

实例化构造好的触发类,调用 serialize() 生成序列化字符串,示例:

php

$trigger = new Trigger();

payload = serialize(trigger);

// 若需URL传参,需URLEncode(处理特殊字符)

payload = urlencode(payload);

五、第四步:发送payload触发反序列化

将处理后的payload传入第一步找到的反序列化点,触发 unserialize() 执行:

  1. 选择传参方式:

根据反序列化点的参数类型(GET/POST/Cookie/Header)选择传参方式:

  • 若反序列化点是 unserialize($_GET['payload']) :直接拼接URL,如 http://target.com/test.php?payload=O:7:"Trigger":1:{s:10:"%00Trigger%00template";O:27:"think\template\driver\Php":1:{s:8:"template";s:26:"<?php system('whoami'); ?>";}} ;

  • 若反序列化点是Cookie(如ThinkPHP的cookie反序列化漏洞):将payload写入Cookie的对应字段(如 thinkphp_show_page_trace=payload )。

  1. 规避框架的安全防护:

部分框架会对反序列化做防护(如ThinkPHP的 deny_func_list 禁用危险函数),需针对性绕过:

  • 替换危险函数:用 exec() / shell_exec() 替代被禁用的 system() ;

  • 利用框架自身函数:用框架的内置方法(如 think\helper\Arr::get() )间接调用危险函数,绕过黑名单。

六、第五步:反序列化执行与恶意逻辑触发

当框架执行 unserialize($payload) 后,按以下流程触发恶意代码:

  1. PHP解析序列化字符串:

PHP按格式解析payload,找到 Trigger 类,框架自动加载该类(若为自定义触发类,需确保已在目标环境中存在------或通过框架漏洞引入);

  1. 实例化对象并赋值属性:

PHP创建 Trigger 类的对象,将payload中的属性值(如 $template 对象)赋值给该对象;

  1. 触发魔术方法:
  • 若POP链用 __wakeup() :反序列化时立即触发;

  • 若用 __destruct() :脚本执行结束时(所有代码执行完后)触发;

  • 若用 __toString() :当对象被当作字符串使用时(如 echo $obj )触发。

  1. 链式调用框架方法:

魔术方法调用框架类的中间方法(如 Trigger::__destruct() 调用 Php::display() ),框架的核心逻辑按正常流程执行;

  1. 执行恶意代码:

最终调用到危险函数(如 eval() ),执行 system('whoami') 等恶意逻辑,达成攻击目标(命令执行、文件读取、权限提升等)。

七、流程收尾:处理执行结果与清理痕迹

  1. 获取执行结果:

若恶意逻辑是命令执行,结果可能直接输出到页面(如 whoami 的结果),或需通过文件写入(如 file_put_contents('result.txt', shell_exec('ls')) )后读取;

  1. 清理痕迹:

若需长期控制,需删除日志(如框架的 runtime/log 目录下的访问日志)、清理临时文件(如 result.txt ),避免被管理员发现。

框架反序列化与原生PHP反序列化的核心差异

环节 原生PHP反序列化 框架反序列化

类来源 需手动引入或已定义的类 框架自带大量可利用类(自动加载)

POP链构造 依赖少量原生类,链路短 框架类方法丰富,可构造复杂长链路

安全防护 无内置防护,仅依赖PHP配置 框架可能有黑名单、参数过滤等防护

反序列化点来源 多为开发者手动调用 开发者代码 + 框架历史漏洞 + 第三方组件

关键总结

框架反序列化的核心是 "借框架的'壳'(类和方法),走自己的'路'(POP链)":框架提供了自动加载、丰富的类方法和链路,攻击者只需找到反序列化点,构造符合框架逻辑的POP链,处理好格式和转义,就能触发恶意代码执行。整个流程的关键是 "适配框架环境"------不同框架的可利用类、防护机制不同,需针对性调整POP链和payload。

我可以帮你基于ThinkPHP 5.1.x版本,写一份 完整的框架反序列化实战演示代码(含反序列化点搭建、POP链构造、payload生成和命令执行测试),需要吗?

相关推荐
kobe_OKOK_1 天前
windows 部署 django 的 方案
后端·python·django
言之。1 天前
Django流式响应
后端·python·django
安冬的码畜日常2 天前
【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)
python·测试工具·django·接口测试·postman·fastapi·api项目
青啊青斯2 天前
Django实现SSE流
django·sse
luoluoal2 天前
基于python的机器学习的文本分类系统(源码+文档)
python·mysql·django·毕业设计·源码
电商API&Tina3 天前
【电商API接口】关于电商数据采集相关行业
java·python·oracle·django·sqlite·json·php
高斯的手稿08013 天前
Django里面,多个APP的url设置,每个APP单独对应HTML设置
数据库·django·html
计算机毕设指导63 天前
基于微信小程序+django连锁火锅智慧餐饮管理系统【源码文末联系】
java·后端·python·mysql·微信小程序·小程序·django
高斯的手稿08013 天前
Django里面,多个APP的url怎么设置
django