我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 97.1% 的成绩。 了解更多

深度解析

深度解析

已有公开 PoC 的 FortiSIEM gRPC 路径遍历 RCE(CVE-2026-106173)

FortiSIEM 7.5.0 在 TCP 27918 上的未经身份验证 gRPC 服务允许攻击者覆盖文件以实现 RCE;7.5.1 已修复,已有公开 PoC,目前没有已知利用。

要点速览

  • CVE-2026-106173 是 FortiSIEM gRPC 服务中的路径遍历与空字节注入漏洞,可让未经身份验证的攻击者向设备上的任意路径写入任意内容。
  • 覆盖由 cron 每 60 秒运行一次的健康检查脚本,即可将文件写入转化为远程代码执行。
  • 严重程度:严重,Fortinet 评分为 CVSS v3.1 9.4。
  • 只有 FortiSIEM 7.5.0 受影响。FortiSIEM 7.6 和 FortiSIEM Cloud 不受影响,未列出其他版本。
  • Fortinet 内部发现该漏洞(发现者为 Fortinet 产品安全团队的 Gwendal Guégniaud),于 2026 年 10 月 13 日发布公告 FG-IR-26-180,没有已知利用。
  • Horizon3.ai 于 2026 年 10 月 20 日发布技术深度分析及可运行的概念验证。
  • CISA 记录标注已有公开概念验证,但没有已知利用。
  • 立即将 FortiSIEM 7.5.0 升级至 7.5.1 或更高版本,在此之前限制 TCP 端口 27918 的访问。此举通过切断对漏洞服务的未经身份验证访问来阻止利用,但不会移除漏洞。

我是否受到影响?

以下两个条件同时成立时,您就受到影响:

  • 您运行 FortiSIEM 7.5.0。Fortinet 公告未列出其他受影响版本:7.6 不受影响,FortiSIEM Cloud 也不受影响。
  • 不可信网络位置可以访问相关 gRPC 服务。phQueryMaster 是在设备所有接口的 TCP 27918 上监听的 gRPC 服务,其 TLS 配置不请求客户端证书,因此任何可以访问该端口的网络位置都可成为攻击位置。
软件 受影响版本 修复版本
FortiSIEM 7.5.x(自行管理) 7.5.0 7.5.1 或更高版本
FortiSIEM 7.6.x 不受影响 不适用
FortiSIEM Cloud 不受影响 不适用

从攻击者可能使用的网络检查真正重要的暴露面,即该端口:

nmap -p 27918 --open <fortisiem-host>       # phQueryMaster's gRPC service; a "27918/tcp open" line means reachable, no output means filtered or closed, which is the safe state

FortiSIEM 7.5.0 是唯一受影响版本,因此版本检查只需回答一个问题:在 FortiSIEM 管理界面确认构建版本;如果是 7.5.0,先升级,再处理其他事项。网络分段不能替代补丁。即使内部 SIEM 具有良好分段,漏洞服务依然运行,同一网段上一台被攻陷的主机就能访问它。无论当前是否可达,都应升级,因为修复消除的是漏洞本身。

如何修复?

按照 Fortinet 的 FortiSIEM 升级指南,将 FortiSIEM 7.5.0 升级至 7.5.1 或更高版本,并重启设备服务。

完成升级前,限制 TCP 端口 27918 的访问。Fortinet 公告将此列为临时措施(“限制 tcp 端口 27918 的访问可防止该问题”):它通过切断服务的未经身份验证访问阻止利用,但漏洞仍然存在。

服务绑定设备的所有接口,因此应在外围防火墙或网络分段中实施限制,而不是在设备上操作。这是网络阻断,不是补丁:任何仍能到达端口 27918 的路径,包括同网段上已被攻陷的主机,都意味着漏洞仍可触及。

升级后,检查 Horizon3 标为现实覆盖目标的两个文件 /opt/charting/redishb.sh 和 /opt/phoenix/bin/phLicenseTool,因为升级前发生的覆盖在升级后仍会保留。

根本原因是什么?

gRPC SendQueryResult 处理器将攻击者可控的请求头拼接进文件路径,并按客户端声明的长度复制请求头值,因此请求头内的空字节可将路径截断为攻击者选定的任意精确文件名。

phQueryMaster 服务在 TCP 27918 上启动 gRPC 服务器;Horizon3 引用了 libphGrpcServices.so 中的启动代码:

// grpcNotificationServer::run()
unsigned short port = mServerPort;                          // command_port = 7918
std::string addr = "[::]:" + phSecureUtils::getGrpcPort(port);  // [::]:27918
grpc::SslServerCredentialsOptions sslOps;
// sslOps.client_certificate_request stays 0 == GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE
auto creds = grpc::SslServerCredentials(sslOps);
builder.AddListeningPort(addr, creds, 0);

phSecureUtils::getGrpcPort() 将设备的基础命令端口(7918)映射为 gRPC 服务的监听端口 27918,因此引用代码从 7918 开始,却绑定 [::]:27918。该地址绑定所有接口,客户端证书选项保持为零意味着任何客户端都可在无证书、无身份验证的情况下连接。以下为引用自 grpcNotificationService.cpp 的漏洞 RPC 及其处理器:

