课堂笔记-8月31日
课堂笔记-8月31日:PHP Session 机制与 ThinkPHP 5.2 反序列化利用链实战
来源:
8_31/录音.txt| 日期:2026-08-31 | 主题:Session 机制与反序列化、CSRF/SSRF、SQL 注入防御,以及以 ThinkPHP 5.2 反序列化 RCE 利用链为核心的攻防实战
〇、今日主线
今天是一堂以秋招面试为导向的攻防技术课。课程从 PHP 的 Session 机制与反序列化讲起,先讲清 Session 存取原理与"存取错位"型漏洞,再串起 CSRF、SSRF、SQL 注入三大常考漏洞,最后用一条完整的 ThinkPHP 5.2 反序列化 RCE 利用链(phar 伪协议 → 正则回溯写文件 → MySQL LOAD DATA 触发 → 命令执行)作压轴。
讲师明确:漏洞讲解优先级由面试出题频率决定——SQL 注入最常考,SSRF 因与 Redis 关联且红队需求不可省,云安全(容器逃逸)今年起重新纳入考纲。简历与就业建议见文末附录。
一、开篇:面试导向与魔术方法热身
- 老师开场强调:面试收不到就等于没展示机会,简历要会"夸张"且能自圆其说(参考前两天两同学例子),要自信、能展示自己。
- 前面讲的四个简单小例子是为了帮助理解魔术方法(Magic Methods)的调用时机。关键对应:入口一般是
__construct(初始化处进)——相当于"门";出发点一般是__destruct等魔术方法——相当于"开关"(被拉响才执行)。难点在把"触发位置、入口、出发点"三者对应起来。注:Session 反序列化虽存在但实际用得不多。
二、PHP Session 机制详解
1. Session ID 的双重身份
第一次访问网站调用 session_start() 会创建唯一的 Session ID,它同时:作为用户身份标识(存进客户端 Cookie);作为服务端 Session 文件的文件名(默认文件存储)。类比图书馆借书号——号给你当身份凭证,也贴在书袋上当文件名方便找记录。
2. Session 请求全过程
客户端请求 → Apache 找到请求文件 → PHP 模块解析
├─ 已有 Session ID → 按 ID 找 Session 文件 → 反序列化 → 填充 $_SESSION 全局变量(存内存)
└─ 无 Session ID → 新建 Session 文件
程序结束时 → 数据写回 Session 文件(磁盘,非内存)
- 首次登录:服务端生成 Session ID 并建同名文件,把 ID 发给客户端存 Cookie。
- 再次访问:Cookie 带着 ID 来,程序找同名文件——找到直接登录,不分配新 Session;没找到/过期则重建 ID 和文件,把新 ID 发回客户端重存。
- 区分:Session 文件是磁盘存储,
$_SESSION全局变量是内存中的全局数组,靠反序列化打通。文件里存的是序列化后的数组数据。
3. Session 处理相关配置项(PPT 重点)
| 配置项 | 作用 | 备注 |
|---|---|---|
session.save_handler |
决定 Session 存到哪(文件/Redis/自定义) | 默认文件存储,Windows 下存于 tmp |
session.serialize_handler |
决定 谁来序列化 Session 内容 | 不止一个,TP 框架中有三个,默认其一 |
session.upload_progress.enable |
文件上传时把进度存进 Session | 默认开启,控制进度条 1%→100% |
session.upload_progress.cleanup |
上传完成后自动清除进度数据 | 默认开启,利用时需先关掉 |
- 易混淆点:
save_handler管"存哪",serialize_handler管"谁序列化",两者不同。 - 自定义存储:自定义类需继承 PHP 的 Session 接口,实现关闭、销毁、设生存期、打开、存储、读写等方法。
upload_progress相关配置即"裸文件包含"章节讲过的,后来开启做条件竞争利用。
4. 利用 Session 文件拿 Shell(一句话木马 + 条件竞争)
思路:upload_progress 字段值本应是上传进度,但可以伪造——改成一句话木马后写进 Session 文件,包含该文件即可拿 Shell。
<?php @eval($_POST['cmd']); ?> // 伪造的上传进度值(写入 session 文件后包含)
易错点:包含常失败是因为 cleanup 开启会自动清空 Session 内容——必须先关掉 cleanup,再开启做条件竞争才能成功。不同 PHP 版本(如 7.3/7.4)的 Session 属性可能有差异,写法以当前环境为准。
三、Session 存储引擎与"差异化"反序列化漏洞
1. 三种存储格式对比
PHP 的 Session 序列化引擎有多种,存储形态直接暴露了用的是哪种引擎:
| 引擎 | 存储格式 | 特征 | 安全性 |
|---|---|---|---|
php |
`键名 | 序列化值(如 username |
s:5:"admin"`) |
php_serialize |
整个 Session 做数组序列化 | 无竖线 | 相对安全 |
php_binary |
长度 ASCII 字符 + 键名 + 序列化值 | 开头是不可见控制符 | 难利用 |
- 判据:ASCII 可见字符范围是 32–126——文件里出现不可见字符(如 7/8/9/10 控制符)即二进制引擎;看到
键名|值结构则是php引擎。键名写入方式之一:键名长度对应 ASCII 字符(如username长度 8,先写 ASCII 8 再拼键名)。
2. 竖线(|)歧义是漏洞核心
php 引擎存储时竖线只是普通字符,但读取时把竖线当分隔符(前面键名,后面序列化的值)。若提交的数据自带竖线,取数据时就会解析歧义——值里的竖线被误认成字段分隔,数据错位、反序列化出危险对象。
3. "构造差异化":存与取的引擎不一致
- 反序列化漏洞的构造核心在于"构造差异化":存用一个 handler、取用另一个 handler(如存
php_serialize、取php),格式差异就会触发反序列化问题。 - 关键认知:存的意义就是为了取;存/取 handler 必须配套。实例:某环境把 Session 引擎从默认
php改成php_serialize,导致存/取引擎不一致,可插入任意字符构造利用。
4. 结合 upload_progress 的利用链
当无法直接给 $_SESSION 赋值时,可利用 upload_progress:上传时系统自动在 Session 里创建键值对。
- POST 一个与
session.upload_progress.name同名的变量,其值存入 Session;Session 中的键带固定前缀(如upload_progress_)——你传123,实际存的是upload_progress_123,检测时要同时看变量名(前缀)和值。 - 最终存储的是"前缀 + 提交值"而非表单 name 的原值;存储加前缀、取时反序列化、改值即触发,三者环环相扣。
典型利用思路(一道题):环境为 php 引擎且开启 Session 自动反序列化,类构造函数赋 $pinfo,析构函数 __destruct 执行 eval($pinfo)。利用 upload_progress 把恶意类序列化写入 Session,session_start() 自动反序列化后对象销毁即触发 eval → 命令执行。
四、CSRF 跨站请求伪造
1. 原理
- 老师拆解缩写:C = Client,S = Spoofing,R = Request,F = Forgery,全称"客户端请求伪造",本质是利用你已登录的会话替攻击者发请求。原理:利用后台表单(创建用户、修改密码),攻击者伪造请求,趁你登录时诱使提交,非自愿地创建用户或改密码。
- 与普通前端造假不同:依赖已登录的有效 Cookie/Session,后端未校验请求是否用户真实意愿。
2. 攻击场景
- 攻击对象:登录状态的管理员(Cookie、Session 均有效)。黑客发来投其所好的伪造链接(如"摩托车免息促销"),管理员安全意识薄弱点击。
- 危险根源:登录态 + 浏览器自动携带 Cookie——点击时自动带上有效凭证,若链接指向后台操作,等于以管理员身份执行黑客指令。易错点是以为"只看链接内容无害"就安全。
3. JS 自动提交表单
黑客服务器上的 index 页面放一个写死的用户创建表单(用户名、密码预设),用 JS 监听页面加载事件自动提交:
window.onload = function() {
if (/* 账号密码已填全 */) {
document.getElementById('auto_form').submit(); // 一访问即触发
}
}
- 注意是页面加载(load)时触发,不是卸载(unload)——老师口误纠正。JS 被称为"前端之王",增删改查都行,能实现页面自动提交。
4. 防御手段对比
| 方案 | 原理 | 优缺点 |
|---|---|---|
| 图形验证码 | 黑客猜不到便无法提交 | 前台常用;后台管理员创建用户通常无验证码(内部操作),反倒成漏洞 |
| 手机验证码 | 每次创建都发 | 安全但后台用着麻烦、牺牲效率 |
| Referer 校验 | 检查请求来源 | 极易伪造(抓包或 JS 里都能改),防不住非法提交 |
| CSRF Token | 服务端给每个表单生成随机 token,提交时放 header(如 csrf_token) |
服务端下发难仿,最可靠 |
- 关于 Referer:它只是"从哪来"的标记(像快递单的发货地,B 站广告跳到京东时
referer标为 B 站,京东据此结算推广费),但不验证真实来源,抓包即可改。 - 用
admin_csrf这类 Session 限制可简单禁止掉 CSRF 漏洞。
5. 过时性判断(老师强调)
- 到 2026 年 CSRF 已几乎过时:主流框架内置防护,实际利用率低于 5%,所以课程本不想细讲;但面试仍可能考到——面试官常照搬旧面经提问,不能忽视。别和字面相近的
CSF等缩写混淆。
五、SSRF 与 Redis(必讲考点)
- 服务端请求维度里 CSRF 可略过,SSRF 必须讲。面试常问 SQL 注入分类与绕过,偶尔问 RCE、SSRF 的绕过。
- SSRF 常和 Redis 漏洞结合:长亭面试红队成员直接问"是否接触过 SSRF 和 Redis";虽其他公司不一定问,但以防被问住仍需花半天补 SSRF。长亭面试偏红队渗透,拉人直接做攻击,不像蓝队偏防守。
六、SQL 注入:本质、分类与防御
1. 本质:混淆数据与指令
SQL 注入的本质,是把用户提交的正常数据混淆成了数据库操作语句,造成歧义,数据被当成指令执行。防御的关键在于消除这种混淆与歧义——隔断数据与指令的边界。
2. 三种防御方案(面试常考,建议按此顺序答)
| 方案 | 原理 | 评价 |
|---|---|---|
| 过滤函数转义 | 过滤转义单双引号等闭合符号,使数据始终在引号内 | "外头套武器";并非所有字段都能转义,有些字段必须带引号,强行转掉会报错 |
| WAF(如雷池 SafeLine) | 外部加防护,GET 请求基本绕不过 | 未从底层消除注入 |
| PDO 预编译 | 用占位符把 SQL 结构写死,数据仅作值传入 | 从底层分离代码与数据,面试最该点出 |
PDO 预编译示例:
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]); // 数据永远只作值处理,不会被当成新指令
先发带占位符的语句定死结构,再单独传值——避免字符串闭合拼接的歧义,也绕开引号依赖问题。
3. 分类与二次注入
常考分类:数字型注入、字符型注入、布尔盲注、时间盲注、二次注入、直接输入等。
- 被追问重点是二次注入:成因(数据先合法存入库、后续拼接时才触发)、如何消除、如何解决;与普通注入区别在写入时安全、读取拼接时才触发。防御需对存入和取出的数据均严格转义校验。
4. 预编译失效与绕过(由易到难的回答框架)
- 面试聊预编译要主动说明它并非万能,存在失效场景。
- 回答从简单到难:先讲双写、大小写绕过 → 再提逗号、等号、空格绕过。
- 若现有环境行不通,可补 雷池 SQL 注入绕过:对 POST 型构造差异化,让 WAF 误判为文件上传,后端却收到 POST 提交。
- 核心原则:绕过类问题必须覆盖多手法、由易到难,才算答全。
5. 提权(面试补充点)
- 被问"简单聊 Windows 提权"不能只说一种,要系统列出:服务权限提权、无双引号的服务提权等。
- Linux 提权顺序:内核提权 → sudo 提权 → 暴力破解提权 → 速度提权 → 改权限筛账号提权 → 敏感文件提权,层层试错兜底。内核漏洞先找未打补丁的漏洞(借助公开 CVE 库/工具)。若被问 UDF、SQL 上传获取等没听过的内容就会卡住——平时要补。
七、云安全与容器逃逸
- 云安全(docker 逃逸、k8s 微授权访问)曾在 2024 年讲过,因当时接受度低、面试几乎没考而放弃;25/26 年企业普遍上云用容器,寒假班学员面试频繁被问,故本次重讲但只花一天、略过原理。技术风向决定考察重点,知识需每年更新——24-25 年不问 AI,26 年必问。
- 容器逃逸:业务多跑在容器里,渗透常进容器;但容器是封闭隔离空间,里面只有数据库账号密码可脱库,拿不到物理机敏感信息——必须逃逸出容器到物理机。
- 内网 5~8 台容器都存在 RCE 漏洞,利用它逃逸很简单。容器逃逸今年考察概率比去年高,需提前看。
八、PHAR 伪协议反序列化:文件操作即触发
1. 核心机制
- phar 文件以序列化形式存储用户自定义的 metadata,文件系统参数可控时配合
phar://伪协议,不依赖serialize()也能触发反序列化。phar 压缩包由四部分组成,metadata 块最关键——读取时系统对 metadata 自动反序列化。
2. 文件头与类型识别
- 文件头是识别文件类型的核心依据(如 zip 有特定头)。phar 必须有特定标识头,且必须以该标识头结尾,否则扩展名无法识别。
- 关键:系统识别 phar 不看文件后缀——内部带特定属性头即可被当 phar 处理,借此绕过上传限制(如伪造
jf89a图片头)。
3. 触发规律:80% 的文件操作函数都会触发
几乎所有涉及文件操作的函数(file_get_contents、file_exists、unlink、copy、改时间、查权限、删除等)都会触发对 phar 协议的处理,最终走进 metadata 反序列化。
文件操作函数(file_get_contents / unlink / ...)
→ 传入 buffer(如 phar://)
→ 系统按伪协议类型取对应注册的 wrapper
→ wrapper 实现文件增删改查
→ 最终触发 metadata 反序列化
- 底层逻辑:传什么 buffer,就找对应注册的伪协议。phar 早已注册进 wrapper 列表,能处理文件即可触发。
- 利用点:若正则把
phar头过滤掉,可前加压缩协议(如compress.zlib://)触发,因为 phar 归根是压缩文件;类似地数据库文件操作函数也能触发(如 PostgreSQLCOPY、MySQL 文件操作)。 - 辅助知识点:
LOAD DATA LOCAL INFILE可从客户端本地导入 SQL 文件到 MySQL 表,说明它能操作本地文件——只要碰文件就可能走到反序列化触发点,把$x赋值替换掉本是system的$a即可执行命令。
4. MySQL 文件交互三要素(老师重申常考)
| 项 | 作用 | 要点 |
|---|---|---|
local_infile |
读文件(LOAD DATA LOCAL INFILE) |
需开启功能 |
secure_file_priv |
限定读写路径 | 为空可任意读;设路径(如 /tmp)则只能读该目录 |
file 权限 |
控制导出(INTO OUTFILE / INTO DUMPFILE) |
通常只有 root 有 |
九、实战:ThinkPHP 5.2 反序列化利用链(重点难点)
1. 技术栈探测
两种常用方式:
- 靠猜:根据系统猜 Win/Linux,如 Windows 下见 D 盘
phpstudy目录便猜用 phpstudy 搭建; - 利用开源框架的 debug 功能:几乎所有开源框架(如 ThinkPHP)都有 debug,上线若忘关,访问错误控制器时报错页会暴露网站目录路径等敏感信息。
- tp 调试配置:config 里
debug开关开发必须打开、并打开页面报错异常显示;两者开启后故意写错代码触发报错,就能轻易获取物理路径。上线务必关掉,防路径泄露。
2. 一道"上传 + 文件包含"题
- 注册账号后上传文件,直接传一句话木马被拦 → 有过滤;把类型改成 image 上传 png 成功,但文件名参数可能引发文件包含漏洞。用 include 类函数带参数读
/etc/passwd成功,证实存在包含。 - 不要盲猜接口名:按 F12 开发者工具 / Network 面板看按钮点击后提交的请求,确认是
download和delete两个操作。二者都包含class.php,用 include 把 class 内容写进当前页面。
3. 对象方法调用的魔术方法(__call 委托)
file_list内部没有close方法,传入file调用close会转而触发file的__call__——实际执行的是file的 close(类比遥控器没关机键,按下去自动找电视的关机键)。- 关键结论:只要和文件相关的操作,都能触发 phar 伪协议执行。
file里没列出 delete 函数,但作者隐藏了它——传参后用unlink删文件,同样触发伪协议造成反序列化。
4. 一道基于 5.20 版本源码的题
- 源码三个文件:
index.php仅一处反序列化,接受 GET 传参直接反序列化;Query 文件做数据库查询,取 POST 的query/code/dv参数连库。 - 页面分两步:登录页(输数据库账号密码,连接成功才跳转)→ 查询页(可写
select database()等语句查库)。 - md5 校验关卡:提交内容(如
123)要拼接上自身 md5 的前 5 位,再与原 md5 前 5 位比对,一致才继续查询;session code 由函数随机取 5 位拼接,答案不固定,不能写死。
5. 正则回溯 + 写文件
- 查询限制不允许用
INTO OUTFILE和INTO DUMPFILE,正常写文件通道被堵。 - 思路:用正则回溯(PCRE 回溯超限)把恶意数据写进文件——利用链内容写成十六进制(如
123456)防止特殊符号/函数干扰;回溯百万次数据必然写入。 - 考点总结(老师列出三个):
- 绕过——不算难事,用 AI 基本 100% 解决;
- 如何把恶意文件写进来——
INTO DUMPFILE被禁,改用正则回溯写文件; - 如何触发——借 MySQL
LOAD DATA LOCAL INFILE触发。
6. ThinkPHP 5.2 反序列化利用链(核心难点)
先掌握两个前置概念:
- trait 不是类:
conversion是 trait(trait关键字声明),用于弥补 PHP 单继承缺陷——想复用多份代码就定义 trait 并在 class 中引用。class才是类。 - 抽象类不能直接实例化:
model是抽象类,需由具体子类(本例Pivot)继承实现才能用。
利用链图示(反序列化入口 → 命令执行):
反序列化入口:think\process\pipes\Windows(window 类)
→ __destruct 调用 close / removeFiles(文件操作)
→ 触发 file 数组元素的 __toString(files 须为 conversion 对象)
→ model(抽象类,被 Pivot 继承)→ 引用三个 trait:
attribute / relationship / conversion
→ 取 date / relation / getValue
→ 走到动态函数调用(如 call_user_func → system)
→ 命令执行
- 入口即起点:序列化从 window 类开始,反序列化也从 window 类入口(原路返回)。window 类需传入数组(内部循环),传入
Pivot类。 - 方法查找按继承链向上追溯:自身无 → 找
model→ model 无则找它引用的三个 trait(attribute里定义了get/getValue)。 - 可控性推导(老师强调要会追):
date可控(attribute 处)→relation空值可写新类替换 → K 可控 → 内部可控 → fill_name 可控 → value 可控 → close 可控,用户全参数可控。value来自name,name来自date,须找到源头。 - 传参对应:
date有值而relation为空;K与 data 数组里的键名必须一致,否则取不到值(date是smile,两个smile必须一样)。 system函数能接收两个参数,第二个参数即使传数组也不影响命令执行(cat ps w真跑起来)。- 调试心得:必须用断点调试——函数跳转多,人眼追不上;"没找到"先怀疑找错路径而非不存在。课后务必结合调用图拆解数据流向。
7. 完整利用链串起来
- 第一步:正则回溯 +
INTO DUMPFILE把序列化内容(原 urlencode,实际需封装进 phar)上传到/tmp目录; - 第二步:ThinkPHP 5.2 反序列化 payload 已备好(写入 phar 的 metadata);
- 第三步:借 MySQL
LOAD DATA自动触发——phar 的 set_metadata 自动反序列化,让恶意链被执行; - 最终:命令执行 → 读登录器下的 flag → 通过
coi外带把 flag 发到自己服务器,监听 80 端口接收。
限制提示:题目给的是普通用户,无权限调用对应逻辑,需另找触发路径。三个知识点:TP5.2 反序列化(首个正式框架 RCE 反序列化)、正则回溯、触发方式(第三个未讲完,需结合已发文档自习)。
十、工具与厂商杂谈
- 被动扫描器:不主动探测,而是设代理把外出流量转发给它,从流量里找漏洞。与主动扫描易混,关键区别在是否依赖用户流量转发。某被动扫描工具曾与 BurpSuite 搭用顺手,后因体验问题弃用。
- 雷石(雷石坊):某公司产出的原型工具,可直接加载题目环境来分析,不必重复搭建。
附:行动清单
- 改简历:把简历发给老师看,按"夸张 + 自圆其说"的原则修改,有问题直接问。
- 补 SSRF:结合 Redis 复习 SSRF 原理与利用,防范长亭这类红队向面试提问。
- 吃透 Session:动手过一遍
upload_progress+ 条件竞争拿 Shell 的实操,确认cleanup开关对结果的影响。 - 研究 TP5.2 利用链:结合讲师发的调用图与文档,用断点调试把 ThinkPHP 5.2 反序列化链完整跑通(正则回溯写 phar → LOAD DATA 触发)。
- 整理面试话术:SQL 注入三种防御(过滤/WAF/PDO)与分类(含二次注入)、预编译失效的由易到难绕过、Windows/Linux 提权清单,按"系统列点、由易到难"准备。
- 关注云安全:容器逃逸(RCE→逃逸)今年考察概率上升,提前看。
附:简历与就业要点
- 面试是第一步:面试收不到就等于没展示机会;简历要会"夸张"且能自圆其说,自信、能展示自己。改完发老师看会收到建议。
- 不同公司面试方式不同:
- 长亭红队爱问情景题——考察实战应对(如入侵中遇阻怎么解决);
- 有些公司是格式化面试——总问那几套固定问题(如 851),死板套路固定;
- 有些公司安全业务只是"套模板 + 挑几个反序列化、内网渗透点"凑数,并非真正体系化。
- 公司认知:
- 长亭被阿里收购后薪资未涨、强度反升甚至降薪,大量核心离职,实力不如从前;
- 深信服、奇安信是综合型大公司,服务甲方为主。深信服给应届生 16-18k + 每月 4000 多饭补,原因是极累:做安全服务经理同时服务 30-50 个客户,365×7×24 待命,手机不能关、凌晨也接电话;
- 客户响应极严:漏接电话第一次上级拉群道歉 + 记处分扣当月奖金,两次未接扣全年奖金,三次未接直接辞退;
- 深信服虽累却是行业领头羊:去年多数安全公司亏损,它逆势盈利约 7 亿多(靠与政府、国央企关系好利于设备销售),属优质选择。
- 加班与工作动力:累不可怕,关键是加班要给钱——很多人讨厌的是无薪加班(华为过年加班一天几千块)。35 岁优化风险各行各业都有,不特殊。
- 应届身份最宝贵:烟草、电力、电网、重庆机场等优质岗位仅限应届生且需考试,非应届进入概率近乎为零;应届身份让竞争压力骤减数十倍甚至百倍,一旦丢失竞争力立刻飙升,须谨慎对待。
- 年轻是优势:年龄小、无家庭负担、用工成本最低,公司更愿意招;培训三五个月能上手、能加班出差就比要高薪养家的人吃香。别怕试错,浪费一两年后果也承担得起。
- 信息差:很多国央企好岗位根本不对外招聘(做企业培训才接触到),得拓宽渠道。
- 面试心态:每个人都会卡住很正常,关键是要有思路——能说出"自己只是遗忘但思考路径清晰"就 OK,最怕完全说不出任何内容。
浙公网安备 33010602011771号