☰
Discuz站长误区:删install.lock防不住文件删除到Getshell的链条
2026/10/21 23:48:11 网站建设 项目流程

先跟你唠个真实的场景:很多用Discuz! X3.4的站长,只要一听"漏洞"两个字,第一反应就是冲到服务器上把install目录里的install.lock删掉,总以为把这个锁文件一删,别人就不能重装接管网站了。这个动作我见过太多次,群里、论坛里、甚至一些所谓的"安全教程"里都在传。

但说句扎心的话:真正想搞你站点的人,从头到尾就没打算去碰install.lock。听说过"绕锁"吗?锁只是进门的第一道坎,而攻击者要的是一整条路线:从一次看似不起眼的文件删除开始,逐步解锁、写入、触发,最终拿到网站权限——这一整套组合拳打下来,install.lock在里面连个配角都算不上。

这篇文章我就站在一个偏防守方的视角,把这条"从文件删除到Getshell"的完整链条给你拆开揉碎讲清楚。目标只有一个:让你知道漏洞是怎么串起来的,下回别再盯着一个锁文件做无用功了,真正该补的窟窿在哪儿,咱们一条一条捋。

1. 为什么“删install.lock”成了站长的默认动作

1.1 install.lock到底锁了什么

要搞明白这个问题,得先看看install.lock在Discuz里是什么角色。这个文件是Discuz安装完成之后自动生成的,位置在install目录下面。它的作用很简单:防止你重复运行安装程序。

Discuz的安装脚本install/index.php在进入安装流程之前,会先检查这个锁文件是否存在。存在,就提示"你已经安装过Discuz"之类的信息,直接终止安装流程;不存在,才允许进入下一步。本质上,它就是一个纯标记文件——用文件系统里"有没有这个文件"来表示"安装这件事做没做过"。

源码层面的逻辑大概是这个意思:

