Someone left the security camera's maintenance hatch unlocked, and now the cameras themselves are the entry point
CVE-2026-106173 is a path traversal flaw in Fortinet FortiSIEM's phQueryMaster gRPC service (TCP/27918). The SendQueryResult RPC method blindly concatenates attacker-supplied query-id metadata into a filesystem path without sanitization. By injecting ../ sequences combined with a NUL-byte (\x00) to truncate the server-appended suffix, an unauthenticated attacker achieves exact-filename-control arbitrary file write. The canonical exploitation target is /opt/charting/redishb.sh, a Redis health-check script executed by cron every 60 seconds — overwriting it delivers reliable RCE as the FortiSIEM admin user within one minute. Affected versions span the entire FortiSIEM 7.x line: 7.0.0–7.0.4, 7.1.0–7.1.9, 7.2.0–7.2.7, 7.3.0–7.3.5, 7.4.0–7.4.2, and 7.5.0. Only 7.5.1+ and 7.6 are safe.
Fortinet's own CVSS of 9.4 and NVD's 9.8 both land at CRITICAL, and both are fair. This is a textbook pre-auth RCE: network-reachable, zero-click, low complexity once you have the PoC (which is public from Horizon3.ai and 0xBlackash). The NUL-byte trick requires hand-crafted gRPC frames rather than off-the-shelf grpcurl, which might tempt some analysts to argue 'high complexity' — but both published PoCs handle this programmatically, making it point-and-shoot. The only real-world friction is that TCP/27918 is a non-standard port unlikely to be internet-facing. That friction is immaterial for the threat model that matters: any attacker with internal network access (post-phish, post-VPN-compromise, lateral movement) can reach this port trivially, and the target *is the SIEM itself* — the single pane of glass your SOC depends on. Compromising it blinds detection, harvests fleet-wide credentials and log data, and provides a pivot point into every network segment the SIEM collects from.
5 steps from start to impact.
Discover gRPC endpoint
phQueryMaster gRPC service binds to on all interfaces ([::]:27918). The service uses TLS but skips client certificate validation, meaning any TCP client can negotiate a session. Standard internal network scans or Fortinet documentation reveal this port.- Network path to FortiSIEM appliance on TCP/27918
- Port 27918 is non-standard and typically not forwarded through perimeter firewalls
- Most deployments place FortiSIEM on a management VLAN — lateral movement or VPN access required from external positions
Craft malicious gRPC SendQueryResult request
SendQueryResult stream. The query-id metadata field is set to a path-traversal payload targeting /opt/charting/redishb.sh (e.g., ../../../../../opt/charting/redishb.sh\x00ignored). The NUL byte truncates the server-appended filename suffix (result-<src>-0-0-<id>.dat), granting exact filename control. Standard gRPC libraries reject NUL bytes in headers, so the PoC manually assembles raw HTTP/2 HPACK-encoded frames.- Public PoC tooling (Python-based, available on GitHub)
- Basic understanding of gRPC framing (handled by PoC)
- Off-the-shelf gRPC clients like
grpcurlcannot inject NUL bytes — custom tooling required - Both public PoCs solve this completely, reducing friction to 'download and run'
/opt/charting/redishb.sh is the primary detective controlOverwrite cron-executed script
SendQueryResult handler writes the attacker's file_content bytes to the traversed path, overwriting /opt/charting/redishb.sh in-place. The overwrite preserves original file permissions and ownership, so no additional privilege manipulation is needed. The attacker plants a reverse shell, beacon, or credential-harvesting payload.- Successful gRPC connection from Step 1
- Crafted payload from Step 2
- None — the write is unconditional and unauthenticated
/opt/charting/ would catch the modification; however, relying on the SIEM itself to detect its own compromise is a circular dependencyCron triggers RCE within 60 seconds
/opt/charting/redishb.sh every 60 seconds for Redis health monitoring. The attacker's payload runs as the FortiSIEM admin user with full access to the application, its databases, stored credentials, and log data. An alternative file-write target — /opt/phoenix/bin/phLicenseTool — offers a secondary execution vector via the license-check pathway.- Cron daemon running (default on all FortiSIEM appliances)
- None — cron is integral to FortiSIEM operation and cannot be disabled without breaking the product
redishb.sh; network-level detection of outbound C2 from the SIEM appliancePost-exploitation: blind the SOC and pivot
- Successful RCE from Step 4
- None beyond standard post-exploitation tradecraft
redishb.sh and phLicenseTool. This provides detective control for exploitation attempts and validates that the firewall block is holding. Ship alerts to a secondary SIEM or SOC platform to avoid circular dependency. Deploy within 3 days.query-id metadata in the SendQueryResult handler, preventing path traversal. Organizations on 7.0–7.4 branches have no backported fix and must upgrade to 7.5.1+. Schedule and execute the upgrade within the 90-day noisgate remediation SLA for CRITICAL findings. Test in staging first — FortiSIEM major-version upgrades can require database migration.- Web Application Firewall (WAF): TCP/27918 is a gRPC/HTTP2 service on a non-standard port. No WAF inspects this traffic, and gRPC payloads are binary protobuf — WAF path-traversal signatures do not apply.
- FortiSIEM's own correlation rules: Relying on the compromised SIEM to detect its own exploitation is a circular dependency. The attacker's first post-exploit action will be to suppress or delete alerts.
- IPS/IDS signatures for path traversal: The NUL-byte is embedded in HPACK-encoded HTTP/2 headers, not in plaintext HTTP. Standard IPS path-traversal signatures (looking for
../in HTTP URIs) will not match the gRPC binary framing. - TLS inspection / certificate pinning: The gRPC service already uses TLS but skips client certificate validation — this is the vulnerability's enabler, not a defense. Adding mutual TLS would help but requires FortiSIEM configuration changes that Fortinet has not documented as a workaround.
The supporting signals.
| In-the-Wild Exploitation | No confirmed active exploitation as of 2026-10-21. Not listed on CISA KEV. Fortinet PSIRT stated 'no known exploitation' at advisory publication (Oct 13). However, public PoC availability since Oct 18 significantly increases near-term risk of opportunistic exploitation. |
|---|---|
| Proof-of-Concept | Two public, weaponized PoCs available. horizon3ai/CVE-2026-106173 — full exploit chain including NUL-byte gRPC frame assembly, by the same team behind CVE-2023-34992 and CVE-2024-23108 FortiSIEM research. 0xBlackash/CVE-2026-106173 — independent reimplementation. Both indexed on Sploitus. These are point-and-shoot RCE tools, not theoretical write-ups. |
| EPSS | 0.00433 (0.43%), 36th percentile. Low probability score reflects the 8-day-old CVE and lack of mass scanning. Expect this to climb sharply now that PoCs are public. EPSS historically lags exploit publication by 2–4 weeks for Fortinet CVEs. |
| KEV Status | Not listed as of 2026-10-21. Given the public PoC and Fortinet's history of rapid KEV additions (CVE-2023-34992 was added within 3 weeks of PoC release), KEV listing is likely imminent if any in-the-wild activity is confirmed. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (NVD: 9.8). Fortinet's own score is 9.4 (FG-IR-26-180). The delta likely reflects Fortinet rating AC as slightly higher due to the NUL-byte requirement — a distinction the public PoCs have rendered moot. |
| Affected Versions | FortiSIEM 7.0.0–7.0.4, 7.1.0–7.1.9, 7.2.0–7.2.7, 7.3.0–7.3.5, 7.4.0–7.4.2, 7.5.0. Essentially the entire 7.x line. FortiSIEM 7.6 and FortiSIEM Cloud are not affected. |
| Fixed Versions | FortiSIEM 7.5.1 or later. No backported fixes for 7.0–7.4 branches — organizations on older branches must upgrade to 7.5.1+ or apply the TCP/27918 block workaround. |
| Internet Exposure | TCP/27918 is a non-standard gRPC port. FortiSIEM appliances are rarely internet-facing; Shodan/Censys show minimal direct exposure of this port. However, the service binds to all interfaces ([::]), making it reachable from any internal network segment with routing to the SIEM management VLAN. FortiSIEM's architecture requires broad inbound connectivity for log collection, widening the internal attack surface. |
| Disclosure Timeline | Discovered internally by Gwendal Guégniaud (Fortinet Product Security). Advisory FG-IR-26-180 published 2026-10-13. Horizon3.ai deep-dive and PoC published ~Oct 18. This is Horizon3's fourth FortiSIEM disclosure, following CVE-2023-34992, CVE-2024-23108, and CVE-2025-64155 — demonstrating persistent attack surface in this product. |
| FortiSIEM Install Base | ~4.7% of the enterprise SIEM market, approximately 1,671 organizations globally per 6sense. Significant presence in mid-market (100–5,000 employees) and managed security service providers. ~47% of deployments are US-based. |
Sources.
- Horizon3.ai Deep Dive — CVE-2026-106173 gRPC Path Traversal RCE
- Fortinet PSIRT Advisory FG-IR-26-180
- Horizon3.ai PoC — GitHub
- Strix.ai CVE Intelligence — CVE-2026-106173
- SecurityOnline — Fortinet Patches Critical FortiSandbox and FortiSIEM Flaws
- VulDB — CVE-2026-106173
- 6sense — FortiSIEM Market Share
- Sploitus — 0xBlackash PoC
Why this verdict
- Pre-auth, zero-click, network-reachable RCE with public weaponized exploit code. The chain from TCP connect to shell is 4 steps, fully automated by two independent PoCs, and completes within 60 seconds. No authentication, no user interaction, no race condition.
- Minimal exploit complexity in practice. While the NUL-byte gRPC framing would be AC:H in a vacuum, both Horizon3.ai and 0xBlackash PoCs handle this programmatically. The defender cannot rely on 'attacker skill' as a friction point when point-and-shoot tooling is on GitHub.
- TCP/27918 binds all interfaces — internal exposure is near-universal. The gRPC service listens on
[::], not localhost. FortiSIEM's architecture requires inbound connectivity from log collectors across the network, so the management VLAN is routable from most internal segments. The 'not internet-facing' argument only gates external attackers; any post-phish or post-VPN-compromise position reaches this port. - Role multiplier: FortiSIEM IS the SIEM/detection plane — a canonical high-value-role component. 100% of FortiSIEM installations are, by definition, the organization's security monitoring infrastructure. Compromise delivers: (1) SOC blindness — the attacker can suppress alerts and modify correlation rules, (2) fleet-wide credential harvest — stored collector credentials, API keys, syslog configs for every monitored device, (3) log data exfiltration — complete visibility into the target's infrastructure topology, incident response playbooks, and security posture, (4) lateral pivot — the SIEM has network routes to every monitored segment. Blast radius is tenant → domain → fleet. Under the high-value-role floor rule, since the affected component is canonically SIEM (≥10% threshold trivially met — it's 100%), the verdict floor is CRITICAL.
- Historical pattern of FortiSIEM exploitation. This is the fourth major FortiSIEM RCE in three years (CVE-2023-34992, CVE-2024-23108, CVE-2025-64155). Threat actors know this product and have demonstrated willingness to target it. Expect rapid weaponization in ransomware and espionage campaigns.
Why not higher?
The CVSS 9.8 / CRITICAL is already the maximum severity bucket. The only theoretical increase would be Scope:Changed (S:C), but the exploitation stays within the FortiSIEM appliance's own security boundary — downstream impact is a consequence of the SIEM's privileged position, not a formal scope change in CVSS terms.
Why not lower?
Downgrading to HIGH would require demonstrating that the gRPC port is unreachable in the majority of deployments. It is not — the service binds all interfaces and FortiSIEM's log-collection architecture guarantees broad internal reachability. The 'requires internal network access' argument does not break the CRITICAL floor because the component itself IS the detection plane: compromising it delivers fleet-scale blindness and pivot capability regardless of how the attacker reached port 27918. Public weaponized PoC removes any remaining exploit-complexity friction.
Crowdsourced verification payload.
Run this script on the FortiSIEM appliance as root or the admin user. Usage: bash check_cve_2026_106173.sh. No arguments required. The script reads the FortiSIEM version file, checks it against known-vulnerable ranges, and verifies whether TCP/27918 is actively listening. Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN.
#!/usr/bin/env bash
# check_cve_2026_106173.sh — CVE-2026-106173 FortiSIEM gRPC Path Traversal RCE
# Run on the FortiSIEM appliance as root/admin.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
VULN="UNKNOWN"
# Locate version file
VER_FILE="/opt/phoenix/deployment/VERSION"
if [ ! -f "$VER_FILE" ]; then
echo "STATUS: UNKNOWN"
echo "REASON: $VER_FILE not found. This host may not be a FortiSIEM appliance."
exit 2
fi
VERSION=$(head -1 "$VER_FILE" | tr -d '[:space:]')
echo "FortiSIEM version: $VERSION"
# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Affected: 7.0.0-7.0.4, 7.1.0-7.1.9, 7.2.0-7.2.7, 7.3.0-7.3.5, 7.4.0-7.4.2, 7.5.0
# Fixed: 7.5.1+, 7.6+
if [ "$MAJOR" -eq 7 ] 2>/dev/null; then
case "$MINOR" in
0) [ "$PATCH" -le 4 ] && VULN="VULNERABLE" ;;
1) [ "$PATCH" -le 9 ] && VULN="VULNERABLE" ;;
2) [ "$PATCH" -le 7 ] && VULN="VULNERABLE" ;;
3) [ "$PATCH" -le 5 ] && VULN="VULNERABLE" ;;
4) [ "$PATCH" -le 2 ] && VULN="VULNERABLE" ;;
5) if [ "$PATCH" -eq 0 ]; then VULN="VULNERABLE"; else VULN="PATCHED"; fi ;;
*) VULN="PATCHED" ;;
esac
elif [ "$MAJOR" -gt 7 ] 2>/dev/null; then
VULN="PATCHED"
else
VULN="UNKNOWN"
fi
# Check if gRPC port 27918 is listening
PORT_UP="unknown"
if command -v ss &>/dev/null; then
ss -tlnp 2>/dev/null | grep -q ':27918' && PORT_UP="yes" || PORT_UP="no"
elif command -v netstat &>/dev/null; then
netstat -tlnp 2>/dev/null | grep -q ':27918' && PORT_UP="yes" || PORT_UP="no"
fi
echo "TCP/27918 listening: $PORT_UP"
if [ "$VULN" = "VULNERABLE" ]; then
if [ "$PORT_UP" = "no" ]; then
echo "STATUS: VULNERABLE (port blocked or service stopped — mitigated)"
else
echo "STATUS: VULNERABLE"
fi
echo "REASON: FortiSIEM $VERSION is in the affected range (7.0.0–7.5.0)."
echo "ACTION: Upgrade to 7.5.1+ and block TCP/27918 from untrusted sources."
exit 1
elif [ "$VULN" = "PATCHED" ]; then
echo "STATUS: PATCHED"
echo "REASON: FortiSIEM $VERSION is not in the affected range."
exit 0
else
echo "STATUS: UNKNOWN"
echo "REASON: Could not determine vulnerability status for version $VERSION."
exit 2
fi