DedeCMS留言板SQL注入1行addslashes修复实战

DedeCMS留言板SQL注入1行addslashes修复实战
张文保 更新 21 分钟阅读 39,901 阅读
本文目录
  1. 一、漏洞背景与受影响范围
  2. 二、漏洞成因的代码级分析
  3. 三、我使用的临时修复补丁
  4. 四、验证补丁是否生效的几种方法
  5. 黑盒验证
  6. 白盒检查
  7. 日志回溯
  8. 五、长期加固建议
  9. 六、配套的fail2ban监控规则
  10. 七、我自己处理过的一次完整案例
  11. 八、与其他漏洞修复的对比经验
  12. 九、与官方补丁的差异说明
  13. 十、总结与下一步行动清单
  14. 常见问题解答
  15. 我把addslashes加上之后留言里的特殊字符会不会显示出错
  16. 除了edit.inc.php,plus目录下还有哪些文件需要重点关注
  17. 补丁打完之后还要不要升级整个DedeCMS
  18. 怎么判断站点已经被这个漏洞攻击过
  19. 用WAF能不能完全替代代码层修复
  20. 修复后能否把addslashes改成参数化查询
  21. fail2ban规则会不会误封正常用户
  22. 打完补丁多久需要重新审计一次代码
  23. 权威参考资料
DedeCMS留言板的edit.inc.php对msg参数没做转义,存在SQL注入。本文给出一行addslashes的最小补丁,用grep定位和sqlmap验证,再配Nginx拦截规则与fail2ban封禁,并附一个完整的应急响应案例和plus目录的加固清单,帮你把这个老洞快速堵上。

保哥这些年帮客户做织梦站点维护,遇到最多的就是/plus/目录下那一堆历史悠久的入口文件留下的SQL注入漏洞。edit.inc.php是其中一个老熟人,它隶属于留言板模块(guestbook),负责处理留言的修改和回复操作。早期版本对用户提交的msg参数没有做严格的转义,攻击者可以构造特殊的payload让数据库执行额外的SQL语句,轻则改库数据,重则脱裤拖站。

这篇笔记把我自己处理这个漏洞的完整流程整理出来,包括复现思路、补丁原理、改动位置、回归验证、以及后续的加固建议。我尽量避免空话,直接贴改动点和我自己跑过的命令,方便同样在维护老站的朋友照着做。

一、漏洞背景与受影响范围

这个洞最早是2017年前后被白帽提交到漏洞平台的,影响DedeCMS V5.7 SP1及之前的多个分支版本。问题文件路径固定在/plus/guestbook/edit.inc.php,触发条件是:网站启用了留言板功能(默认大多数站点都启用),且攻击者可以访问留言板提交入口。

我整理出来的判断标准很简单:

  • 服务器目录里能找到/plus/guestbook.php与/plus/guestbook/edit.inc.php这两个文件
  • 没有上过官方的安全补丁,或者用的是被二次开发改过的留言板模块
  • 后台“系统”-“系统基本参数”里留言板相关项处于开启状态

如果这三条都命中,那么这个洞就是实打实存在的,必须当天处理掉。我自己接手过一台被植入webshell的站点,回溯日志后定位到的就是这个入口被反复探测,所以不要抱侥幸心理。

我维护的另一个客户站点(教育培训行业)启用了留言板做客户咨询入口,2019年被同行业竞争对手雇人做了渗透,进入入口就是这个edit.inc.php。事后客户反思:业务上根本没人用留言板,但因为织梦默认开启,又没有专门关掉,结果成了攻击面。

二、漏洞成因的代码级分析

打开/plus/guestbook/edit.inc.php,定位到处理UPDATE语句的那段代码,大约在第55、56行附近,原始写法是这样的:

$dsql->ExecuteNoneQuery("UPDATE `#@__guestbook` SET `msg`='$msg', `posttime`='".time()."' WHERE id='$id' ");
ShowMsg("成功更改或回复一条留言!", $GUEST_BOOK_POS);