if (file_exists(DISCUZ_ROOT . './install/install.lock')) { // 已经安装过,退出 }

关键就在这儿:这个锁文件只代表"有没有装过",它不校验数据库通不通、不校验管理员密码对不对。所以很多站长觉得"删了锁就能被重装",其实只猜对了一半——删了锁确实能再次进入安装界面,但能不能装成,是另一回事。

1.2 删掉lock文件真的能重装接管吗

如果攻击者只是删掉了install.lock,然后打开install/index.php,他会看到什么?安装引导页。看上去很接近了吧?但接下来才是关键:

安装Discuz需要填数据库地址、数据库名、数据库账号密码,还需要创建管理员账号,并且要和UCenter通信。如果攻击者手里没有数据库权限,他走到第一步就卡死了——填不出正确的数据库信息,安装根本没法完成,后面的管理员创建什么都无从谈起。

反过来想,如果攻击者已经拿到了数据库权限,他还需要大费周章去删除install.lock重装吗?不需要。他完全可以直接在数据库里插一条管理员记录,或者改管理员密码,比重新安装快捷得多,也隐蔽得多。

所以结论很明确:删掉install.lock这件事,攻击者不是做不到,是不需要做。这件事对攻击者的价值,远没有站长们想象的那么大。它更像是一个"看起来在防御"的动作,实际上的防护效果,约等于在家里门上贴了一张写着"小偷勿入"的纸条。

1.3 拆掉误区:真正的攻击者在干什么

那攻击者到底在干什么?他们真正关注的是"链路",不是"单点"。

一次典型的攻破过程从来不是拿一个漏洞一次性搞定全部,而是像一个产品经理梳理用户路径一样,把目标分解成几步:

第一步,先找一个可以控制的入口,比如某个带参数的功能点、某个老插件的历史漏洞、某个没过滤的请求参数。

第二步,用这个入口做点"小事"——这个小事往往就是删除一个文件。听起来没什么危害对吧?但要看删的是哪个文件:删掉一个缓存文件,程序下次运行会重新生成;删掉一个校验开关文件,某个本来被锁住的功能就开放了;删掉一个配置备份标记,系统就以为配置没初始化完成了。

第三步,利用"重新生成""重新加载"这个过程,把你想要的内容写进去。这部分的内容可能是恶意的PHP代码,也可能是用来二次利用的脚本。

第四步,触发它。访问某个URL,或者等计划任务来跑一次,代码被执行,权限到手,Getshell完成。

你看,install.lock在这里面能干什么?它充其量只是"第二步"里众多可选目标中的一个,而且是一个根本没多大价值的目标。攻击者盯着的是整条链路上最关键的那些节点——那些删掉之后能引发"解锁→写入→执行"连锁反应的文件。

2. 文件删除漏洞:破坏只是表象,解锁和注入才是目的

2.1 任意文件删除漏洞从哪里来

聊完了攻击者视角,咱们回到漏洞本身。Discuz历史上曝出过不少文件删除类的漏洞,它们产生的根源,说来说去就是那么几类。

最常见的一类是后台功能参数过滤不严。比如某些插件提供附件管理、备份下载、日志清理之类的功能,功能本身是合法的,但实现的时候对传入的文件名参数没做足够的白名单校验。攻击者把参数改一下,传入一个带有路径穿越特征的字符串,原本只能删"backup"目录下的文件,结果删到了config目录下的敏感文件。

第二类是下载和导出功能路径可控。有一些功能是"下载指定文件"的设计,比如导出报表、下载备份包。如果开发时直接把前端传过来的文件名拼到下载路径里,攻击者可以通过构造参数,把任意文件都"下载"下来——这个虽然没有直接写入能力,但配合文件删除使用,就成了信息收集的利器。

第三类是计划任务和越权调用。Discuz的cron机制在某些版本里允许特定场景下调用计划任务,而这些任务里有文件清理的操作。如果鉴权不严,攻击者可以触发一个本不该由他触发的清理任务,进而实现删除。

有一个很典型的代码模式是:

$filename = $_GET['file']; unlink(DISCUZ_ROOT . './data/' . $filename);

明明设计意图是只让用户删除data目录下的某个指定文件,但$filename没有任何限制,攻击者传入../config/config_global.php,路径一拼接,删的就是全局配置文件了。这种代码在真实项目里出现频率极高,尤其是一些个人开发的插件和小模块里。

2.2 删除之后攻击者接下来做什么

那么问题来了,攻击者辛辛苦苦删掉一个文件,图的是什么?总不可能是为了让你网站出个报错页面吧。我梳理一下实战中常见的几种"删除后利用"方向:

第一种:删除校验文件,放开功能限制。系统里经常存在这类文件——某个功能是否开放,取决于一个标记文件是否存在,而不是靠权限系统控制。比如某个"首次运行引导"文件存在,后台就强制跳转到引导页,不让进入正常管理界面。攻击者删掉它,就等于解除了限制,可以进入本不该访问的功能区域。

第二种:删除缓存/配置,强制重新初始化。程序读取缓存文件,存在就直接用,不存在就重新从数据库或者其他地方生成。攻击者把缓存文件删掉,再想办法让程序重新生成——重新生成的过程中,如果攻击者能在"输入源"里塞点私货,那生成出来的文件就可能被污染。这个思路在模板缓存、语言包缓存、配置缓存上都成立。

第三种:删除日志,掩盖痕迹。这个相对简单粗暴,攻击者在完成一系列操作之后,把记录了他访问行为的日志文件删掉,让后续的排查无从查起。这也解释了为什么很多站点被入侵之后,站长翻日志什么都找不到——不是没记录下来,是记录已经被"组合拳"里的最后一环处理掉了。

第四种:删除备份,断掉后路。有的站点会在服务器本地存备份压缩包,攻击者先找到备份文件的位置,把它删掉。这样即使站长发现了异常,想从备份恢复,也找不到可用的备份了,被迫只能跟攻击者周旋。

2.3 安全评估时如何看待文件删除漏洞

我在给一些站点做安全评估的时候,有一个很清楚的经验:看一个文件删除漏洞的危害,不能只看它"删了什么",要看它"删完之后打开了什么"。

打个比方,一个文件删除漏洞就像一把能开锁的钥匙。钥匙本身不能让你进到金库,但如果你知道这把钥匙打开的那扇门后面是一段楼梯,楼梯尽头是金库的安检系统,那这把钥匙的价值就完全不一样了。

所以在评估一个漏洞时,我一直推荐用"受影响面"的思路去看:这个漏洞能触达哪些路径?删掉这些文件之后,系统会发生什么动作?这些动作里有没有后续可以利用的点?

我见过有一种评估报告,只写了"该漏洞可导致任意文件删除,危害等级中危",这就太浅了。正确的评估逻辑应该是先测试删除白名单内的文件,再试探穿越目录能不能删到配置或缓存,最后结合系统本身的生成机制判断有没有可能升级为代码执行。三步走完,危害等级才定得下来,而不是拿个CVSS分数就交差。

3. 从文件删除到Getshell:组合拳的典型链条拆解

3.1 链条第一环:找到可删除的“解锁点”

组合拳的第一步,是找一个值得删除的"解锁点"。这里的"解锁点"不一定是install.lock,而是任何"删掉之后会改变系统行为"的文件。

我举几个具体方向:

  • 模板缓存文件:Discuz的模板编译后生成缓存文件存在data/template目录。删掉之后,模板引擎会认为模板需要重新编译,会在下一次访问对应页面时重新生成缓存文件。
  • 数据缓存文件:类似data/cache目录下的一些序列化缓存,删掉后系统会从数据库重新拉取数据生成新的缓存。
  • 配置标记文件:某些版本里,后台某些功能依赖一个标识文件来判断是否完成了初始化,删掉它会导致初始化流程重新执行。
  • 插件目录里的锁文件:不少第三方插件也学官方搞了锁文件,用来防止重复安装或者标记运行状态。这些文件被删除后,插件可能重新进入"安装"或"配置初始化"状态,这时候配合特定的请求,就能注入攻击者想要的配置。

为什么攻击者要优先找这些点?因为它们的共同特征是"删了会重新生成"。有重新生成,就有"写入"的可能。这是整个链条承上启下的关键——单纯的删除是破坏,删除之后的重新生成才是利用。

3.2 链条第二环:制造一个可写入的机会

当你找到了一个"删了会重新生成"的文件之后,下一步就是想办法在重新生成的时候,把恶意内容写进去。

这个环节的技术核心在于:重新生成的内容,来源是哪里?

如果重新生成的内容完全来自数据库,而数据库里的数据又是经过严格过滤的,那攻击者很难插入恶意代码。但如果重新生成的内容有一部分来自用户可控的输入,那就有文章可做了。

拿模板缓存来举例,逻辑是这样的:模板引擎编译模板文件时,会把模板里的HTML和PHP标签混合编译成一个新的PHP文件存到缓存目录。如果攻击者能影响模板的内容,比如通过某个功能往模板里写入自定义代码,或者利用某些表单字段污染模板变量,那么在模板重新编译的时候,这部分污染内容就会被"原样"写进缓存文件。

缓存文件是PHP文件,里面包含恶意代码,一旦被include或者直接访问,恶意代码就会执行——这就是Getshell的入口。

类似的思路还有日志文件:网站日志会把一些请求参数写进去,攻击者构造一个包含恶意PHP代码的请求,代码被记录到日志文件里,然后用漏洞删掉日志文件触发重新生成,或者直接把日志文件作为包含目标,一样能执行。

还有一个常见方向是附件上传。Discuz有附件上传功能,上传的内容本身不直接执行,但如果你能通过某条删除链把上传目录的防护配置删掉,或者把上传目录的.htaccess删掉,那附件里的脚本代码就可能在访问时被执行。攻击者先传一个图片马,再用文件删除漏洞删掉目录下防执行的文件,图片马就从"图片"变成了"可执行的脚本"。

3.3 链条第三环:让代码执行(Getshell)

写入完成之后,最后一步是触发执行。触发方式一般有三种:

一种是直接访问。写入的恶意代码如果生成了一个可访问的PHP文件,比如在data/template目录下生成了恶意的缓存模板,攻击者直接访问这个文件路径,代码就跑了。

另一种是程序自动加载。像模板缓存这种文件,系统在正常的页面渲染流程里就会自动include它,不需要攻击者额外做什么。也就是说,恶意代码一旦被写进去,下一次任何一个用户访问相关页面,代码就会在服务器上执行,隐蔽性很高。

还有一种是计划任务触发。Discuz的cron机制会定期运行一些后台任务,如果攻击者能把恶意代码写到计划任务相关的生成文件中,那代码执行就不需要随时的主动请求,而是被动等待任务运行——这给排查增加了很多难度。

无论哪种触发方式,最终结果一样:攻击者拿到了在服务器上执行PHP代码的能力,也就是俗称的Getshell。到了这一步,网站基本等于门户洞开,后续不管是拖数据库还是留后门,都是顺手的事。

3.4 为什么叫“组合拳”:单点漏洞和链条的差距

这里要展开说一个观点:为什么单点漏洞不可怕,组合起来才可怕?

第一个原因是单点漏洞很难直接打穿。一个单纯的文件删除漏洞,如果你的系统权限配置得够好,删掉了文件系统也会自动重建,危害有限。但组合起来就不一样了——删除只是"钥匙",解锁是"开门",后续的写入和执行才是"进金库"。你单独看每一环都觉得问题不大,但串在一起就成了完整通道。

第二个原因是组合链条能绕过单点防护。比如很多站长只知道把install目录设为只读,以为这样install.lock就安全了。但攻击者删的压根不是install.lock,而是data/template下的缓存文件。你再怎么保护install目录,都不影响这条链路的推进。

第三个原因是链条的每一环都可能利用不同的漏洞。这就像一个人进小区,门禁用的是A漏洞,单元门用的是B漏洞,房门用的是C漏洞——A、B、C单独看都不是什么高危漏洞,但它们组合起来,才把一个"进不了小区"的外人变成了"坐在你客厅沙发上"的不速之客。你只堵住其中一环,攻击者可能换个顺序再来一遍。

这套组合拳的逻辑不只在Discuz上成立,放眼整个Web应用安全领域,任何一次漂亮的入侵几乎都是"组合"的结果。标题里那句"别再只删install.lock了",本质上就是提醒站长们:你的防御思路必须也变成链条式的,单点防守永远慢人一步。

4. 防御视角:从修补单点到拆除整条链条

4.1 配置加固:目录权限别再做一刀切

明白了组合拳的原理,防御思路就应该跟着升级。第一层要动的,是目录权限。

很多站长默认把所有目录都设置成755甚至777,这是非常危险的。我先给一套比较通用的权限基线:

路径权限说明
config/644 / 750含数据库配置等敏感信息,可读不可写
data/755需要写入缓存和附件,但要限制目录内脚本执行
data/template/644模板缓存目录,程序自动生成,不应允许外部写入
data/attachment/755附件目录,重点防范上传脚本被执行
uc_server/755UCenter通信中心,权限从严
install/644安装目录,建议直接改名或回收写权限

文件归属方面,要注意属主不能是PHP运行用户。如果网站目录的属主和PHP进程属主是同一个,那么即使你设置了644权限,PHP代码仍然可以通过自身进程去修改文件——这就是很多站点做了权限设置却依然被写马的原因。

具体操作上,如果你是云服务器,建议把站点文件属性改为root:www或者类似组合,PHP运行用户只读不写;data目录单独设为www:www,让程序能正常写缓存和附件。用虚拟主机的站长注意,虚拟主机一般不支持你自己改属主,那就需要靠open_basedir来限制PHP能访问的目录范围,把config目录排除在PHP可读范围之外是不现实的,但可以限制它不被脚本修改。

4.2 PHP安全参数:两个开关能挡掉大半麻烦

除了目录权限,PHP配置层面的加固同样重要。

第一个是open_basedir。它的作用是限制PHP脚本能访问的目录范围。像config目录、data目录这类敏感路径,可以通过open_basedir把PHP的可访问范围圈定在站点根目录以内,同时排除掉那些不需要被PHP直接访问的敏感目录。有一个实用经验:把open_basedir指向站点根目录和data目录的父级路径,并允许/tmp(很多缓存机制需要),但不允许越过站点边界。

第二个是disable_functions。这个配置可以禁用危险PHP函数,比如exec、system、shell_exec、passthru、popen、proc_open等。如果你的站点没有特殊需求,建议全部禁用。配合open_basedir,就算攻击者真的把恶意代码传上来了,也只能用来做一些文件读写类操作,无法直接执行系统命令,Getshell的"含金量"会大打折扣,攻破链条被打断在"触发执行"这一步之前。

4.3 应用侧加固:升级、插件与代码习惯

配置层面的加固只是基础,应用本身的维护才是大头。

最根本的一件事是升级。Discuz X3.4已经停止维护很久了,官方持续推送安全补丁的是X3.5系列。X3.5在内部做了一次大范围的安全重构,大量文件操作接口从设计上就收紧了路径校验,很多在X3.4里能用的文件删除思路到了X3.5就失效了。如果条件允许,尽早安排升级。

插件方面,第三方插件是重灾区。很多老插件多年不更新,里面全是老错误——参数没过滤、SQL拼接、文件操作无校验。我的建议是:只保留还在持续更新、口碑好的插件,其他一律删除。别舍不得,一个被攻破的站点,修复成本远比你删除一个顺手插件要高。

如果你是二次开发者,己方代码里尤其要注意文件操作类函数的使用。凡是涉及文件名、路径的地方,必须做白名单校验;凡是涉及删除、下载的接口,必须做权限二次校验。不要想当然地认为"只有管理员能进后台所以后台里的操作都安全",攻击者有很多方式绕过后台权限,比如插件漏洞直接构造请求,根本不经过你的登录流程。

4.4 自查步骤:三步定位是否已被打穿

前面说的都是预防,但万一已经被打穿了怎么办?我给你一个三步自查的思路。

第一步,查异常文件。重点看最近7天内被修改的.php文件,尤其是data目录、upload目录这类本不该频繁出现PHP文件的位置。命令一行就能搞定,核心是列出时间范围内的PHP文件并逐个排查。如果发现有不认识的PHP文件,大概率是后门。

第二步,查异常日志。看网站访问日志和PHP错误日志。异常的特征包括:大量../路径穿越特征、访问不存在文件但状态码是200、请求体积异常大、短时间内对同一接口的密集访问等。日志是链条上最容易被攻击者"清理"掉的一环,所以日志最好开启到独立的日志服务器或者对象存储上,本地一份,异地一份,防止被一锅端。

第三步,查配置差异。对比当前config目录下的配置文件是否被改动过、数据库里有没有多出你不知道的管理员账号、UCenter的应用列表里有没有异常的新应用。这三处经常被用来"留后门",排查的时候一个都不能漏。

5. 常见问题与排查技巧实录

5.1 为什么修完了又被再次入侵

我接触过很多被入侵的站点,有一个非常典型的现象:站长费了很大劲把木马清理了、把漏洞补了,结果过了一周又被打穿。为什么会这样?十有八九是"链条没拆干净"。

攻击者一旦拿到Getshell,不会只放一个文件那么简单。他可能会在数据库里插入一个新的管理员、在模板文件里藏一段加密代码、在计划任务里植入一个定期执行的脚本、甚至在系统底层留下常驻进程。你只是把所有明显可疑的PHP文件删了,但计划任务里的后门、数据库里的隐藏账号、模板文件里的加密代码都还在,过几天后门脚本自动运行,重新下载一个木马文件,站点又被接管了。

所以处理被入侵的第一原则不是"清理",是"隔离"。先断网,再排查;先备份现场,再动手清理;清理完之后,数据库密码、管理员密码、服务器登录密码全部重置一遍;最后检查计划任务和启动项,确认没有常驻后门,再谈恢复上线。

5.2 访问日志里什么都查不到,可能漏了什么

很多站长在排查时遇到的困境是:日志文件明明存在,但怎么查都查不到异常记录。这时候先别怀疑攻击者"隐身",大概率是日志在链条中被干掉了。

结合我们前面说的文件删除漏洞,攻击者完全有可能在完成利用之后,把当天日志文件直接删除,或者只保留对自己无害的那一部分。再加上有些虚拟主机的日志轮转周期长,攻击者动手那几天的日志正好不在当前可见的日志文件里。

针对这种情况,我的建议是:日志不能只留在服务器本地,必须异地存储。最简单的办法是写一个脚本,每天把当天的访问日志压缩后上传到对象存储或者另一台服务器上,服务器本地只保留最新几天的。这样即使本地日志被删除,你依然能从异地备份里找到攻击者的痕迹。这一步投入成本极低,排查时能救大命。

5.3 install.lock到底删还是不删

这个问题被问了太多次了,我在这里给个明确的态度:不删。

在正常的运营场景里,你不需要反复进入安装程序。而且如前面分析的,删掉install.lock并不等于提升安全性——攻击者根本不需要通过重装来接管站点。留着它,至少还能挡掉一些"误入安装页面顺手点一下"的过路人。

如果你确实要升级版本或者迁移站点,跟着官方升级流程走就行。官方文档会明确告诉你哪些文件需要保留、哪些步骤需要操作,完全不需要靠删除install.lock来解决问题。

5.4 一个安全基线清单

最后,把我平时给自己维护的站点定的安全基线整理成一张清单,你可以对着逐项检查:

  1. 站点运行最新稳定版本(当前是X3.5系列),不跑老版本。
  2. install目录要么删,要么改名,要么设置不可写。
  3. config目录权限为644,属主不是PHP运行用户。
  4. data目录可写,但目录内不解析PHP。
  5. PHP禁用exec等危险函数,开启open_basedir。
  6. 后台管理员使用强密码,开启二次验证(如有)。
  7. 不用的插件全部删除,使用的插件持续跟踪更新。
  8. 每日异地备份日志,定期全量备份站点和数据库。
  9. 计划任务管理入口有独立密码或权限控制。
  10. 用文件完整性校验工具(如find配合哈希对比)定期扫描可疑新文件。

这套基线的核心逻辑就是一条:不要在单点上和攻击者较劲,要在链条上切断他们的通路。

我在实际维护过程中最大的体会是——安全不是装上某个安全软件或者守住某个文件就能一劳永逸的,它是一个持续对抗的过程。攻击者在想方设法把漏洞串成链条,咱防守方就得学会看懂链条、拆掉链条。下次再看到有人跟你说"删掉install.lock就安全了",你可以把这篇文章转给他:先搞清楚组合拳是怎么打的,再回来谈防御。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询