← Back to Feed CACHED · 2026-10-20 17:47:59 · CACHE_KEY CVE-2026-106173
CVE-2026-106173 · CWE-22 · Disclosed 2026-10-13

Fortinet FortiSIEM gRPC phQueryMaster unauthenticated path traversal to RCE via NUL-byte injection

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

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.

"Unauth RCE on your SIEM via gRPC file-write — public PoC, 60-second cron execution. Patch now."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Discover gRPC endpoint

The attacker scans for TCP/27918, which the 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.
Conditions required:
  • Network path to FortiSIEM appliance on TCP/27918
Where this breaks in practice:
  • 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
Detection/coverage: Network IDS rules for unexpected inbound connections to port 27918; FortiSIEM's own connection logs may record the source IP but do not flag the exploit payload
STEP 02

Craft malicious gRPC SendQueryResult request

The attacker uses Horizon3.ai's or 0xBlackash's public PoC to construct a raw gRPC 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.
Conditions required:
  • Public PoC tooling (Python-based, available on GitHub)
  • Basic understanding of gRPC framing (handled by PoC)
Where this breaks in practice:
  • Off-the-shelf gRPC clients like grpcurl cannot inject NUL bytes — custom tooling required
  • Both public PoCs solve this completely, reducing friction to 'download and run'
Detection/coverage: Deep packet inspection for NUL bytes in HTTP/2 HPACK headers on port 27918 — no standard IDS signature exists yet; file integrity monitoring on /opt/charting/redishb.sh is the primary detective control
STEP 03

Overwrite cron-executed script

The 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.
Conditions required:
  • Successful gRPC connection from Step 1
  • Crafted payload from Step 2
Where this breaks in practice:
  • None — the write is unconditional and unauthenticated
Detection/coverage: File integrity monitoring (AIDE, OSSEC, Tripwire) on /opt/charting/ would catch the modification; however, relying on the SIEM itself to detect its own compromise is a circular dependency
STEP 04

Cron triggers RCE within 60 seconds

A cron job executes /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.
Conditions required:
  • Cron daemon running (default on all FortiSIEM appliances)
Where this breaks in practice:
  • None — cron is integral to FortiSIEM operation and cannot be disabled without breaking the product
Detection/coverage: Process execution monitoring for anomalous child processes of redishb.sh; network-level detection of outbound C2 from the SIEM appliance
STEP 05

Post-exploitation: blind the SOC and pivot

With RCE on the SIEM, the attacker can suppress alerts, modify correlation rules, exfiltrate stored log data (containing credentials, tokens, and infrastructure topology), and leverage the SIEM's broad network connectivity — which by design reaches every monitored segment — to pivot laterally. Stored collector credentials, API keys, and syslog forwarding configurations provide ready-made pivot material.
Conditions required:
  • Successful RCE from Step 4
Where this breaks in practice:
  • None beyond standard post-exploitation tradecraft
Detection/coverage: Out-of-band monitoring from a separate SIEM instance, NDR appliance, or cloud-based detection that does not depend on the compromised FortiSIEM
03 · Compensating Control

1
CRITICAL 9.8→LOW 2.5
SEVERITY REDUCED
Block TCP/27918 at network firewall immediately — Fortinet's recommended workaround. Create a firewall rule on the host firewall (iptables/nftables) and on upstream network firewalls to deny all inbound connections to TCP/27918 except from authorized FortiSIEM worker nodes (if clustering is used). This is a zero-impact change for standalone deployments. For clustered deployments, restrict to the IP addresses of peer supervisors/workers only. Deploy within 3 days per the noisgate mitigation SLA for CRITICAL findings.
2
CRITICAL 9.8→HIGH 8.5
SEVERITY REDUCED
Deploy file integrity monitoring on /opt/charting/ and /opt/phoenix/bin/ — Use an out-of-band FIM agent (OSSEC, Wazul, Tripwire — NOT FortiSIEM itself) to alert on any modification to 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.
3
CRITICAL 9.8→IGNORE 0.0
SEVERITY REDUCED
Upgrade to FortiSIEM 7.5.1 or 7.6 — The definitive fix. FortiSIEM 7.5.1 sanitizes the 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.
4
CRITICAL 9.8→MEDIUM 5.5
SEVERITY REDUCED
Segment FortiSIEM management VLAN — Place the FortiSIEM appliance on a dedicated management network segment with ACLs restricting inbound access to only syslog sources (UDP/514, TCP/514, TCP/6514) and authorized admin workstations. Block all other ports including 27918, 7900, and 443 from general user VLANs. This reduces the population of hosts that could reach the vulnerable service even if the host firewall rule is misconfigured. Deploy within 3 days.
What doesn't work
  • 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.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo 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-ConceptTwo 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.
EPSS0.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 StatusNot 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 VectorCVSS: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 VersionsFortiSIEM 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 VersionsFortiSIEM 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 ExposureTCP/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 TimelineDiscovered 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.

  1. Horizon3.ai Deep Dive — CVE-2026-106173 gRPC Path Traversal RCE
  2. Fortinet PSIRT Advisory FG-IR-26-180
  3. Horizon3.ai PoC — GitHub
  4. Strix.ai CVE Intelligence — CVE-2026-106173
  5. SecurityOnline — Fortinet Patches Critical FortiSandbox and FortiSIEM Flaws
  6. VulDB — CVE-2026-106173
  7. 6sense — FortiSIEM Market Share
  8. Sploitus — 0xBlackash PoC
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.8/10)

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.

06 · Verification

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.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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