一个从未被审视过的gRPC服务,一个空字节,一次未认证的远程代码执行。Fortinet在2026年10月13日发布了针对FortiSIEM的安全公告,编号CVE-2026-106173,漏洞类型是gRPC路径穿越导致任意文件写入,最终可走到远程代码执行。
![]()
Horizon3团队对FortiSIEM并不陌生。此前他们披露过多个严重漏洞,包括CVE-2023-34992的命令注入、CVE-2024-23108的二阶命令注入,以及CVE-2025-64155——用他们自己的话说,是"远程root FortiSIEM的第三年"。这些研究都围绕同一个组件:监听tcp/7900端口的phMonitor服务。
这次公告指向了一个新服务:监听tcp/27918端口的gRPC服务。出于对该产品的熟悉,也出于对新服务的好奇,团队决定深入看看。
未认证就能连上的gRPC服务
在FortiSIEM的监听服务中,phQueryMaster绑定在tcp/27918上。该服务在grpcNotificationServer::run()中启动gRPC服务器,监听端口来自command_port配置值(7918),经phSecureUtils::getGrpcPort()映射到tcp/27918,地址被构造成[::]:27918,因此服务绑定所有网络接口。
gRPC服务器有时会要求客户端证书做认证,但这里的服务把该选项设为了false。这意味着任何远程客户端都能在不认证、不出示客户端证书的情况下连上tcp/27918,触达存在漏洞的gRPC服务。
从路径穿越到任意文件写入
团队检查可用的RPC方法,找到了SendQueryResult。它的处理逻辑会从收到的消息中解析四个值:query-id、src-ip、period-start、period-end,然后用经过简单格式化的src-ip和query-id拼接出一个文件路径。
问题出在fileDir由字符串拼接构造,query-id是攻击者可控的值,且没有任何输入校验。一个形如../../../../../tmp/x的query-id,就能让攻击者穿越到查询目录之外,从而控制目录。
文件名本身也部分可控,格式为result- -0-0- .dat,随后流式传输的字节就是文件内容。因此,一个未认证攻击者可以把任意数据写入任意目录,但文件名受限。
空字节补上最后一块拼图
路径穿越让攻击者能选目录,却选不了确切文件名——服务器总会在末尾加上自己的result-…-.dat后缀,其中的计数器由服务器控制,请求无法设置。单看这一点,这只是一个用处有限的文件写入。
但服务器复制头部值的方式改变了局面:它按客户端声明的精确长度复制每个值,这让攻击者可以在query-id中间嵌入空字节(\0)。当服务器把路径交给操作系统打开文件时,操作系统把空字节视为字符串结尾,静默丢弃其后的所有内容,包括服务器自己的result-…-.dat后缀。写入就落在空字节之前的那个确切路径上。
指向已存在的文件,就是原地覆盖,保留原所有者和权限;另一种变体用两次请求创建一个确切命名的新文件。由于curl和标准gRPC库会限制在头部放入空字节,攻击者必须手工组装gRPC消息。
覆盖一个每60秒执行的脚本
拿到任意文件写入后,下一步是找能带来代码执行的文件。FortiSIEM上有大量服务通过cron任务读取文件。其中一个用于redis健康检查的脚本/opt/charting/redishb.sh,每60秒被cron任务调用一次,且在当前admin用户上下文下可写。
把链条拼起来:穿越目录路径、控制文件名、覆盖一个周期性执行的脚本,代码执行就成立了。团队的概念验证代码已放在GitHub上。
没有日志,只能看时间戳
团队没有找到能明确显示该漏洞被利用的应用日志。但如果漏洞被用来覆盖已有脚本,应仔细检查文件的写入或修改时间戳,找出与周围文件时间戳明显不匹配的项。特别值得关注的文件有两个:
- /opt/charting/redishb.sh
- /opt/phoenix/bin/phLicenseTool
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.