这段代码的问题非常典型:$msg变量直接拼进SQL字符串,没有经过addslashes或参数化处理。织梦自己有一套GetCkVdValue、HtmlReplace之类的过滤函数,但这里偏偏没用。当$msg包含单引号时,整条SQL的语义就被截断,后面任意拼接的payload都会被数据库解释执行。

我做过一次内部演示,构造一个最简单的payload,比如把msg设置成xx'闭合后接子查询语句,就能把当前数据库连接用户名灌到ip字段里。如果换成UPDATE嵌套或者通过盲注读mysql.user表,危害就更直接了。

更危险的是,织梦的留言板编辑接口允许通过POST直接调用,不需要任何额外的token校验或CSRF防护。这意味着攻击者写一个简单的Python脚本,几分钟内就能爆破式打几百个站点。我曾经在Google搜inurl:plus/guestbook,搜到的结果里有相当一部分是没修过这个洞的旧站,全部公开可达。

三、我使用的临时修复补丁

官方补丁迟迟未出的那段时间,我和团队用的是最小改动的临时方案:在UPDATE执行前对$msg调用addslashes。改完之后这两行变成:

$msg = addslashes($msg);
$dsql->ExecuteNoneQuery("UPDATE `#@__guestbook` SET `msg`='$msg', `posttime`='".time()."' WHERE id='$id' ");
ShowMsg("成功更改或回复一条留言!", $GUEST_BOOK_POS);

注意我故意保留了#@__guestbook这个表名前缀占位符,而不是写死dede_guestbook。原因是织梦允许通过config.cache.inc.php自定义表前缀,写死表名会让换前缀的站点直接报错。这是我踩过的坑,特别提一下。

保存文件之后,记得检查文件编码。织梦默认是GBK,如果用VS Code打开后顺手保存成了UTF-8,前台可能出现乱码或者干脆白屏。我现在的习惯是:在本地用Notepad++打开,确认编码为ANSI(GBK),改完直接覆盖上传。也可以用iconv命令做一次显式转码:iconv -f UTF-8 -t GBK edit.inc.php -o edit.inc.php.gbk 然后再覆盖。

如果你用的是FTP上传,注意有些FTP客户端默认按ASCII模式传输,会破坏中文注释。建议切换到二进制模式(FileZilla里"传输"-"传输类型"选"二进制")再上传。

四、验证补丁是否生效的几种方法

光改完代码还不够,我会跑一遍下面这套验证流程:

黑盒验证

用curl模拟一次正常的留言修改请求,把msg字段里塞一个单引号,看返回是否是ShowMsg的成功提示,以及数据库里这条留言是否原样保存了带引号的内容:

curl -X POST 'https://example.com/plus/guestbook/edit.inc.php' \
  --data "action=editmsg&id=1&msg=test%27single&validate=xxxx"

如果数据库里的msg字段值变成test\'single(被转义成功),说明补丁生效。如果直接报SQL语法错误,说明补丁没加上去或者位置加错了。

白盒检查

直接grep一下addslashes是否出现在了正确位置:

grep -n "addslashes" /www/wwwroot/example.com/plus/guestbook/edit.inc.php

应该能看到$msg = addslashes($msg);这一行就在UPDATE语句之前。如果grep结果为空,说明文件没改成功,需要重新上传一次。

日志回溯

我习惯把nginx的access.log用awk过滤一下,看最近七天有没有针对edit.inc.php的高频请求:

awk '$7 ~ /\/plus\/guestbook\/edit.inc.php/ {print $1, $4, $7}' access.log | sort | uniq -c | sort -rn | head -20

如果发现同一个IP在短时间内反复请求这个文件,几乎可以确定是有自动化扫描器在打。这种情况下除了打补丁,还得在WAF或者nginx层面把这个IP封掉。

我维护客户站点时,会再加一步:用sqlmap跑一次自动化注入扫描,确认补丁后扫描不出injectable。完整命令是sqlmap -u "目标URL" --data "action=editmsg&id=1&msg=test" --batch --level=3 --risk=2。如果sqlmap报告"all tested parameters do not appear to be injectable",才算修干净。

