WordPress post.php 任意文件删除漏洞临时修复实战记录
WordPress 4.9.6及以下版本post.php存在任意文件删除漏洞,攻击者可通过构造附件thumb路径删除任意文件,临时修复方法是在functions.php添加wp_update_attachment_metadata过滤代码。
保哥维护过不少 WordPress 站点,2018 年 RIPS 公开 post.php 任意文件删除漏洞那段时间,我手上正好有一批多作者投稿型的博客在跑,差不多一周内全部排查并打了临时补丁。这篇笔记把我当时整理给团队的处理手册再次梳理一遍,包括漏洞机理、临时方案的代码位置、验证手段、以及后续我们做的几层加固。
这个洞影响 WordPress 4.9.6 及之前的所有版本,触发条件是攻击者拥有 author 或更高权限。看上去门槛不低,但只要站点开放了投稿注册、或者管理员账号被弱口令打穿,攻击者立刻就能拿到这个能力。能删的文件不仅是上传目录里的图片,连 wp-config.php 这种敏感文件都能被删除——一旦 wp-config.php 没了,再次访问站点会进入安装引导界面,攻击者就能用自己的数据库重新初始化、把站点收编。
漏洞原理:thumb 参数没做路径净化
洞出在 wp-admin/post.php 处理附件元数据更新的逻辑里。WordPress 会读取 _wp_attachment_metadata 这个 post meta,里面有一个 thumb 字段记录缩略图相对路径。当附件被删除时,WordPress 会把附件原图所在目录拼上 thumb 的值,然后调用 wp_delete_attachment_files 把它干掉。
关键在于 thumb 字段允许 author 通过 wp_update_attachment_metadata 钩子写入,且没有任何路径净化。攻击者可以把 thumb 写成 ../../../../wp-config.php 这种穿越路径,删除附件时就会顺手把 wp-config.php 删掉。
这个洞最致命的地方在于触发链条只需要两步 HTTP 请求:第一步上传任意附件,第二步用 POST 修改这个附件的 meta,把 thumb 改成穿越路径,然后触发删除。完整 PoC 当年在 RIPS 博客已经放出,我就不在这里重复了。
我用的临时修复方案
当时官方补丁还没正式发布,社区给出的最快修复思路是:在 wp_update_attachment_metadata 这个 filter 上挂一个回调,强制把 thumb 字段里的目录部分剥掉,只保留文件名。代码加在当前主题的 functions.php 里:
add_filter( 'wp_update_attachment_metadata', 'rips_unlink_tempfix' );
function rips_unlink_tempfix( $data ) {
if ( isset( $data['thumb'] ) ) {
$data['thumb'] = basename( $data['thumb'] );
}
return $data;
}basename 这个 PHP 函数会把传入字符串里的所有目录分隔符去掉,只留下最后一个文件名片段。即便攻击者传入 ../../../../wp-config.php,经过这个 filter 之后会变成 wp-config.php,再拼上附件原目录之后,删除的就是该附件目录下名为 wp-config.php 的文件,而不是网站根目录的真正配置文件。
我这里把代码放进主题的 functions.php,主要原因是好回滚——官方补丁出了之后直接把这段删掉就行,不影响别的逻辑。但要注意:如果你换主题,这段代码也会失效。更稳妥的做法是把它做成 mu-plugin。
改成 mu-plugin 的版本
我后来在多站点环境下用的是 mu-plugin 版本,路径是 wp-content/mu-plugins/rips-tempfix.php:
<?php
/**
* Plugin Name: RIPS post.php Arbitrary File Deletion Tempfix
* Description: 临时修复 RIPS 公开的 wp-admin/post.php 任意文件删除漏洞,等待官方补丁。
* Version: 1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_filter( 'wp_update_attachment_metadata', function ( $data ) {
if ( isset( $data['thumb'] ) ) {
$data['thumb'] = basename( $data['thumb'] );
}
return $data;
} );mu-plugin 不受切换主题影响,且在站点初始化阶段自动加载,覆盖度比写在 functions.php 高。我现在维护新站点时,凡是这种「临时但全局生效」的补丁都习惯用 mu-plugin 来管理。
补丁验证流程
打完补丁我一般会做下面三件事来确认它真的生效:
用 author 账号实际跑一遍 PoC
建立一个 author 角色的测试账号,按 PoC 流程上传图片、修改 meta、触发删除,然后看 wp-config.php 是否还在。如果文件没被删,并且站点其它功能正常,补丁就算过关。
检查 hook 是否被正确挂载
在 WP-CLI 环境里跑:
wp eval 'global $wp_filter; print_r( isset($wp_filter["wp_update_attachment_metadata"]) ? "hooked" : "missing" );'返回 hooked 表明 filter 生效。如果返回 missing,那就要回去检查 functions.php 或 mu-plugin 是否真的被加载了。
审计 wp_postmeta 是否有可疑数据
在打补丁的同时,我会扫一遍数据库里的 wp_postmeta 表,看 _wp_attachment_metadata 字段值有没有包含 .. 或 / 之类的可疑路径:
SELECT post_id, meta_value
FROM wp_postmeta
WHERE meta_key = '_wp_attachment_metadata'
AND meta_value LIKE '%thumb%..%';如果命中了记录,要逐条人工确认是不是攻击者预先种下的「定时炸弹」。我之前接手的一个站点就在这一步发现了三条异常记录,攻击者还没来得及触发删除就被我们拦下了。
后续的多层加固
打了临时补丁不代表万事大吉。我习惯把这件事当作一次提醒,顺手做几件长期受益的事情:
第一件,关掉公开注册或者把默认角色改成 subscriber。subscriber 角色没有上传附件的权限,从根上断了攻击者拿到 author 能力的可能。
第二件,给 wp-config.php 加上文件系统层的写保护。Linux 下可以用 chattr +i wp-config.php 给它上不可变属性,这样即便代码逻辑漏掉了某个删除入口,文件也删不掉。需要更新配置时再 chattr -i 解开。
第三件,开启 WordPress 自动小版本更新,并把核心、主题、插件的版本通过监控告警出来。我用的是简单的 cron 脚本:
#!/bin/bash
cd /www/wwwroot/example.com
wp core check-update --format=json > /tmp/wp-update.json
if grep -q 'version' /tmp/wp-update.json; then
mail -s "WordPress 核心待更新" admin@example.com < /tmp/wp-update.json
fi第四件,配置 WAF 规则拦截带 .. 的 thumb 参数。这个层面的拦截作为兜底很有用,因为它不依赖 PHP 代码本身的正确性。
第五件,做一份冷备份。所有补丁、所有 WAF、所有限制都不如一份「断网保存的」昨日备份来得实在。我个人推荐每天打一次全量包,scp 到不联网的备份机上。
我自己踩过的一个坑
当年给一个客户站点上这个补丁,我顺手把代码写进了主题的 functions.php。没过多久,客户因为视觉调整换了主题,补丁连带一起没了。一周后被自动化扫描盯上,差点被删 wp-config.php,幸亏 WAF 规则那一层拦住了带 .. 的请求。这件事之后我就把所有「不能丢」的补丁全部 mu-plugin 化,再也没出过类似事故。
FAQ
Q1:补丁打完之后,官方正式补丁还要不要升级?
必须升级。临时补丁只是堵住了已知的触发路径,官方补丁通常还会修一些相邻代码、加上权限校验、补齐路径净化。WordPress 4.9.7 是修这个洞的版本,4.9.x 分支后续也持续在打补丁。我的建议是把核心永远保持在最新小版本上。
Q2:subscriber 角色还会受到这个漏洞影响吗?
不会。subscriber 没有 upload_files 权限,无法上传附件,自然无法构造触发 PoC。但要注意如果你装了某些插件给 subscriber 加了上传能力(很多前端投稿插件会这么做),就要重新评估。
Q3:把 thumb 字段直接清空可以吗?
技术上可以,但会破坏站点功能。thumb 字段保存的是附件的缩略图路径,前台主题或某些插件可能依赖这个字段渲染。basename 这种「保留文件名、剥掉路径」的做法是最稳妥的折中方案。
Q4:如何判断站点过去是否已经被这个洞利用过?
三条线索可以查:第一,对比 wp-config.php 的 mtime 与站点其它核心文件,如果异常新且没人记得改过它,要警惕;第二,扫 wp_postmeta 表里 _wp_attachment_metadata 字段是否有 .. 路径;第三,查 wp-content/uploads 目录下是否莫名少了一些日期目录。任何一条命中就要走应急响应:备份现状、改密码、重置 SALT、扫描后门。
本文标题:《WordPress post.php 任意文件删除漏洞临时修复实战记录》
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0