你知道吗?未认证就能发文章的 WordPress SEO 插件
原创
0x00
0x00
随笔漫记安全路
2026年11月6日 08:46
北京
去阅读
本文基于真实安全事件整理,已对涉及的公网 IP、域名等敏感信息做脱敏处理,仅作安全科普与应急响应经验分享,不提供任何攻击细节的实操指导。
一、事件概述
近期在为一家企业站点做安全评估时,发现其 WordPress 站点上的一款 SEO 插件存在严重的未授权访问漏洞(Broken Access Control,CWE-862)。
这款插件叫 baiduseo(内部目录名 seo_title_baidu),发布在 WordPress.org 官方插件目录,属于公开插件。受影响版本为 2.1.x 及更早,最新版 2.2.0(2026 年 3 月发布)已修复。
最直观的危害一句话概括:任何人不需要登录、不需要任何凭据,只要向站点任意页面 POST 一段特定参数,就能直接创建并\"发布\"一篇文章——实测文章立刻出现在站点前台公开可访问。
严重等级:Critical,CVSS 参考评分 9.8。
二、攻击链复盘
整个利用链非常短,只有一步:
任意访客 → 向站点任意前台 URL POST data={\"BaiduSEO\":49,...} → 插件直接 wp_insert_post 发布文章
插件在 init() 中注册了一段处理逻辑:只要检测到 POST 请求里有一个名为 data 的字符串字段,就按 JSON 解析,根据其中整数键 BaiduSEO 的值分发到不同操作。
源码里还有一行颇有\"自白\"意味的注释:
//由于插件需求,有一些数据是其他服务器推送过来的,无法满足随机字符串验证
翻译过来就是:插件作者为了方便另一台服务器推送数据,主动放弃了鉴权。没有登录校验、没有权限校验、没有 nonce、没有来源签名。
三、利用条件(表格)
| 条件 | 要求 |
|---|---|
| 认证 | 无需任何 Cookie / Token / 登录态 |
| 请求路径 | 站点任意前台页面均可(含首页) |
| 请求方法 | HTTP POST |
| 请求体 | 表单字段 data,值为 JSON 字符串,顶层键 \"BaiduSEO\" 为整数 |
| 前置信息 | 无(插件版本可通过接口直接读取) |
四、技术深度拆解
各操作码一览
解析后的数据进入写操作处理器,不同 BaiduSEO 值对应不同动作:
| 操作码 | 功能 | 落点 |
|---|---|---|
| 17 | 更新关键词表 | 插件自有表 |
| 37 | 写任意文章的 post meta | wp_postmeta |
| 41 | 覆写任意文章正文 | wp_posts.post_content |
| 44/45/46 | 插件表直接插入/更新 | 请求体字段直接落库 |
| 47 | 版本读取 | 只读 |
| 48 | 全站分类读取 | 只读 |
| 49 | 创建并直接发布文章 | wp_posts |
其中最危险的两个:
op 49 —— 未认证发文。核心代码就几行:
$my_post = array(
'post_title' => $data['title'],
'post_content' => $data['con'],
'post_status' => 'publish', // 直接发布
);
$id = wp_insert_post($my_post, 0);
标题、正文、状态全部来自请求体,且状态固定为 publish——文章创建即公开。
op 41 —— 篡改任意文章。目标文章 ID 由请求任意指定,直接对 wp_posts 表执行 UPDATE 覆写正文。也就是说,攻击者可以改掉站上任何一篇文章的内容。
op 44/45/46 —— 数据库直写。请求体中的字段数组不做逐列白名单,直接传给 $wpdb->insert/update 写入插件自有表,构成额外的任意数据写入面。
实测过程
验证只读入口(版本读取):
curl -sk --data-urlencode 'data={\"BaiduSEO\":47,\"key\":\"test\"}' https://目标站点/
# => {\"code\":0,\"version\":\"2.0.3\"}
未认证发文,响应返回 {\"code\":1} 表示写入成功;随后通过 REST API 回读,新文章已经以 publish 状态出现在文章列表,前台可以公开访问。
整个验证过程未携带任何 Cookie。
五、应急处置过程
复测确认修复:站点随后将插件升级到 2.2.x。复测时所有接口均不再返回 JSON,只返回普通页面;发文复测也未创建任何新文章。通过 REST API 与源码指纹(新版本前端脚本特征)双重确认。
版本边界验证:从 WordPress.org 拉取各版本源码逐一比对,2.0.3 / 2.0.4 / 2.1.0 均含漏洞代码,2.2.0 起该入口被整体移除——即上游采用\"直接删入口\"的方式修复。
一个耐人寻味的细节:作者在 2.2.0 的 changelog 里只写了一句 \"Security: Fixed broken access control\",且指向的文件路径与实际漏洞入口并不完全一致,也没有 CVE 编号或安全公告。这类\"低调修复\"在国内自研 WordPress 插件里并不少见,也意味着大量仍在使用旧版本的站点可能并不知道自己暴露在风险中。
遗留清理:受测站点的 REST 文章列表中还发现了多个测试期产生的文章,建议运维核对并清理历史测试内容,避免成为钓鱼/仿冒素材。
六、整改建议
给还在用类似\"远程推送\"设计插件的开发者几条建议:
- 鉴权三件套缺一不可:登录态 +
current_user_can能力校验 +wp_verify_nonce;跨服务器推送场景改用 HMAC 签名(共享密钥对请求体签名 + 时间戳防重放),而不是\"放弃校验\"。 - 写操作收紧:发文/改文入口限定
publish_posts能力;目标文章 ID 不允许请求任意指定;$wpdb->insert/update必须逐列白名单化。 - 独立通道:机器间同步走独立 REST 路由(
register_rest_route+permission_callback),不要混在前台页面请求里。 - 站侧排查:管理员可自查插件列表中是否有 baiduseo,版本低于 2.2.0 的立即升级;同时在 WAF 侧拦截对前台页面 POST 且 body 含
\"BaiduSEO\"字样的请求作为过渡。 - 专项审计:对自研插件做一次\"无鉴权入口\"专项排查,重点搜索
wp_ajax_nopriv_、直接分发$_POST且无能力校验的写操作。
七、总结
这个案例的教训不复杂,但很典型:
- \"方便\"是安全的大敌。作者为了让另一台服务器推送数据省去鉴权,把整个站点的内容管理权交给了互联网上的任何人。
- 修复可以低调,但用户需要知道。没有 CVE、没有公告的静默修复,让旧版本用户的升级缺乏紧迫感。
- 未授权 + 写操作 = Critical。判断漏洞等级时,\"能不能未认证触发\"和\"触发后落到什么写路径\"是两个最硬的标尺。
附:IOC / 指纹清单(表格)
| 项目 | 内容 |
|---|---|
| 插件名 | baiduseo(目录 seo_title_baidu) |
| 受影响版本 | ≤ 2.1.x |
| 修复版本 | 2.2.0(2026-03-25) |
| 漏洞入口 | 插件 init() 中处理 $_POST['data'] 的逻辑(inc/common/index.php) |
| 特征请求 | 前台页面 POST,body 含 data={\"BaiduSEO\":<int>,...} |
| 高危操作码 | 41(覆写任意文章)、49(未认证发文) |
| 漏洞类型 | CWE-862 Missing Authorization |
参考
- WordPress.org 插件目录:baiduseo
- OWASP Broken Access Control: https://owasp.org/Top10/A01\_2021-Broken\_Access\_Control/[1]
- WordPress 插件安全最佳实践:https://developer.wordpress.org/plugins/security/[2]
引用链接
[1]https://owasp.org/Top10/A01\_2021-Broken\_Access\_Control/
[2]https://developer.wordpress.org/plugins/security/
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论