
1. 项目概述一次针对Cacti高危漏洞的实战化渗透测试最近在复现和分析一些历史高危漏洞时Cacti监控系统中的CVE-2023-39361和CVE-2024-31459引起了我的注意。这两个漏洞都源于graph_view.php文件中的SQL注入点并且能够进一步导致远程代码执行危害等级非常高。很多安全从业者和爱好者可能都听说过sqlmap这个神器但如何将其精准地应用于一个真实、复杂的漏洞场景尤其是像这种需要多步利用的链式漏洞其中的门道不少。今天我就结合这次对Cacti漏洞的实战测试从头到尾拆解一遍如何利用sqlmap进行深度注入并最终理解其通向RCE的原理。无论你是正在学习Web安全的初学者还是想深化工具使用技巧的进阶者这篇从环境搭建、漏洞定位、工具利用到原理剖析的完整记录应该都能给你带来一些直接的参考。简单来说我们的目标是从一个已知的漏洞公告CVE编号出发搭建或定位存在漏洞的Cacti环境找到那个存在问题的graph_view.php文件及其参数使用sqlmap自动化验证并利用SQL注入漏洞进而分析其如何与Cacti的系统特性结合导致远程代码执行。这个过程不仅涉及sqlmap命令的灵活运用更关键的是理解漏洞的上下文和后续利用链的构造逻辑。我会把踩过的坑、参数调整的思考以及手动验证的技巧都揉碎了讲清楚。2. 漏洞背景与Cacti环境认知在动手之前我们得先搞清楚目标是什么。Cacti是一套基于PHP/MySQL的开源网络图形化监控工具它通过SNMP协议获取数据然后用RRDtool绘制成直观的图表。在运维圈子里它的应用非常广泛。graph_view.php是其中一个用于查看监控图表的核心页面。CVE-2023-39361和CVE-2024-31459这两个漏洞本质上是同一个graph_view.php文件中对graph_start和graph_end等参数过滤不严导致的二次SQL注入。攻击者通过注入恶意SQL语句可以篡改数据库查询。更危险的是Cacti的部分功能如poller_output表的数据处理会执行数据库中存在的数据如果通过SQL注入将恶意代码写入数据库的特定字段随后当Cacti的后台进程或特定页面读取并执行这些数据时就可能触发远程代码执行。注意本文所有操作均在授权测试环境如自行搭建的漏洞靶场中进行。严禁对任何未经授权的系统进行测试此举违法且违背职业道德。理解这个“SQL注入 - 数据写入 - 后续触发执行”的链式逻辑至关重要。它不再是简单的“注入即获取数据”而是需要利用注入点作为入口向数据库的“特定位置”写入“特定内容”然后等待或触发另一个“执行流程”。这要求我们对sqlmap的使用不能停留在简单的--dbs、--tables而要深入到--file-write、--os-shell失败时的替代方案即通过--sql-query或--sql-shell来精确操纵数据库内容。为了复现你需要一个存在漏洞的Cacti环境。通常我们可以从Cacti官网下载历史版本例如受影响的1.2.24之前版本进行安装。安装过程需要LAMP环境这里不过多赘述。关键是要确保安装的版本是存在漏洞的。安装完成后访问http://your-ip/cacti/graph_view.php应该能看到页面。3. 手工验证与漏洞点定位在祭出sqlmap这类自动化工具前我强烈建议先进行简单的手工验证。这能帮你快速理解漏洞触发的条件和参数为后续使用工具提供关键信息。首先我们访问graph_view.php页面。通过观察URL和表单通常会发现它接收诸如local_graph_id、graph_start、graph_end等参数来控制显示哪个图表以及时间范围。漏洞公告指出graph_start和graph_end是存在问题的参数。一个基础的手工测试是在参数值后添加一个单引号。例如访问http://your-ip/cacti/graph_view.php?actionviewlocal_graph_id1graph_start0graph_end100观察页面返回。如果页面返回了数据库错误信息如MySQL的语法错误那么基本确认存在SQL注入点。有时错误信息可能被屏蔽页面返回空白或与正常响应不同如缺少部分图表元素这也是一种间接的迹象。进一步我们可以尝试经典的and 11和and 12逻辑测试...graph_end100 and 11 -- -...graph_end100 and 12 -- -如果第一个请求返回正常页面第二个请求返回异常如图表加载失败、页面布局错乱那么就可以基本断定该参数存在数值型或可被转换为数值型的SQL注入。这里的-- -是注释符用于注释掉原SQL语句中后续的部分。在我的测试中发现graph_end参数对敏感且and 11与and 12返回结果不同确认存在注入。并且初步判断为数字型注入因为直接拼接数字和SQL语句片段似乎被数据库执行了。实操心得手工验证这一步不能省。它不仅能帮你确认漏洞更重要的是让你了解应用程序的“脾气”。比如它是否开启了错误回显过滤了哪些特殊字符是GET还是POST请求这些信息对于后续配置sqlmap的--level、--risk、--tamper等参数有直接指导作用。盲目上sqlmap可能会导致漏报或误报。4. sqlmap深度配置与注入利用确认漏洞存在后我们就可以使用sqlmap进行自动化、深度化的利用。sqlmap的强大在于其丰富的载荷库和绕过技巧但要想用好必须根据目标情况精细配置。一个最基本的启动命令如下sqlmap -u http://your-ip/cacti/graph_view.php?actionviewlocal_graph_id1graph_start0graph_end100 -p graph_end-u: 指定目标URL。-p: 指定需要测试的参数这里我们指定graph_end。如果不指定sqlmap会测试URL和Body中的所有参数速度较慢。运行后sqlmap会询问你是否跳过其他参数的测试选择Y。很快它应该能检测到基于布尔盲注和基于时间的盲注漏洞。但是我们的目标不仅仅是检测。我们需要利用这个注入点向数据库写入能导致RCE的数据。这就需要用到sqlmap更高级的功能。第一步获取数据库信息在确认注入点后我们首先获取当前数据库名、用户等信息这有助于理解我们在数据库中的权限。sqlmap -u http://your-ip/cacti/graph_view.php?actionviewlocal_graph_id1graph_start0graph_end100 -p graph_end --current-db --current-user如果返回成功你可能会看到当前数据库是cacti用户可能是cactiuserlocalhost。这很重要因为写入文件等操作需要高权限。第二步枚举表结构寻找关键表导致RCE的关键在于向poller_output表写入恶意数据。我们需要先确认这张表是否存在以及它的结构。sqlmap -u URL -p graph_end -D cacti --tables从表列表中查找poller_output。然后查看其结构sqlmap -u URL -p graph_end -D cacti -T poller_output --columns关键字段通常包括id,local_data_id,rrd_name,time,output。其中output字段是我们写入恶意负载的目标。第三步通过SQL注入写入恶意数据这是最核心的一步。我们需要构造一条INSERT或UPDATE语句通过SQL注入点执行将恶意代码写入output字段。由于是通过注入执行我们使用sqlmap的--sql-query或--sql-shell功能。首先我们需要知道poller_output表里已有的数据格式以便模仿。可以先用--sql-query查询一下sqlmap -u URL -p graph_end --sql-query SELECT * FROM cacti.poller_output LIMIT 1如果执行成功你会看到一条记录。注意output字段的格式。在导致RCE的利用中output字段的内容会被Cacti的poller进程当作shell命令执行。因此我们可以写入如id这样的系统命令反引号在MySQL中会执行系统命令但取决于数据库配置和权限成功率不高。更通用的方法是写入PHP代码如果Cacti有文件包含漏洞则可导致代码执行。但在这个特定漏洞链中是利用了Cacti对output字段内容处理的缺陷。根据公开的漏洞利用细节一种方法是向poller_output插入一条记录其中output字段包含恶意命令。例如INSERT INTO poller_output (local_data_id, rrd_name, time, output) VALUES (1, 恶意rrd名, NOW(), 恶意命令输出);而恶意命令可以通过MySQL的INTO OUTFILE功能写入一个PHP Webshell但这需要FILE权限且知道Web目录的绝对路径。考虑到权限和路径的不确定性在实战中利用这个漏洞链需要更精巧的构造。我们可以使用sqlmap的--os-shell功能尝试直接获取一个伪shell但其原理也是通过INTO OUTFILE写文件成功率受制于权限。一个更稳妥的、贴合漏洞原理的利用思路是分两步走。利用注入点写入Webshell如果数据库用户有FILE权限并且我们知道Web根目录如/var/www/html/cacti/可以直接尝试写入Webshell。sqlmap -u URL -p graph_end --file-write/本地路径/shell.php --file-dest/远程Web路径/shell.php但这通常很难成功因为权限和路径是两大拦路虎。利用poller_output触发命令执行这是该漏洞链的独特之处。我们可以通过注入向poller_output表的output字段写入一个特殊的字符串当Cacti的poller下次运行时会执行这个字符串。例如在某些配置下如果output字段以特定的PHP代码开头可能会被包含执行。但这需要深入研究Cacti的源代码。由于漏洞细节的复杂性完全自动化的利用链可能无法仅靠sqlmap的默认参数完成。这时我们需要切换到--sql-shell模式进行交互式的、更精细的SQL操作。sqlmap -u URL -p graph_end --sql-shell进入shell后你可以像在MySQL客户端里一样执行SQL语句。例如sql-shell SELECT version; sql-shell SELECT secure_file_priv;secure_file_priv的值决定了INTO OUTFILE能写到哪里。如果值为空则可以写入任意目录如果为NULL则禁止文件写入如果是一个路径则只能写入该目录下。注意事项在实际测试中我遇到的绝大多数MySQL安装secure_file_priv默认都是NULL或者一个受限路径。这意味着通过数据库直接写Webshell的路径常常被堵死。这也是为什么CVE-2023-39361/CVE-2024-31459的利用链更侧重于通过写入poller_output触发后续行为而非直接写文件。sqlmap的--os-shell在这些场景下会直接失败。5. 从SQL注入到远程代码执行的原理剖析理解了利用sqlmap进行注入的操作后我们有必要深入看看为什么一个graph_view.php的SQL注入能导致远程代码执行。这涉及到对Cacti架构的深入理解。Cacti的主要工作流程是通过poller.php通常由crontab定时执行收集数据将原始数据存入poller_output表再由另一个进程或poller.php自身处理poller_output表中的数据更新RRD文件并清空该表。关键漏洞点在于graph_view.php中的graph_start/graph_end参数在构造SQL查询时未经充分过滤就直接拼接到了查询语句中。攻击者利用这一点可以执行任意的SQL语句。那么如何跳到RCE呢核心在于篡改poller_output表的数据。如果攻击者能通过SQL注入向poller_output表的output字段插入精心构造的数据那么当下一次poller进程运行时它会读取并处理这些数据。在Cacti的某些版本或特定配置下poller处理output字段的逻辑可能存在缺陷。例如如果output字段的内容被直接传递给system()、exec()、passthru()等PHP函数或者被用于构造一个最终会被执行的命令字符串那么攻击者插入的系统命令就会被执行。具体到利用链攻击者可能需要完成以下步骤确定注入点与上下文确认graph_view.php的注入点可用且当前数据库用户有对poller_output表的写权限INSERT/UPDATE。构造恶意负载研究Cacti源代码找到poller处理poller_output.output字段的代码路径。构造一个能被该代码路径解析并最终触发命令执行的字符串。这个字符串可能包含特定的前缀、特制的命令拼接等。执行注入写入通过SQL注入执行一条INSERT INTO poller_output ...语句将步骤2构造的恶意负载写入output字段。这步正是我们使用sqlmap的--sql-query或交互式--sql-shell要完成的。触发执行等待Cacti的poller定时任务执行通常每分钟一次或者寻找方法主动触发poller的执行。恶意负载被处理命令得以执行。这个过程充满了不确定性高度依赖于Cacti的版本、配置和代码逻辑。这也是为什么公开的漏洞利用脚本Exploit往往比单纯使用sqlmap更有效因为它们精确编码了针对特定版本所需的恶意负载和触发方式。实操心得在面对这类链式漏洞时sqlmap的角色更像是一把“万能钥匙”的前半截——它能帮你打开SQL注入这扇门写入数据。但门后房间里的机关如何触发RCE需要你根据房间的构造目标系统源码来单独设计。单纯依赖sqlmap的全自动功能往往无法走完全程。结合手动代码审计、理解漏洞公告中的细节描述、以及参考公开的PoC才是成功利用的关键。6. sqlmap高级参数调优与绕过技巧在针对Cacti这种实际应用的测试中你可能会遇到各种WAFWeb应用防火墙或简单的输入过滤。这就需要我们对sqlmap的参数有更深入的了解。1. 延迟与超时设置 (--delay,--timeout)对于网络不稳定或处理速度慢的目标可以设置请求延迟和超时时间避免误判。sqlmap -u URL -p graph_end --delay1 --timeout15--delay1表示每个HTTP请求间隔1秒--timeout15设置连接超时为15秒。2. 随机化用户代理 (--random-agent)有些应用会屏蔽默认的sqlmap User-Agent。使用--random-agent可以让sqlmap从内置的浏览器UA列表中随机选择一个更好地模拟正常浏览器。3. 使用代理池 (--proxy)在需要隐藏自身IP或绕过某些基于IP的简单限制时可以使用代理。sqlmap -u URL -p graph_end --proxyhttp://127.0.0.1:8080如果你使用Burp Suite等代理工具进行调试这个参数非常有用可以在Burp中观察sqlmap发出的所有请求。4. 载荷篡改脚本 (--tamper)这是绕过WAF的利器。Tamper脚本可以对注入载荷进行混淆、编码等操作。例如space2comment脚本将空格替换为/**/between脚本用BETWEEN替换大于号。sqlmap -u URL -p graph_end --tamperspace2comment,between针对Cacti可以尝试常用的tamper脚本组合。但要注意过度混淆可能导致载荷失效。5. 指定注入技术 (--technique)sqlmap支持多种注入检测技术B布尔盲注、E报错注入、U联合查询、S堆叠注入、T时间盲注。如果知道目标注入点类型可以指定以提高效率。sqlmap -u URL -p graph_end --techniqueB对于Cacti这个漏洞初步手工测试是布尔型所以可以优先使用B。6. 风险与等级 (--risk,--level)--risk: 风险等级1-3等级越高使用的Payload越可能对数据库造成破坏如OR 11可能导致大量数据被操作。默认是1。在需要执行INSERT或UPDATE操作时可能需要调高到3。--level: 测试等级1-5等级越高发送的测试Payload越多测试也更全面包括对HTTP头等处的测试。默认是1。对于有防护的目标可以尝试调高到2或3。sqlmap -u URL -p graph_end --risk3 --level37. 处理Cookie与Session如果目标应用需要登录你需要将有效的Cookie提供给sqlmap。sqlmap -u URL -p graph_end --cookiePHPSESSID你的sessionid; Cacti你的cacti cookie你可以先通过浏览器正常登录Cacti然后从开发者工具F12的网络标签中复制Cookie值。结合以上参数一个针对有简单防护的Cacti的完整探测命令可能如下sqlmap -u http://target/cacti/graph_view.php?actionviewlocal_graph_id1graph_start0graph_end100 \ -p graph_end \ --techniqueB \ --level2 \ --risk2 \ --delay0.5 \ --random-agent \ --cookieCactiabc123def456... \ --current-db7. 常见问题排查与实战心得在实际操作中你一定会遇到各种各样的问题。下面我整理了一些常见的情况和解决思路。问题1sqlmap检测不到注入点但手工测试有明显异常。可能原因1参数类型判断错误。sqlmap可能将数字型参数误判为字符型或者反之。可以尝试用--dbms指定数据库类型或用--no-cast关闭载荷类型转换。可能原因2动态页面或Token干扰。如果页面每次请求都有变化如包含动态Tokensqlmap的测试请求可能会因Token失效而失败。可以尝试使用--csrf-token和--csrf-url参数来处理。可能原因3WAF拦截。sqlmap的默认Payload被WAF识别并拦截。尝试使用--tamper脚本混淆或降低请求频率--delay或使用代理。问题2能检测到注入但无法枚举数据--dbs失败。可能原因1数据库用户权限过低。当前注入点对应的数据库用户可能只有普通查询权限没有SELECT系统表的权限如information_schema。可以尝试直接猜解已知的表名和列名使用--common-tables和--common-columns参数。可能原因2盲注深度大响应不明显。时间盲注或布尔盲注的区分度不够。可以尝试增加--time-sec时间盲注的延迟时间或使用--string/--not-string参数手动指定判断页面真假的标志字符串。问题3使用--os-shell或--file-write失败。根本原因数据库权限不足。这是最常见的原因。需要FILE权限和secure_file_priv设置允许。可以通过--sql-query SELECT version, secure_file_priv, user(), file_priv FROM mysql.user WHERE user CURRENT_USER()来检查权限。如果secure_file_priv为NULL或非Web目录--os-shell基本无法成功。此时应转向利用应用本身的逻辑漏洞如本漏洞中的poller_output链。问题4如何判断漏洞利用是否成功对于SQL注入本身sqlmap的成功检测就是证明。对于RCE则需要更明确的回显。如果通过写入poller_output触发命令可以尝试执行一个能观察到效果的命令例如写入一个测试文件echo test123 /tmp/test_rce.txt如果知道路径。发送一个DNS或HTTP请求使用curl http://your-server/或ping -c 1 your-server在你的服务器上查看日志。利用Cacti自身的回显如果命令执行结果能被Cacti记录或显示在某个地方如日志文件、页面则可以尝试读取。实战心得与技巧信息收集是第一步也是最重要的一步在真正开始注入前尽量多地收集目标信息。包括Cacti的准确版本、操作系统、Web服务器、PHP版本、数据库版本和类型。这些信息能帮你选择最合适的Payload和利用方式。善用--batch和--answers在自动化测试或对已知目标进行重复测试时使用--batch可以让sqlmap自动选择默认选项无需人工交互。--answers参数可以预设答案例如--answersextendingN,followN。结合手动测试与工具使用不要完全依赖工具。对于关键步骤如确认注入点类型、测试WAF规则、构造最终的RCE Payload手动测试和思考往往更有效。用Burp Suite拦截sqlmap的请求分析其Payload构造方式能帮你更好地理解原理和调整策略。注意日志与痕迹清理在授权测试中虽然目标是漏洞验证但也应尽量避免对目标系统造成不必要的负载或留下大量测试日志。测试完成后如果可能应清理通过注入插入的测试数据。使用sqlmap的--cleanup功能可以尝试清理它创建的临时表。8. 漏洞修复与安全加固建议在完成漏洞验证和分析后从防御者的角度了解如何修复和防范此类漏洞同样重要。对于Cacti管理员立即升级最根本的解决方法是升级到已修复该漏洞的Cacti版本。请关注Cacti官方安全公告及时应用补丁。最小权限原则运行Cacti的数据库用户应仅被授予其所需的最小权限。绝对不要使用root或具有FILE、GRANT等高级权限的账户。只赋予对cacti数据库的SELECT、INSERT、UPDATE、DELETE权限必要时可细化到具体表。输入验证与参数化查询对于graph_view.php这样的文件应对所有输入参数进行严格的类型检查和过滤。graph_start和graph_end应强制转换为整数。更重要的是将代码中的SQL查询从字符串拼接改为使用预处理语句参数化查询这是防止SQL注入最有效的方法。Web目录权限控制确保Web目录如/var/www/html/cacti/的权限设置严格禁止非Web服务用户写入。这可以防止攻击者通过其他漏洞上传Webshell。网络隔离与访问控制将Cacti监控系统部署在内网并通过防火墙限制访问来源IP。如果必须对外提供访问考虑使用VPN或堡垒机。对于开发者安全编码规范建立并强制执行安全编码规范明确要求所有数据库操作必须使用预处理语句。代码审计与自动化扫描定期对代码进行安全审计并集成SAST静态应用安全测试工具到开发流程中在代码提交阶段就发现潜在的SQL注入等问题。依赖库管理及时更新项目所使用的第三方库和框架它们可能包含已知的安全漏洞。通用安全实践部署WAF虽然不能根除漏洞但Web应用防火墙可以在漏洞被公开但尚未修复的“空窗期”提供有效的缓解拦截常见的攻击Payload。完善的日志与监控启用并定期审查Web服务器、数据库和应用程序的日志。异常的SQL查询、大量的错误请求、来自单一源的可疑访问模式都可能是攻击迹象。通过这次对Cacti漏洞的完整复现我深刻体会到一个成熟的漏洞利用往往是多个薄弱环节串联的结果。作为攻击方需要耐心地抽丝剥茧作为防御方则需要层层设防打破攻击链中的任何一环。工具如sqlmap它极大地提升了我们验证漏洞的效率但工具背后的原理、局限以及如何根据实际情况调整策略才是安全测试中更具价值的部分。