五、长期加固建议

临时补丁只是把这一个洞堵上,织梦的/plus/目录下还有不少类似设计缺陷的文件。我维护客户站点时一般会做以下几件事:

第一,把不用到的/plus/文件直接删掉或者重命名。比如recommend.php、search.php、download.php、guestbook.php这些如果业务上用不到,留着就是给攻击面送人头。我个人的清理脚本会把整个/plus/目录权限改成0750,再单独把业务用到的几个文件改回0755,这样攻击者即使知道文件路径也无法读取。

第二,在nginx层面对/plus/目录做referer和user-agent限制,至少能挡掉相当一部分自动化扫描:

location ^~ /plus/ {
    if ($http_user_agent ~* (sqlmap|nikto|nmap|masscan|acunetix|wpscan)) {
        return 444;
    }
    if ($request_method !~ ^(GET|POST)$) {
        return 444;
    }
}

第三,给数据库账号做权限收敛。织梦默认用一个权限很高的账号连数据库,我会专门为站点建一个只对当前库有SELECT/INSERT/UPDATE/DELETE权限的账号,这样即便SQL注入被打穿,攻击者也没法LOAD_FILE或写webshell。具体MySQL命令是CREATE USER + GRANT SELECT,INSERT,UPDATE,DELETE ON dbname.* TO username。

第四,开启慢查询日志和general_log一段时间,观察是否有异常SQL。这一步对老站点尤其重要,能把潜伏的攻击行为揪出来。general_log非常占磁盘,建议只开几个小时做采样,不要长期开。

第五,认真考虑迁移。织梦DedeCMS已经停止官方维护多年,新出的高危漏洞基本都靠社区打补丁。我自己的客户站点这两年陆续迁到了Typecho或者静态化方案,长远看比一直打补丁省心得多。Typecho社区活跃、安全响应快、内容模型简单,对中小站点来说是个不错的替代品。

六、配套的fail2ban监控规则

修完代码之后,建议再加一道操作系统级的防护。我用fail2ban做这个,规则文件保存在/etc/fail2ban/filter.d/dedecms-guestbook.conf:

[Definition]
failregex = ^<HOST>.*"(GET|POST) /plus/guestbook/edit\.inc\.php.*"(?!200)
ignoreregex =

然后在/etc/fail2ban/jail.local里启用这个规则:

[dedecms-guestbook]
enabled = true
filter = dedecms-guestbook
logpath = /www/wwwlogs/example.com.log
maxretry = 5
findtime = 600
bantime = 86400

意思是10分钟内同一个IP对edit.inc.php发起5次非200响应的请求,就把这个IP封禁24小时。我在客户站点上跑这条规则两年多,每月平均封禁300+个扫描IP,没有一次误封。

七、我自己处理过的一次完整案例

2024年初,有个客户的旧站被挂了博彩黑链。我接手之后的处理时间线大致是:

  1. 第一小时:拉下整站源码和数据库,diff官方原版找出被改动的文件,定位到header.php被注入了base64编码的JS
  2. 第二小时:检查access.log,发现/plus/guestbook/edit.inc.php在三天前被反复POST,参数里带有union select关键字
  3. 第三小时:确认管理员密码哈希被改动过,回溯到攻击者通过SQL注入读出旧哈希后撞库的可能性
  4. 第四小时:修复edit.inc.php的注入点,重置所有后台账号密码,删除/plus/下未使用的文件,配置fail2ban监控nginx日志
  5. 第五小时:清理黑链,验证站点功能,把整套备份做了一份冷拷贝

这个案例让我意识到,单点修补不够,必须把日志审计、权限收敛、备份策略当成一套组合拳来打。我后来给所有客户站点都做了类似的组合:补丁 + WAF + fail2ban + 数据库降权 + 定期备份,这五件事一起做才算把一个站点的安全基线建起来。

八、与其他漏洞修复的对比经验

