# CVE-2026-106173 Deep-Dive: FortiSIEM gRPC Path Traversal File Write RCE

Unauthenticated gRPC SendQueryResult on FortiSIEM tcp/27918 lets attackers path-traverse query-id and inject a null byte to overwrite any file, achieving RCE via cron-executed script.

- Payout: Research
- Severity: critical
- Program: FortiSIEM
- Write-up by: Zach Hanley
- Disclosed: 2026-10-20
- CVE: CVE-2026-106173
- Write-up quality: 8/10
- Original: https://horizon3.ai/attack-research/cve-2026-106173-deep-dive-fortisiem-grpc-path-traversal-file-write-rce/
- Library page: https://bugbountylibrary.com/r/blog%3Ahorizon3-ai-attack-research-cve-2026-106173-deep-dive-fortisiem-grpc-path-traversal-file-write-rce
- Tags: area:devices domain:network impact:rce kind:research platform:research sev:critical tech:grpc technique:chained technique:source-review vuln:path-traversal vuln:rce

**Target:** phQueryMaster gRPC service on tcp/27918 (grpcNotificationService::SendQueryResult)
**Root cause:** SendQueryResult concatenates attacker-controlled query-id into a file path without validation, and copies header values using client-declared length, allowing embedded null bytes to truncate the server-appended filename suffix.
**Steps:**
1. Connect unauthenticated to gRPC on tcp/27918 (server uses GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE).
2. Manually assemble a SendQueryResult gRPC message with metadata `query-id: ../../../../../opt/charting/redishb.sh\0ignored`, `src-ip: 127.0.0.1`, `period-start: 0`, `period-end: 0`.
3. Stream the desired script contents as file_content bytes.
4. Server builds fileDir from query-id, traverses out, and OS truncates at null byte, overwriting /opt/charting/redishb.sh in place.
5. Cron executes redishb.sh every 60 seconds, running attacker payload.
**How found:** Prior research focused on phMonitor tcp/7900; noticing a new gRPC service on tcp/27918 (phQueryMaster), they reviewed grpcNotificationServer::run() and found client certs not required, then inspected SendQueryResult handler and spotted unvalidated string concatenation of query-id into file path.
**Exploitation:** Single unauthenticated gRPC request with null-byte-injected query-id overwrites a cron-executed health-check script (/opt/charting/redishb.sh), which runs every 60s, yielding code execution as the script's user.
**Escalation:** Chained arbitrary file write with null-byte filename truncation to overwrite a recurringly executed cron script for RCE.
**Insight:** When a server appends its own suffix to an attacker-influenced path, look for length-prefixed header copying that permits embedded null bytes to truncate the suffix and gain exact filename control; also check gRPC SSL options for GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE.

## Proof of concept (copied exactly from the original report)

**gRPC SendQueryResult path traversal metadata**

```
query-id: ../../../../../tmp/fortisiem-cve-2026-106173
src-ip: 127.0.0.1
period-start: 0
period-end: 0
```

**Null-byte filename overwrite of redishb.sh**

```
query-id: ../../../../../opt/charting/redishb.sh\0ignored
src-ip: 127.0.0.1
period-start: 0
period-end: 0
```

