课堂笔记-8月20日
课堂笔记-8月20日:WordPress 六环 RCE 攻击链——缓存投毒、错位利用与管理员接管
来源:
8_20/录音.txt| 日期:2026-08-20 | 主题:从昨日 SQL 盲注/CVE 回顾到 WordPress 六环 RCE 完整利用链(缓存投毒 → 权限提升 → 创建管理员)
〇、今日主线
今天是一堂高度贴近实战的 WordPress 漏洞分析课。欣老师先带大家回顾昨天内容:WordPress 曝出的两个 CVE(路由混淆 + 盲注)以及 POS 系统三层参数校验机制,解释第一次盲注为何失败、batch 请求如何绕过 method 限制。随后切入今天的核心——理解"环一"和"环二":攻击者如何通过盲注伪造"假文章"完成内存投毒,再借助 oEmbed 视频嵌入机制把毒化数据持久化写入数据库。上午打通"环一"并调试验证,下午计划继续环二到环六,最终串起一条六环 RCE 攻击链:从匿名 REST 请求到创建管理员账号、拿到站点控制权。老师多次强调不要照搬 payload,要理解每一步"为什么",并示范了条件断点、时间盲注验证等调试方法论。
一、昨日回顾:两个 CVE 与三层参数校验
1.1 WordPress 近期漏洞(CVE)
WordPress 曝出两个获得官方 CVE 编号的漏洞:
- 路由混淆漏洞(Routing Confusion):利用路由解析规则的不一致绕过权限控制。
- 盲注漏洞(Blind SQL Injection):在特定查询中可注入恶意 SQL。
注意:还存在一个 RCE(远程代码执行)漏洞,但厂商没有分配 CVE 编号,因此官方确认的只有两个。
1.2 POS 系统三层参数校验(为什么第一次注入失败)
老师用 POS 系统举例,说明系统对传入参数有三层严格检查,像"安检的三道闸门":
| 层 | 检查内容 | 强度 | 判定方式 |
|---|---|---|---|
| 第一层 | 参数必须存在于预设的数据表字段列表 | 弱(不算真防线) | 按规范传参即可通过 |
| 第二层 | 参数类型必须匹配预设(如要求数字类型) | 中 | 类型不匹配直接拒绝 |
| 第三层 | 查询阶段强制类型转换:数组每个元素强制转数字 | 强 | 转换失败则终止查询(如 "select" 转数字失败) |
要成功注入必须同时满足:① 使用合法字段名;② 传参类型匹配;③ 参数内容能通过强制数字转换。缺一不可。
1.3 SQL 盲注的过滤与绕过思路
- 第一次攻击失败,是因为触发了第二条防线——数据清洗函数。该函数预期接收数组,但传入的是字符串,于是自动用空格和逗号把字符串分割成数组元素。
- 之后 WordPress 会二次检查数组元素是否为整数(int),不是就直接返回 400。注入的 SQL 被分割后无法通过数字校验,这是第一次失败的直接原因。
- 绕过核心:构造特殊 payload,让"分割后的数组元素看起来像数字",从而骗过过滤函数。
二、batch API 机制与"错位"利用
2.1 batch 请求的 method 限制
WordPress 支持批量传输多个 API 请求,方式称为 batch。关键限制:
- batch 内请求要求使用 POST / PUT / DELETE,GET 方法被排除。
- 但要实现盲注等操作,往往需要 "既符合 batch 要求、又能使用 GET 效果"。
绕过技巧:在 batch 里先写三个 POST 请求满足 method 限制,再通过构造格式错位(例如第一位只写一个冒号)间接实现 GET 效果。核心是利用格式漏洞绕过 method 限制,同时保持 batch 整体有效。
补充:bash 处理 requests 时,body 里的 method 字段可随意填写,实际绕过了 batch 限制;但内容不能乱写。
2.2 请求循环处理与"错位"(Mesh / Request 索引不同步)
系统处理请求时对外层三个 requests 做循环,每个 request 用 sw_error / wp_error 判断。容易混淆的点:外层 requests 与内部处理的 singler.request 的关系——第一次处理时 singler.request 其实就是外层三个 requests 之一;因 request 与 match 循环处理,会出现两个报错。
错位发生的具体场景(整个漏洞的核心机制):
| 项 | 内容 | 数量 |
|---|---|---|
| 原始 request | 报错请求 + 合法请求 A + 合法请求 B | 3 个 |
| 实际 match/mesh | 只存了合法请求 A、B(error 被跳过) | 2 个 |
当循环到 i=1 处理 POST 时,mesh 中取到的是 batch,而实际 request 取到的是 post,产生第一次错位——本应由 post 处理的 body 被 batch 错误处理。就像两个人数数,一个跳过错误项,另一个却继续计数,最终对不上号。
2.3 错位导致清洗函数失效
错位的直接后果:本应由 category 控制器处理的请求,最终被 post 控制器处理了。由于清洗函数绑定在 category 控制器上,post 控制器里没有该清洗逻辑:
- 原本应被清洗的 12 个字段直接绕过验证环节;
- 这些参数在 post 里还被错误映射成
os not in之类的字段。 - 类比:本该由质检员 A 检查的货物,被阴差阳错送到没有检查流程的质检员 B 手里,不合格产品就溜过去了。
修复逻辑:攻击者传递字符串参数就能绕过数组判断,因为字符串会跳过关键安全检查。WordPress 7.2 的修复方案是——即使收到字符串参数,也先强制打散处理,堵住参数类型不一致导致的漏洞(原本只检查整箱水果,现在散装也逐个拆箱检查)。
2.4 请求设计(昨日作业回顾)
- 第一个 request:含三个 post(其中一个是故意构造的报错请求);
- 第二个 request:含三个 get(含带 exclude 参数的 category 请求)。
这种设计既能绕过 batch 限制,又能帮助理解请求构造逻辑。老师特意不直接展示答案,要求通过回忆前一天内容回答——因为主动回忆才能真正检验掌握程度。
三、SQL 注入深度:盲注的局限与注入手法演变
3.1 盲注的功能局限
在 WordPress 这类严格环境下,盲注只能执行 SELECT 查询,功能非常有限:
- 想通过
into outfile实现 RCE 几乎不可能:MySQL 普通用户没有文件写入权限;secure_file_priv参数通常被严格限制。 - 成功率极低,十万个网站里都难成功一个,利用条件太苛刻。
3.2 注入手法演变:从 DLL 提权到伪装 UNION SELECT
- 早期的 DLL 提权手法在 2012–2013 年流行,但到 2026 年已完全失效。
- 现在更隐蔽的做法:构造与文章表结构完全相同的 UNION SELECT 查询。
- 攻击者先研究目标网站文章表字段结构;
- 注入时构造包含所有字段的 UNION SELECT,如第一个字段放 ID(0)、第二个放作者(1)、第三个放日期……完全模拟正常文章字段结构,让恶意查询看起来像正常数据请求。
- 关键:攻击者须事先掌握目标数据库的精确表结构,隐蔽性远高于直接提权。
3.3 拼接技巧与 16 进制绕过
- 让原查询失效是核心思路:
and 1=0让前半部分查询返回空,后面UNION SELECT的恶意数据就能显示出来;传id=-1同理。 - 直接
and 1=0在真实项目中太明显,需要更隐蔽的方式。 - 16 进制编码绕过单双引号过滤:在伪造函数里完成 23 个字段的拼接并用
UNION SELECT展示,所有拼接都转成 16 进制,避免单双引号对 SQL 查询的干扰。 - 呼应之前讲过的 MySQL 绕过技术:三种逗号形式、空格绕过。
伪代码示意(注入点位于 NOT IN 条件中):
-- "1)" 闭合 NOT IN 括号;and 1=0 使原查询失效;UNION SELECT 夹带伪造数据
... WHERE post_type NOT IN (1) AND 1=0
UNION SELECT 0,1,date,...,23个字段...
-- 最终对拼接语句做 URL 编码,方便通过 GET 参数传递
四、缓存投毒与"水合"(Hydration)机制
4.1 假文章如何被当成真文章
- 攻击者通过盲注构造虚假文章数据;WordPress 用
get_results查询时会把伪造数据当作真实文章处理。 convert_to_wp_post_objects函数把查询结果转换成标准文章对象(wp_post)。- 伪造对象会被自动存入缓存(代码中的 catch 部分)。即使数据库里没有这篇文章,系统也认为它存在——缓存污染就这样实现。
4.2 stdClass → WP_Post:水合(hydrate)
- 通过 SQL 查询构造的只是一个 stdClass 对象("看起来像文章")。
- 关键步骤用
array_map做水合(hydrate),转换成WP_Post类实例(WordPress 能识别的文章类)——类比给干海绵注水变成湿海绵。水合后伪造文章才能被系统正常处理。
4.3 两种查询机制对比
| 机制 | 行为 | 本次攻击使用的 |
|---|---|---|
get_cool |
直接返回文章 ID 去数据库查询 | ✗ |
get_result |
把整个查询语句展开并放入内存 | ✓ |
本次攻击必须利用 get_result 机制,让恶意构造的查询先进入缓存,而不是像昨天那样直接在数据库操作。
4.4 缓存写入条件:post_id 必须为 0
- post_id 为空/为 0 时才进入 update 分支往缓存写入数据;若有值则跳过缓存写入。
- 若 post_id 指向已有文章,系统会把伪造的视频数据错误存进该文章,破坏原有缓存结构——就像往新本子上写字(整洁)和往旧本子上涂改(搞乱)的区别。
4.5 缓存键冲突:同 MD5 覆盖
- 缓存名称由 URL + 宽高 等参数生成的 MD5 决定;URL 与宽高完全相同时,缓存名(MD5)相同,再次写入会直接覆盖旧缓存。
- 案例:原有缓存 0/1/2 三条,新操作用了与"1 号(编号 14)"完全相同的 URL/宽高,新数据直接覆盖 14 号记录——后续查询"1"实际取到的是被覆盖后的数据。开发中要注意相同缓存键导致数据意外覆盖。
五、oEmbed 视频嵌入与内容过滤器(持久化的关键)
5.1 oEmbed 嵌入机制
- 网页中可以用
<oembed>标签嵌入视频(类似<p>插入文字),系统从远程服务器动态获取视频内容:
<oembed url="https://www.youtube.com/watch?v=xxx"></oembed>
- 攻击场景中,作者伪造一个 id 为零的临时帖子作为"容器",等视频真正加载后再替换成实际内容。
- embed 协议是通用的,不限于 YouTube,任何支持该协议的平台都能嵌入。
5.2 视频缓存:避免重复抓取
- 流量大的网站若每次刷新都远端抓视频会浪费带宽(1000 人刷新 = 抓 1000 次),所以 WordPress 首次访问从远端抓取视频存入数据库,之后直接从数据库读取。
- 抓取 YouTube 界面实际拿到的是对方返回的 Frame,相当于把对方视频通过 Frame 嵌入到己方站点。
- 写入策略:帖子已有内容(非空)→ 数据放入现有帖子;帖子为空 → 新建帖子存放。缓存有效期通常是一天。
5.3 内容过滤器(the_content / apply_filter)
- 文章内容必须经过过滤,否则用户可能发布反动、黄赌毒等违规内容;文章里若有视频也要通过过滤器抓取处理。
- 系统初始化时注册各种过滤器(图片、视频、违禁词过滤器等),像一个个"检查站",根据内容类型自动分发给对应过滤器处理。
- 开发者在
the_content过滤器里实现:文章内容中出现视频链接时,自动提取并交给 WP Embed 解析。 - 模块化设计思路:先声明规则(创建过滤器),之后所有内容自动应用(类比外卖"超过 30 分钟免单"规则);过滤器通常不止在一个地方注册,而是分散在多个模块中。
5.4 短代码与缓存 Key
- 短代码(short code) 不仅存在于程序代码,文章和视频里也会用到(如视频中带 width/height 属性的三行代码)。
- 系统处理文章时:提取文章的虚拟 ID → 生成唯一缓存键(key)(由文章 URL 的 MD5 得到)→ 用 key 查缓存:
- 缓存命中 → 执行
wp_update_post(更新);未命中 → 执行插入。两者都针对数据库的 posts 文章表。
- 缓存命中 → 执行
六、六环攻击链:从内存投毒到管理员接管(今日核心)
6.1 总体框架
作者用一个简单的 select 请求实现远程代码执行,攻击链拆成六个环(环节)。老师强调这是近年调试过最复杂的漏洞之一,环环相扣、缺一不可——任何一环失败整个流程崩溃。
| 环 | 作用 |
|---|---|
| 环一 | 内存投毒:用 get_result 把伪造的假 post 注入内存缓存 |
| 环二 | 缓存持久化:从被污染的内存中取数据,触发过滤机制,把伪造视频数据写入数据库 |
| 环三 | 读取已缓存数据,构造 7 条查询 / 形成两个循环 |
| 环四 | 利用变更集(Change Set)发布触发状态转换 |
| 环五 | 切换用户权限为管理员(权限提升) |
| 环六 | 重新进入 REST API 流程,创建管理员账号 |
6.2 构造假文章对象
- 需要构造一个包含文章所有必要字段的 post 文章对象。
- payload 里用 7 个参数(id、auth、content、title、static、close、name)组装假文章;但原对象有 23 个字段,必须仔细核对字段一致性,再传给前一个函数处理,最终把"假文章"入库保存。
- 逐项核对 id、content、publish 等关键字段的处理方式——老师反复强调"别着急"。
6.3 成环结构:三个视频 ID 与父子文章引用
为什么必须缓存三个可控的伪造数据:三个 ID 正好能"成环"——两个太少无法成环,四个太多冗余;三篇互相引用的文章形成闭环,触发 WordPress 的循环检测与重新发布逻辑。
为什么不用文章 ID 代替视频 ID:① 文章 ID 可能被程序修改(显示与存储不一致);② 文章数量可能不足;③ 文章 ID 规律难测。
父子文章循环引用示例:
文章 13 → 父 18000
文章 18000 → 父 13
文章 14 → 父 13(又指回第一组)
- 系统检测到循环引用后自动解环,过程中重新发布(update)相关文章触发状态变更。关键条件:post_id 为 null 才进入 update 分支写缓存。环一写内存、环二入库、环三读取后形成两个循环,解环时发布两个帖子——第一个切换管理员身份,第二个重新调用 REST API 完成管理员添加。
6.4 变更集(Change Set)与权限切换(SUID 式提权)
- 变更集(Change Set) 用于站点外观自定义(改标题、颜色、菜单等),支持预览、批量发布、定时发布,本质也以"帖子"形式存储。
- 发布时调用
wp_customer_publish_changeset一次性应用修改。关键漏洞:发布时会切换用户身份——定时任务执行时当前用户 ID 可能被重置为 0(系统用户),产生权限问题。 - 权限快照机制:用户修改内容时记录修改者身份,实际发布时临时切换到当初修改者的高权限身份执行(类比银行代办出示委托书)。
- 攻击思路 = Linux SUID 提权:伪造变更集临时获取管理员权限;用 Navicat 保存自定义请求,修改文章作者为管理员,利用发布时的权限切换窗口,在权限"还没降回去"的瞬间通过 REST API 创建新用户(类似给
find加 SUID 位,执行瞬间获得 root)。 - 系统只能通过带唯一标识(UID)的导航菜单(nav)追踪变更——其他修改项(颜色等)没有 UID 列,这是追踪机制的关键。
6.5 分页逻辑:为什么配置参数要传 -1
page=-1 的完整触发链(环环相扣):
配置 page=-1 → noPaging=true → limit 被置空 → 分割标记 isSliced=false → 跳过分页 → 执行 getResults 获取全部数据
- 只有 limit 为空时
isSliced才为 false(代码第 337 行判断)。 - 为什么不用 category / user 接口:category 里预定义了 pr 配置字段且固定为 1,会覆盖传入值,无法实现 -1 逻辑;部分接口在参数清洗阶段要求字段值等于 1,传 -1 会被拦截(400)。作者特意选择参数表更简单(只有两个参数)、干扰因素少的接口。
6.6 状态转换:future → publish 触发动作
- 发布时间被设为未来时间(如 2020 年)时状态为 future;系统自动检测:若发布时间早于当前时间,则自动把 future 更新为 publish。
- 代码第 5826 行触发状态转换 future → public,自动触发函数 → 进入自定义主题发布流程 → 切换管理员界面。
- 发布变更集前系统会检查创建者的原始权限:使用账号权限必须与当初创建者完全一致(本例变更集由 admin 创建,发布前必须把权限切回 admin)。
6.7 REST API 重入与嵌套 batch
- 状态切换触发动态钩子(pre-request),REST API 正好挂在该钩子上 → API 被重复调用(重入),形成嵌套 batch(外层初始 batch + 内层新生成的 batch),这就是出现两次 batch 调用的原因。
- 攻击链末尾:连续发送两个添加用户请求,第一个被识别为 POST 拦截,第二个成功触发 REST API;以管理员权限通过 wp_user 接口创建新用户 → 登录后台 → 上传恶意主题/插件(伪装成主题的 web shell) 获取控制权。请求先后顺序和权限切换时机是此类攻击的要点。
6.8 发布与"撞环":环检测机制
- 系统发布前会先检测文章是否形成循环引用(成环)。检测发生在 8080 端口 find 方法;发现环就把 parent 指针置零解除循环。
- 依赖链级联检查:发布 14 前检查父 13 是否已发布;13 未发布再检查 13 的父 999;任一父处于草稿状态则整个发布被卡住。
- 只有数据库里原本不存在的 ID 才能成功发布(13、15、16、17、18);16/17/18 能发布是因为提前在数据库存入了这些 ID——"发快递必须先有收件人地址"。
七、调试方法论与工具(老师反复强调)
7.1 学习与复盘方法
- 主动回忆:复盘先闭卷思考,卡壳处重点复习;老师特意不直接给答案,逼学员回忆昨天内容。
- 调试驱动掌握:真正掌握漏洞利用 = 能闭着眼睛在脑子里完整推演整个流程;做不到说明调试次数不够(像学骑车,反复练习几百次自然记住)。老师本人吃透一个漏洞要反复调试几百次。
- 网上关于该 WordPress 漏洞的详细分析很少,课程内容很珍贵。
7.2 断点与调试技巧
- 清空所有旧断点再重新设置,避免干扰观察;断点要设在最后的查询环节(query 处),而不是孤立检查某个点。
- 条件断点:右键"编辑断点"输入条件表达式(如变量等于特定值),断点图标变成等号形状,只有满足条件的请求才中断,不用不停按 F9 跳过无关请求(老师在第 3188 行设置)。
- 精准定位目标请求:系统请求过多会淹没目标请求,在错误位置下断点会浪费时间(要手动跳过几十条无关查询)。
- 确认注入语句真正生效:系统内部查询可能先于你的恶意查询执行;必须抓到"确实执行了构造的 payload",否则后续调试失去意义。
7.3 漏洞验证工具("I" 交互式命令行)
- 类似 Linux shell 的交互式工具,可持续查询(不像 cmd 查一次就结束)。
- 必须加授权参数:原作者设计时拒绝远程执行请求,只用于验证本地漏洞、不做入侵。
- 执行 RCE 前工具会先做盲注检查(通过延迟判断是否存在注入点),有漏洞才继续;建议加 sleep 参数验证时间延迟。
7.4 时间盲注验证逻辑
- 通过
sleep(0)(条件假,立即返回)与sleep(4)(条件真,延迟 4 秒)测试响应时间差异。 - 重复执行三次(round=3),对比平均响应时间避免偶然误差。严谨判定条件(老师强调的两个硬性条件):
| 指标 | 要求 |
|---|---|
| 慢查询 - 快查询 | 大于 sleep 时间的 60%(如 sleep 4 则需 > 2.4 秒) |
| 快查询 | 必须小于 2 秒 |
八、WordPress 平台知识(背景补充)
8.1 为什么 WordPress 广受欢迎
- 三大优势:丰富的主题库(前端设计精美)、可视化拖拽建站(像搭乐高)、丰富预设组件库。
- 组件库四大类:视觉组件(轮播图)、布局组件(页面模板)、头部组件(页眉)、菜单组件;零代码、"所见即所得",上百种现成组件直接拖拽复用。
- 缺点:运行稍慢(安装主题后变慢,尤其从谷歌等国外服务器加载字体)。
8.2 安全性
- WordPress 在 CMS 里安全性属顶尖水平——十几年只爆出过一个高危漏洞,比有些 CMS 一年好几个漏洞强得多。
- 核心设计理念:"一切皆文章"——标题、草稿、正式文章、全局样式表、导航、主题全以文章形式存储,系统结构统一。
8.3 载体选择与"绕过验证 ≠ 安全"
- 攻击中载体选择 category 或 user 均可(都不含特定字段),主要目的给 post 传值;RCE 部分用 weget 方法,错位执行机制保持不变。
- 不能使用 category/user 的原因:参数类型校验(要求 int,传 string 即使能强转也会 400)+ 参数表限制(部分字段要求值 = 1)。
- 老师强调:绕过验证不等于安全,即便某组件(如 sidebar)不参与验证,也要纳入整体安全策略。
附:行动清单
- 闭卷复盘昨日 payload:独立写出"三个 POST(含报错)+ 三个 GET(含带 exclude 的 category 请求)"的 batch 请求结构,卡壳处回到昨天的练习重点复习。
- 打通环一并在断点验证:按老师文档在最终查询处下断点,用作者提供的 payload 触发完整流程,确认
page=-1 → limit 为空 → getResults的链路,观察假 post 对象是否写入缓存。 - 练习 16 进制绕过:把 23 字段的
UNION SELECT拼接全部转为 16 进制,验证能绕过单双引号过滤,并复习"三种逗号形式"与"空格绕过"。 - 掌握条件断点与时间盲注判定:在本机环境练习设置条件断点(编辑器里输入条件表达式),并用
sleep(0)/sleep(4)+ round=3 验证时间盲注的两个硬性判定条件(慢-快 > 60%×sleep,快 < 2s)。 - 对照作者脚本(wp)推演六环:若觉得 RCE 太难,先退回昨天的 POST 注入——搞懂"为什么不能直接提交 POST、如何用错位绕过",再从头跟读作者的 2000 行攻击脚本。
- 归纳"字段映射错位"笔记:用表格把 mesh 与 request 的索引差异、清洗函数绑定位置、12 个被绕过字段画清楚,作为面试讲漏洞时的素材。
附:简历与就业要点
录音末尾涉及考试与就业指导,单独整理如下。
- 简历写多少取决于实际掌握程度。安全行业普遍浮躁,很多人直接套用现成脚本;深入分析漏洞原理才是核心竞争力。能完整讲清漏洞机制(如本次 WordPress 六环 RCE),面试时就能自信展示对最新漏洞的研究成果。
- 考证与就业要区分对待:P 级证书考试可集中两天突击辅导框架漏洞(辅导机构也是"讲用法不讲原理"的速成方式),题库都给了、通过不难,不必过度看重;但就业需要的知识要更全面系统地准备。
- 面试重点看主流框架:2015–2017 年的老旧漏洞不需要详细讲解,重点讲 Share、Spring、Struts2 等当前主流框架的漏洞——这些才是实际面试中更可能被问到的内容。
- 别死磕过时标准:完全按照某个旧标准(如深信服内容)要求自己不现实,等于把水平倒退十年,不利于面试求职。
- 考试提醒:考试可能超出课纲范围(课纲存在反虚化漏洞,可能出框架外老题);某些题型解法唯一(如遇到
pear cmd文件时答案确定);9 月 16 日考试时间还早,不必着急。
浙公网安备 33010602011771号