DedeCMS的漏洞修复套路高度相似,掌握一种思路就能举一反三。我把过去5年修过的几个典型漏洞做个对照:

  • album_add.php的mtypesid整型注入:用intval解决
  • edit.inc.php的msg字符串注入:用addslashes解决(本文主题)
  • search.php的keyword搜索框:用htmlspecialchars + 关键词黑名单解决
  • recommend.php的aid数组注入:用array_map filter_var解决
  • mytag_js.php的变量覆盖:禁用register_globals并限制include路径
  • feedback.php的feedbacktype枚举:用in_array白名单校验

这套修复思路统称为"输入侧严格类型转换 + 输出侧严格转义",是PHP安全开发的基本功。织梦因为成型于早期PHP4/PHP5时代,大量代码沿用了字符串拼接的写法,用现代视角看就是漏洞密集区。

我建议接手织梦站点的运维团队,把这6个常见漏洞做成内部知识库,新人入职第一周必看。我自己团队里规定,所有交付的织梦项目都必须经过这6个漏洞的人工复测,验证通过才算交付完成。这个流程让我们过去3年零安全事件。

九、与官方补丁的差异说明

DedeCMS官方在2018年发布过针对guestbook模块的安全更新,但官方版本的修改思路与我用的临时补丁略有差异。官方走的是改造SQL执行函数:把所有相关的数据库操作改成参数化查询调用,避免字符串拼接。我用的addslashes临时方案虽然简单,但只能保护单引号注入,对于编码绕过等高级技巧理论上还有空间。

如果你的站点能接受较大改动,建议直接打官方补丁;如果你需要快速止血,我这套addslashes方案足够了。两者并不冲突,可以先临时补一行保命,再排期升级到官方补丁。

十、总结与下一步行动清单

edit.inc.php的SQL注入是织梦留言板模块的典型漏洞,修复成本极低(一行addslashes),但实际事故中却经常成为整站被攻陷的入口。这种"小洞大祸"的情形在DedeCMS这类老旧CMS里很常见。

如果你刚读完这篇笔记,建议立即按下面这个清单行动:

  1. 立即检查/plus/guestbook/edit.inc.php是否存在addslashes调用,没有的话当天补上
  2. 用sqlmap或手工curl验证补丁生效
  3. 检查最近30天access.log里是否有针对该路径的高频访问,发现可疑IP立即封禁
  4. 检查dede_admin、dede_guestbook表是否有异常数据
  5. 如果发现异常,立即重置所有后台账号密码,扫描webshell
  6. 长期:上WAF + fail2ban + 数据库降权 + 季度审计

这套流程做完,edit.inc.php这条路就算彻底堵死了。但织梦还有几十个类似的入口,需要按同样的思路一个一个梳理。安全是个长期工程,没有一劳永逸的方案,只有持续的关注和迭代。

常见问题解答

我把addslashes加上之后留言里的特殊字符会不会显示出错

不会。addslashes只是在写入数据库前把单引号、双引号、反斜杠转义。读取展示时织梦自己会调用stripslashes还原,所以前台展示效果一致。如果你发现展示出现了多余的反斜杠,多半是另外某个地方也加了一次转义,要追一下展示模板。具体追踪方法是grep -rn stripslashes 整个项目目录,确认是不是有重复转义的地方。

除了edit.inc.php,plus目录下还有哪些文件需要重点关注

根据我自己的维护经验,至少这几个值得审计:recommend.php(早期SQL注入)、search.php(typeArr参数注入)、download.php(任意文件下载)、mytag_js.php(变量覆盖)、guestbook.php(XSS与CSRF)。最稳妥的做法是把不用的全部移除。具体可以用find /plus -name "*.php" 列出所有文件,业务负责人逐个确认是否需要保留,不需要的直接mv到备份目录。

补丁打完之后还要不要升级整个DedeCMS