replaceString(srcIp, ".", "_");
std::string fileDir = "/query/result/" + queryId;   // concatenation, no validation
if (!phMiscUtils::DirExists(fileDir.c_str()))
    phMiscUtils::mkdir(fileDir.c_str());

std::stringstream path;
path << fileDir << "/result-" << srcIp << "-"
     << periodStart << "-" << periodEnd << "-"
     << mGetQueryResultUniqId() << ".dat";
std::ofstream output;
output.open(path.str().c_str(), ios::binary|ios::out|ios::trunc);
while (reader->Read(&req))
    output.write(req.file_content().data(), req.file_content().size());

路径遍历:gRPC 消息的 query-id 请求头未经验证便拼接到目录路径中,因此 ../../../../../tmp/x 可跳出查询目录,让攻击者任意控制目录。单独利用时,文件名仍包含请求的部分内容,因此写入发生在所选目录下,文件名则是服务器附加的 result-...dat。

空字节截断:服务器按客户端声明的精确长度复制请求头值,因此 ../../../../../opt/charting/redishb.sh\0ignored 这样的 query-id 会将内嵌空字节带入路径。操作系统将空字节视为字符串结尾,丢弃服务器自身的 result-...dat 后缀,最终写入空字节前的精确路径。

如何利用?

利用只需一次未经身份验证的 SendQueryResult gRPC 调用:带 ../../ 遍历和空字节的 query-id 让流式传输的 file_content 覆盖 /opt/charting/redishb.sh,cron 随后在 60 秒内运行它。

Horizon3 发布的概念验证手工组装该请求。它无需身份验证或客户端证书便连接 TCP 27918,使用元数据 query-id: ../../../../../opt/charting/redishb.sh\0ignored、src-ip: 127.0.0.1、period-start: 0 和 period-end: 0 调用 SendQueryResult,并将载荷作为 file_content 流式发送。

其余工作由服务器完成。处理器根据遍历路径在 /query/result 外构建目录。空字节截断构建的路径,因此流中的字节原地覆盖 /opt/charting/redishb.sh,保留文件原有的所有者和权限。

redishb.sh 是 Redis 服务的健康检查脚本,由 cron 作业每 60 秒执行,因此覆盖后一分钟内,设备就会运行攻击者的代码。

覆盖现有文件只需一次请求;以精确名称创建全新文件需要两次,因为第一次请求必须建立目标目录,第二次才通过空字节截断写入精确文件名。之所以手工组装请求,是因为 curl 和标准 gRPC 库限制请求头值中的空字节,因此漏洞可被利用,但并非简单调用脚本即可实现。

攻击如何展开?

一次未经身份验证的 gRPC 调用会覆盖 cron 每分钟运行的脚本,因此从文件写入到代码执行的延迟最多为 60 秒。

