qiu_dv

导航

课堂笔记-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 / DELETEGET 方法被排除
  • 但要实现盲注等操作,往往需要 "既符合 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 请求设计(昨日作业回顾)

  1. 第一个 request:含三个 post(其中一个是故意构造的报错请求);
  2. 第二个 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)不参与验证,也要纳入整体安全策略。

附:行动清单

  1. 闭卷复盘昨日 payload:独立写出"三个 POST(含报错)+ 三个 GET(含带 exclude 的 category 请求)"的 batch 请求结构,卡壳处回到昨天的练习重点复习。
  2. 打通环一并在断点验证:按老师文档在最终查询处下断点,用作者提供的 payload 触发完整流程,确认 page=-1 → limit 为空 → getResults 的链路,观察假 post 对象是否写入缓存。
  3. 练习 16 进制绕过:把 23 字段的 UNION SELECT 拼接全部转为 16 进制,验证能绕过单双引号过滤,并复习"三种逗号形式"与"空格绕过"。
  4. 掌握条件断点与时间盲注判定:在本机环境练习设置条件断点(编辑器里输入条件表达式),并用 sleep(0)/sleep(4) + round=3 验证时间盲注的两个硬性判定条件(慢-快 > 60%×sleep,快 < 2s)。
  5. 对照作者脚本(wp)推演六环:若觉得 RCE 太难,先退回昨天的 POST 注入——搞懂"为什么不能直接提交 POST、如何用错位绕过",再从头跟读作者的 2000 行攻击脚本。
  6. 归纳"字段映射错位"笔记:用表格把 mesh 与 request 的索引差异、清洗函数绑定位置、12 个被绕过字段画清楚,作为面试讲漏洞时的素材。

附:简历与就业要点

录音末尾涉及考试与就业指导,单独整理如下。

  • 简历写多少取决于实际掌握程度。安全行业普遍浮躁,很多人直接套用现成脚本;深入分析漏洞原理才是核心竞争力。能完整讲清漏洞机制(如本次 WordPress 六环 RCE),面试时就能自信展示对最新漏洞的研究成果。
  • 考证与就业要区分对待:P 级证书考试可集中两天突击辅导框架漏洞(辅导机构也是"讲用法不讲原理"的速成方式),题库都给了、通过不难,不必过度看重;但就业需要的知识要更全面系统地准备。
  • 面试重点看主流框架:2015–2017 年的老旧漏洞不需要详细讲解,重点讲 Share、Spring、Struts2 等当前主流框架的漏洞——这些才是实际面试中更可能被问到的内容。
  • 别死磕过时标准:完全按照某个旧标准(如深信服内容)要求自己不现实,等于把水平倒退十年,不利于面试求职。
  • 考试提醒:考试可能超出课纲范围(课纲存在反虚化漏洞,可能出框架外老题);某些题型解法唯一(如遇到 pear cmd 文件时答案确定);9 月 16 日考试时间还早,不必着急。

posted on 2026-09-04 00:35  qiu_dv  阅读(2)  评论(0)    收藏  举报