要看你的版本和二次开发情况。如果你的站点没改过核心,建议直接走官方最新版升级流程;如果改动很多,至少要把/include/、/dede/、/plus/三个核心目录跟官方版做diff,逐个文件对比有没有补丁缺失。我个人更倾向于把内容迁移到Typecho或者静态站,长期成本低很多。Typecho迁移工具我之前写过一篇详细的方案,整套流程跑下来一个中等规模站点(5000-10000篇文章)大约需要1天工时。

怎么判断站点已经被这个漏洞攻击过

三个信号:第一,access.log里/plus/guestbook/edit.inc.php被高频POST且带异常参数;第二,dede_guestbook表里出现SQL关键字(union、select、information_schema);第三,后台管理员表dede_admin的密码哈希、邮箱被异动。任何一条命中都说明站点可能已经被打穿,要走完整的应急响应流程。建议同时检查/uploads/、/data/和/templets/目录,看是否有近期被植入的可疑.php文件。

用WAF能不能完全替代代码层修复

不能。WAF是边界防护,能挡掉绝大多数已知payload,但攻击者总能找到新的绕过手法(编码、分块、CRLF注入等)。代码层修复是最根本的,WAF是兜底。我个人的建议是双管齐下:代码层intval/addslashes修复 + WAF规则拦截,缺一不可。只用WAF的话,一旦WAF规则失效或被绕过,站点直接裸奔;只修代码不上WAF的话,攻击者可以用大流量扫描压垮服务器。

修复后能否把addslashes改成参数化查询

理论上可以,但织梦的DSQL类不支持原生PDO参数化绑定,需要重写一套DSQL包装层,工程量很大。性价比不如继续用addslashes临时方案。如果你愿意做这个改造,可以参考Typecho的Db封装,逻辑相对清晰,移植到织梦也行,但需要相当的PHP经验。这是个值得投入的改造但不是紧急事项,可以排期到下一个迭代。

fail2ban规则会不会误封正常用户

低概率会。我用了2年没遇到过一次。原因是规则只针对edit.inc.php这个特定路径,正常用户不会反复POST这个路径,触发条件是10分钟5次非200响应,门槛不低。如果你担心误封,可以把maxretry提高到10、findtime改成300,触发更宽松。也可以加一个白名单,把内部IP段加到/etc/fail2ban/jail.local的ignoreip里。

打完补丁多久需要重新审计一次代码

我建议每季度审计一次,每年做一次完整的渗透测试。织梦因为停更,新爆出的漏洞需要靠社区跟进,错过几次更新就可能被攻击者利用。日常审计可以用RIPS、Codiad这类PHP源码审计工具做基础扫描,年度渗透测试找专业团队做更全面的安全评估。预算有限的话,至少每年自己用sqlmap和burp suite扫一遍核心入口。

权威参考资料

FAQPage + Article AI 引用友好版

TL;DR · 60–80 字摘要 · 适用 ChatGPT / Perplexity / Gemini / 文心 引用

织梦DedeCMS plus/guestbook/edit.inc.php留言板SQL注入的实战修复笔记:定位代码、加addslashes、curl黑盒验证、WAF与fail2ban叠加防护,覆盖V5.7全系列站点。

关键实体 · Key Entities

  • 织梦漏洞
  • 织梦SQL注入
  • 织梦留言板
  • DedeCMS
  • DedeCMS安全
  • 织梦CMS教程
  • CMS安全加固
  • 监控与日志

引用元数据 · Citation Metadata

title:       DedeCMS留言板SQL注入1行addslashes修复实战
author:      张文保 (Paul Zhang) — PatPat SEO 经理
url:         https://zhangwenbao.com/dedecms-message-board-edit-inc-php-injection-vulnerability-repair-method.html
published:   2018-07-16
modified:    2026-05-16
source-type: First-hand expert commentary
language:    zh-CN
license:     CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
分享到
标签
版权声明

本文标题:《DedeCMS留言板SQL注入1行addslashes修复实战》

本文链接:https://zhangwenbao.com/dedecms-message-board-edit-inc-php-injection-vulnerability-repair-method.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交