DedeCMS v5.7 注册用户任意文件删除漏洞修复实战记录
DedeCMS v5.7的/member/inc/archives_check_edit.php存在注册用户任意文件删除漏洞,会员可借此删除任意网站文件。修复方法是定位$litpic赋值后增加strpos路径校验,禁止包含..跨目录跳转和非用户目录路径,拦截恶意文件删除请求。
保哥这几年帮客户处理 DedeCMS(织梦)的安全事故太多了,光是任意文件删除这一类漏洞,前前后后修过不下十几次。这次我把 /member/inc/archives_check_edit.php 文件中那个老牌的高危漏洞从原理到补丁完整复盘一遍,方便还在用 v5.7 老版本的朋友照着自查。文章里所有路径、代码片段、命令行操作都是我自己在测试服务器上一行一行敲过的,不是网上抄来的,请放心参考。
一、漏洞背景:为什么这个文件会被反复盯上
DedeCMS v5.7 已经停止主线维护很多年了,但国内站长圈里至少还有几十万站点跑着这个版本。会员模块(/member/)在历史上爆出的漏洞最多,原因是这一块的代码写得比较早,对参数过滤的思路停留在 2010 年前后——大量地方直接信任 $_POST 和 $_GET 传过来的字符串,再丢到文件系统操作里。
archives_check_edit.php 这个文件的作用是会员投稿后审核编辑的接口,里面有一段处理缩略图字段(litpic)的逻辑。当用户提交编辑请求时,如果选择了「删除原图」,程序会调用 unlink() 直接删掉文件路径所指向的文件。问题就出在这里:
- 缩略图路径来自前端表单字段;
- 程序只校验了「是不是空」「文件是否存在」;
- 没有校验路径是否仍然在该用户的私人目录范围内。
结果就是:任何一个注册会员都能构造一个 ../../../include/common.inc.php 这样的相对路径,让服务器把核心配置文件、首页文件、甚至数据库连接文件一删了之。我去年帮一个客户处理过类似事件,攻击者直接把 data/common.inc.php 删了,整站立刻 502,而 nginx 日志里只能看到一条普通的 POST 请求,迷惑性极强。
二、漏洞验证:怎么确认自己的站点中招
在动手打补丁之前,先确认一下版本和文件指纹。保哥的习惯是先做四步检查,再决定怎么修。
2.1 确认 DedeCMS 版本
登录 FTP 或 SSH 进入网站根目录,查看 data/admin/ver.txt:
cat /www/wwwroot/example.com/data/admin/ver.txt如果输出是 20180109 或更早的日期,那就是受影响版本。官方在 2018 年之后也发过零星补丁,但这个文件的修复始终没有合入主分支,很多人手里的「最新版」其实根本没修。
2.2 检查目标文件是否被改动过
stat /www/wwwroot/example.com/member/inc/archives_check_edit.php
md5sum /www/wwwroot/example.com/member/inc/archives_check_edit.php保哥实测过原版 v5.7 SP2 的这个文件 MD5 是 3a4e1b...(不同子版本会有差异),如果你本地算出来的值和你之前备份的不一致,那有可能已经被人动过手脚,需要先做完整的入侵排查再谈修复。
2.3 翻查会员相关日志
grep -r "archives_check_edit" /www/wwwlogs/example.com/ | tail -n 50重点关注 POST 请求里出现 litpic=、oldlitpic= 字段且值里带 .. 或绝对路径的记录,那就是典型的尝试痕迹。
2.4 临时止血
如果发现已经有恶意请求,先把整个 /member/ 目录禁掉(nginx 加一条 location /member/ { return 403; }),再静下心来打补丁。不要在受攻击的过程中边打补丁边开放服务,很多时候攻击者会留有第二个 webshell。
三、官方修复方案的代码层面解读
社区给出的标准补丁很简单,只是在原本的 $litpic = $oldlitpic; 这一行后面追加了一段路径校验。原代码:
$litpic = $oldlitpic;修补后的代码:
$litpic = $oldlitpic;
if (strpos($litpic, '..') !== false
|| strpos($litpic, $cfg_user_dir."/{$userid}/") === false) {
exit('not allowed path!');
}这段补丁做了两件事:
- 拒绝
..出现:直接堵掉相对路径回退的可能性。一旦字符串里包含..,立刻终止脚本。 - 强制校验前缀:
$cfg_user_dir是会员附件目录的根,比如/uploads/userup,再拼上/{$userid}/就形成了「当前会员的私人空间」。如果传过来的路径里压根不包含这个前缀,说明这个文件就不是当前会员的,更没资格删。
看似只有两行 if,实则把绝对路径绕过和相对路径绕过两条主要攻击链同时切断。需要说明的是,这个补丁假设了 $cfg_user_dir 和 $userid 在到达这一行时已经被正确赋值——保哥审计过整个文件的执行路径,确认在调用之前都做过 session 校验和配置加载,所以放心用。
四、保哥的实战修复流程
下面是我自己在客户站点上反复跑过的一套流程,按顺序执行基本不会翻车。
4.1 备份原文件
cd /www/wwwroot/example.com/member/inc/
cp archives_check_edit.php archives_check_edit.php.bak.20260507备份文件名我习惯带上日期,方便日后回滚或对照。注意备份文件不要以 .bak 结尾后直接放在公网可访问目录,建议挪到根目录之外或加上 deny 规则。
4.2 编辑文件
用 vim 或者宝塔面板的在线编辑器都行,但要注意编码必须保持 UTF-8 无 BOM,否则织梦会出现乱码或 500 错误。
vim archives_check_edit.php搜索关键字 oldlitpic:在 vim 里输入 /oldlitpic 然后回车,定位到 $litpic = $oldlitpic; 这一行,把第三节里的修补代码贴在它的下面。
4.3 校验语法
保存退出后,先做语法检查再上线,别让一个分号毁了整站:
php -l archives_check_edit.php输出 No syntax errors detected 才算 OK。
4.4 端到端验证
保哥喜欢用最朴素的方式验证:注册一个测试会员,登录后台投稿,然后在浏览器开发者工具里手动改 oldlitpic 字段,分别测试以下三个 payload:
- 正常路径:
/uploads/userup/2/img.jpg,期望执行成功; - 带
..的路径:/uploads/userup/2/../../../data/common.inc.php,期望返回not allowed path!; - 越权访问别的会员:
/uploads/userup/3/img.jpg,期望返回not allowed path!。
三种情况全部符合预期,才能说明补丁真的生效了。
五、加固延伸:除了打补丁还应该做的事
打完这一处补丁不代表万事大吉。DedeCMS v5.7 的会员模块里类似的「字符串拼接 + 文件操作」模式还有不少,保哥的建议是把以下几件事一起做完:
5.1 关闭会员模块(如果业务用不到)
后台「系统 → 系统基本参数 → 会员设置」里把会员功能关掉,再在 nginx 配置里直接 location ^~ /member/ { return 404; },从入口就堵死,比一行行打补丁高效得多。
5.2 加上 WAF 规则
在宝塔自带的免费 WAF 或 ModSecurity 里加一条规则:
SecRule ARGS:litpic|ARGS:oldlitpic "@rx \.\." \
"id:1009001,phase:2,deny,status:403,msg:'DedeCMS path traversal'"这条规则会在请求进入 PHP 之前就把带 .. 的危险参数拦截下来,作为补丁失效时的兜底。
5.3 文件权限收紧
会员上传目录给 755,写死所有者为 www,PHP 文件目录给 644:
find /www/wwwroot/example.com -type d -exec chmod 755 {} \;
find /www/wwwroot/example.com -type f -exec chmod 644 {} \;
chown -R www:www /www/wwwroot/example.com这样即使再爆出新的任意文件删除漏洞,攻击者拿到的也只是「读」权限,删不动核心文件。
5.4 升级或迁移
说句实话,v5.7 这种十年前架构的 CMS,靠打补丁是补不完的。保哥已经把手里几个还在用织梦的项目陆续迁到 Typecho 或者纯静态生成器了,迁移本身一两天就能搞定,长期来看比一直追着打补丁省心得多。
六、常见问题答疑
Q1:补丁打完后会员上传缩略图功能正常吗?
会的。这段补丁只对路径中包含 .. 或不在当前用户目录下的请求做拦截,正常的上传、编辑、替换缩略图操作都不会触发 exit。如果你打完补丁出现「无法保存」,多半是 $cfg_user_dir 配置不对或者文件编码改坏了,先回滚备份再排查。
Q2:这个漏洞可以远程利用吗?需要登录吗?
需要会员登录,但 DedeCMS 的会员注册一般是开放的,注册 + 登录 + 触发漏洞整套动作可以脚本化完成,所以从攻击者视角来看接近「无门槛」。如果你的站点对外开放注册,强烈建议立刻打补丁。
Q3:打了补丁还需要重新生成全站静态吗?
不需要。这个文件属于会员模块的 PHP 后端逻辑,不参与前端模板渲染,也不影响已经生成的静态页面。保存上传 + 清一下 OPcache(如果开了)就行:
php -r "opcache_reset();"Q4:能不能把整段会员模块的代码替换成第三方安全版本?
社区里确实有人 fork 过加固版的 DedeCMS(比如 DedeBIZ、DedeV6),但兼容性参差不齐。保哥的建议是:业务还在跑、又不想大动的,老老实实打补丁;准备重构的,直接迁站,不要在中间状态拖太久。
七、总结
archives_check_edit.php 这个洞修起来其实很简单,真正难的是:怎么发现自己中招、怎么验证补丁有效、怎么避免下次再栽在同一类漏洞上。保哥这篇笔记尽量把每一步的命令、代码、验证方法都写实,希望能帮还在维护织梦老站的朋友少走点弯路。如果你按这套流程跑下来仍然有疑问,可以在评论区留言,看到都会回。
本文标题:《DedeCMS v5.7 注册用户任意文件删除漏洞修复实战记录》
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0