flowchart LR A["未经身份验证的 gRPC 客户端
访问 tcp/27918"] --> B["SendQueryResult 使用 query-id
../../../../../opt/charting/redishb.sh
加空字节"] B --> C["通过拼接构建目录,
没有验证"] C --> D["空字节将路径截断
为精确文件名"] D --> E["流式字节覆盖
/opt/charting/redishb.sh"] E --> F["Cron 在 60 秒内
运行脚本"]

攻击者能做什么?

能够通过网络访问 FortiSIEM 7.5.0 设备 TCP 27918 的攻击者可以:

  • 向设备的任意路径写入任意内容,覆盖现有文件时只需一次请求。
  • 以被覆盖脚本执行上下文的权限,在 60 秒内执行这些内容;目标可为设备定期运行的任何脚本或二进制文件。Horizon3 通过 Redis 健康检查脚本 /opt/charting/redishb.sh 演示了这一点。
  • 使用同一原语破坏配置、篡改 SIEM 自身的二进制文件(Horizon3 列出第二个值得关注的文件 /opt/phoenix/bin/phLicenseTool),或植入持久化机制。

不需要身份验证、用户交互或权限,只需网络可达该端口。实际限制在于可达性:Fortinet 的临时措施采用网络限制,正是因为任何仍能访问 27918 的路径,包括同网段上被攻陷的主机,都会让漏洞保持可利用状态。

修复必须封堵什么?

Fortinet 在 FortiSIEM 7.5.1 中修复了漏洞;公告未公布补丁差异,因此关于修复的权威结论就是版本指导本身。

根据 Horizon3 对漏洞两个部分的分析,修复必须做到:阻止未经验证的 query-id 进入目录路径,使遍历序列无法再跳出 /query/result;并阻止复制的请求头值向构建的路径注入空字节。临时措施没有处理这两个问题,而是移除通往漏洞处理器的网络路径。

如何检查是否已遭入侵?

Horizon3 明确指出,没有应用日志会显式暴露此次利用,因此需要文件取证。根据深度分析中的指标:

  • 运行 stat /opt/charting/redishb.sh,将其修改时间(mtime)和状态变更时间(ctime)与周围文件比较;覆盖后时间戳与周围文件不同,是需要调查的信号。
  • 对 Horizon3 标为第二个目标的 /opt/phoenix/bin/phLicenseTool 进行同样检查。
  • 检查设备上的 cron 和进程活动,查找意外子进程。在证明无关前,将定时脚本派生的任何 shell 或解释器,以及设备自行发起的任何出站连接,视为可能相关。

Fortinet 公告记录为没有已知利用,CISA 记录也标注已有公开概念验证但没有已知利用,因此截至撰文时,没有可用于追踪攻击活动的指标。目前可进行的检查就是上述时间戳比较和进程审查。

时间线

  • 2026-10-13:Fortinet 发布公告 FG-IR-26-180 和 CVE-2026-106173 记录,评级为严重,CVSS v3.1 9.4,无需身份验证,没有已知利用。
  • 2026-10-20:Horizon3.ai 发布深度分析和可运行的概念验证,追踪了从 gRPC 端口到 60 秒 cron 执行的完整链条。
  • 2026-10-26:本文发布。Fortinet 和 CISA 均未记录在野利用。

常见问题

端口 27918 默认暴露吗?

FortiSIEM 自身配置并不决定对外暴露,但其默认绑定确实监听所有接口:phQueryMaster 服务在每个接口上监听。实际可达性取决于外围网络;如果设备只能从管理网络访问,该端口就不在攻击者的可达路径上。使用 nmap -p 27918 --open <fortisiem-host> 从相关网络确认:出现 27918/tcp open 行表示可达,没有端口行表示按预期被过滤或关闭。

TLS 配置能阻止攻击者吗?

不能。服务提供 TLS 凭据,但将客户端证书选项保留为“不请求”,因此任何客户端都可在无证书的情况下完成连接并调用 RPC。空字节位于原始 HTTP/2 请求头值中,低于 TLS 栈检查的层级,因此标准 TLS 栈不会拒绝它。

为什么空字节能完全控制文件名?

因为服务器按照客户端声明的精确长度复制请求头值,请求头中的空字节会保留在路径字符串内,而操作系统在该空字节处停止读取路径。这会丢弃服务器自身原本用于确定文件名的 result-<src>-<start>-<end>-<id>.dat 后缀。结果是精确路径写入;唯一额外步骤就是手工组装消息。

FortiSIEM 7.6 或 FortiSIEM Cloud 受影响吗?

不受影响。Fortinet 公告将 FortiSIEM 7.6 和 FortiSIEM Cloud 列为不受影响,只列出 7.5.0 为受影响版本。7.6 部署无需针对该 CVE 采取措施,7.5.0 部署需要升级至 7.5.1。

攻击者能将写入用于代码执行以外的用途吗?

可以。任意文件写入是一种通用原语:破坏配置文件、替换启动时运行的二进制文件或植入凭据都可行,Horizon3 的分析还列出了设备上多个由 cron 驱动、会读取文件的服务。通过 60 秒健康检查脚本执行代码,只是已经演示的最短路径。

已有人遭到攻击吗?

Fortinet 公告表示没有已知利用,CISA 的 CVE 记录标注存在公开概念验证,但没有已知利用。Horizon3 的概念验证已经公开,因此从公开概念验证到在野利用的间隔可能很短,这也是不应推迟升级的原因。

参考资料

  • Fortinet PSIRT 公告 FG-IR-26-180,2026 年 10 月 13 日发布,包含受影响版本表、TCP 27918 临时措施和内部发现者署名:https://www.fortiguard.com/psirt/FG-IR-26-180 (2026 年 10 月 26 日核查)
  • CVE-2026-106173 记录,Fortinet 评级为 CVSS v3.1 9.4 严重,无需身份验证,CISA ADP 标注已有公开概念验证:https://cveawg.mitre.org/api/cve/CVE-2026-106173 (2026 年 10 月 26 日核查)
  • Zach Hanley 撰写的 Horizon3.ai 深度分析,2026 年 10 月 20 日,引用 gRPC 启动和处理器代码,解释空字节机制、redishb.sh cron 目标及基于时间戳的指标:https://horizon3.ai/attack-research/cve-2026-106173-deep-dive-fortisiem-grpc-path-traversal-file-write-rce/ (2026 年 10 月 26 日核查)
  • Horizon3 的概念验证:https://github.com/horizon3ai/CVE-2026-106173 (2026 年 10 月 26 日核查)
  • Horizon3 先前关于 FortiSIEM phMonitor 服务的研究,涉及 CVE-2023-34992、CVE-2024-23108 和 CVE-2025-64155:https://horizon3.ai/attack-research/disclosures/cve-2025-64155-three-years-of-remotely-rooting-the-fortinet-fortisiem/ (2026 年 10 月 26 日核查)
  • The Hacker News 关于公开 PoC 的报道,2026 年 10 月:https://thehackernews.com/2026/10/critical-fortisiem-flaw-gets-public.html (2026 年 10 月 26 日核查)