Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence

Detects a POST to the Splunk Enterprise PostgreSQL backup endpoint followed by a POST to the restore endpoint from the same client to the same host within a 15-minute window. This sequence is unusual and can align with the public CVE-2026-20253 pre-authentication RCE chain, where an attacker stages a database dump via the backup path and executes attacker-controlled SQL via the restore path.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/06/15"
  3integration = ["network_traffic", "zeek", "suricata"]
  4maturity = "production"
  5updated_date = "2026/06/24"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects a POST to the Splunk Enterprise PostgreSQL backup endpoint followed by a POST to the restore endpoint from the
 11same client to the same host within a 15-minute window. This sequence is unusual and can align with the public
 12CVE-2026-20253 pre-authentication RCE chain, where an attacker stages a database dump via the backup path and executes
 13attacker-controlled SQL via the restore path.
 14"""
 15false_positives = [
 16    """
 17    Legitimate PostgreSQL recovery operations performed by Splunk administrators through the backup and restore API.
 18    These should be rare and originate from known management networks. If such operations occur in your environment,
 19    scope exceptions by source IP or approved management network rather than suppressing the rule entirely.
 20    """,
 21]
 22from = "now-19m"
 23language = "esql"
 24license = "Elastic License v2"
 25max_signals = 5
 26name = "Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence"
 27note = """## Triage and analysis
 28
 29> **Disclaimer**:
 30> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
 31
 32### Investigating Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence
 33
 34This rule fires when the same source IP sends a POST to both the `/backup` and `/restore` recovery
 35endpoints on the same Splunk host within 15 minutes. This two-step sequence is unusual and aligns with
 36the watchTowr CVE-2026-20253 RCE chain: the backup request can place an attacker-controlled
 37PostgreSQL dump on the Splunk filesystem (using `backupFile` path traversal or absolute path
 38injection), and the restore request can load that dump and execute attacker-controlled SQL through the
 39local PostgreSQL instance. A backup-plus-restore pair from an unrecognized source IP on a production
 40Splunk host should be investigated as potential exploitation.
 41
 42### Possible investigation steps
 43
 44- Review `http.response.status_code` for both requests. A `400` on the backup step indicates the
 45  sidecar handler was reached (the request parsed but failed), consistent with a vulnerable host.
 46- Check whether the backup request body (if captured by a WAF or proxy) contains `hostaddr=`,
 47  `host=`, or `passfile=` in the `database` field, or path traversal (`../`) or absolute paths
 48  in the `backupFile` field. These are the connection-string injection and file-placement artifacts
 49  specific to this exploit chain.
 50- Correlate with host telemetry on the Splunk server: look for new or modified files under
 51  `/opt/splunk/etc/apps/`, `/opt/splunk/var/packages/`, or `/tmp/` around the time of the requests.
 52- Check for Splunk process execution of `pg_dump` or `pg_restore` with unusual arguments,
 53  particularly connection strings containing external hostnames or IP addresses.
 54- Check for outbound network connections from the Splunk host to external PostgreSQL services
 55  (port 5432 or similar) following the backup request — this indicates successful connection-string
 56  injection causing the server to pivot to an attacker-controlled database.
 57- Verify whether the target Splunk host is running an affected version (10.0.0–10.0.6 or
 58  10.2.0–10.2.3). Splunk Enterprise 10.4 and Splunk Cloud are not affected.
 59
 60### False positive analysis
 61
 62- Authorized administrative recovery operations using the sidecar API. These should originate from
 63  known management IPs; create exceptions for approved management network ranges.
 64- Red-team or vulnerability management tooling that runs a full backup-to-restore probe as part of
 65  a CVE-2026-20253 exposure check.
 66
 67### Response and remediation
 68
 69- Treat a confirmed backup-and-restore sequence from an unrecognized source as active exploitation.
 70  Isolate the Splunk host from the network immediately and preserve forensic state.
 71- Examine the Splunk host for new or modified files, scheduled tasks, and PostgreSQL extension
 72  objects that may have been placed by the restore step.
 73- Patch Splunk Enterprise to an unaffected version (SVD-2026-0603). There is no vendor-provided
 74  workaround; patching is the only mitigation.
 75- Block Splunk Web port (default 8000) at the perimeter and restrict access to management networks
 76  while patching is in progress.
 77"""
 78references = [
 79    "https://nvd.nist.gov/vuln/detail/CVE-2026-20253",
 80    "https://advisory.splunk.com/advisories/SVD-2026-0603",
 81    "https://labs.watchtowr.com/why-use-app-level-auth-when-every-database-has-auth-splunk-enterprise-cve-2026-20253-pre-auth-rce/",
 82    "https://attack.mitre.org/techniques/T1190/",
 83]
 84risk_score = 47
 85rule_id = "7c7d2a89-b7e9-4e8d-bbf2-5a782fdcc803"
 86setup = """## Setup
 87
 88This rule requires HTTP request metadata with `url.path` and `http.request.method` populated from
 89one of the following sources visible to the sensor without TLS decryption:
 90- Zeek (`logs-zeek.http*`) where Splunk Web traffic is cleartext or the sensor is downstream of TLS
 91  termination.
 92- Suricata (`logs-suricata.eve*`) in the same deployment conditions
 93- Elastic Agent `network_traffic` integration (`logs-network_traffic.http*`) with HTTP parsing enabled
 94
 95Splunk Web listens on TCP port 8000 by default (`web.conf` `httpport`), which is included in the
 96default Network Packet Capture/Packetbeat HTTP port list. Add any custom Splunk Web `httpport` value
 97to the HTTP protocol configuration. Splunk's management service defaults to TCP port 8089
 98(`mgmtHostPort`) and commonly uses TLS; add 8089 only if management/API traffic is directly exposed or
 99visible to the sensor after decryption. If the PostgreSQL sidecar is directly exposed or monitored
100locally on TCP port 5435, add port 5435 as an HTTP port as well. Zeek and Suricata can identify
101plaintext HTTP on non-standard ports through protocol detection when their HTTP analyzers are enabled.
102For TLS deployments, the sensor must observe decrypted HTTP, sit downstream of TLS termination, or use
103proxy or load balancer logs that expose the HTTP path, method, and status code.
104
105The rule uses a 19-minute lookback and verifies that the first and last matching recovery events are
106no more than 15 minutes apart. Ensure `event.ingested` is populated and `timestamp_override` is set.
107"""
108severity = "medium"
109tags = [
110    "Domain: Network",
111    "Use Case: Threat Detection",
112    "Use Case: Vulnerability",
113    "Use Case: Network Security Monitoring",
114    "Tactic: Initial Access",
115    "Data Source: Network Packet Capture",
116    "Data Source: Network Traffic",
117    "Data Source: Zeek",
118    "Data Source: Suricata",
119    "Resources: Investigation Guide",
120]
121timestamp_override = "event.ingested"
122type = "esql"
123
124query = '''
125from logs-network_traffic.http*, logs-zeek.http*, logs-suricata.eve*
126| where http.request.method == "POST"
127  and (
128    url.path like "*splunkd/__raw/v1/postgres/recovery/*" or
129    url.path like "/v1/postgres/recovery/*"
130  )
131| eval Esql.is_backup = case(url.path like "*/backup", 1, 0)
132| eval Esql.is_restore = case(url.path like "*/restore", 1, 0)
133| stats
134    Esql.backup_count = SUM(Esql.is_backup),
135    Esql.restore_count = SUM(Esql.is_restore),
136    Esql.first_seen = MIN(@timestamp),
137    Esql.last_seen = MAX(@timestamp),
138    Esql.statuses = VALUES(http.response.status_code)
139  by source.ip, destination.ip
140| eval Esql.duration_minutes = DATE_DIFF("minute", Esql.first_seen, Esql.last_seen)
141| where Esql.backup_count >= 1 and Esql.restore_count >= 1
142  and Esql.duration_minutes <= 15
143| keep source.ip, destination.ip, Esql.*
144'''
145
146
147[[rule.threat]]
148framework = "MITRE ATT&CK"
149[[rule.threat.technique]]
150id = "T1190"
151name = "Exploit Public-Facing Application"
152reference = "https://attack.mitre.org/techniques/T1190/"
153
154
155[rule.threat.tactic]
156id = "TA0001"
157name = "Initial Access"
158reference = "https://attack.mitre.org/tactics/TA0001/"

Triage and analysis

Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.

Investigating Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence

This rule fires when the same source IP sends a POST to both the /backup and /restore recovery endpoints on the same Splunk host within 15 minutes. This two-step sequence is unusual and aligns with the watchTowr CVE-2026-20253 RCE chain: the backup request can place an attacker-controlled PostgreSQL dump on the Splunk filesystem (using backupFile path traversal or absolute path injection), and the restore request can load that dump and execute attacker-controlled SQL through the local PostgreSQL instance. A backup-plus-restore pair from an unrecognized source IP on a production Splunk host should be investigated as potential exploitation.

Possible investigation steps

  • Review http.response.status_code for both requests. A 400 on the backup step indicates the sidecar handler was reached (the request parsed but failed), consistent with a vulnerable host.
  • Check whether the backup request body (if captured by a WAF or proxy) contains hostaddr=, host=, or passfile= in the database field, or path traversal (../) or absolute paths in the backupFile field. These are the connection-string injection and file-placement artifacts specific to this exploit chain.
  • Correlate with host telemetry on the Splunk server: look for new or modified files under /opt/splunk/etc/apps/, /opt/splunk/var/packages/, or /tmp/ around the time of the requests.
  • Check for Splunk process execution of pg_dump or pg_restore with unusual arguments, particularly connection strings containing external hostnames or IP addresses.
  • Check for outbound network connections from the Splunk host to external PostgreSQL services (port 5432 or similar) following the backup request — this indicates successful connection-string injection causing the server to pivot to an attacker-controlled database.
  • Verify whether the target Splunk host is running an affected version (10.0.0–10.0.6 or 10.2.0–10.2.3). Splunk Enterprise 10.4 and Splunk Cloud are not affected.

False positive analysis

  • Authorized administrative recovery operations using the sidecar API. These should originate from known management IPs; create exceptions for approved management network ranges.
  • Red-team or vulnerability management tooling that runs a full backup-to-restore probe as part of a CVE-2026-20253 exposure check.

Response and remediation

  • Treat a confirmed backup-and-restore sequence from an unrecognized source as active exploitation. Isolate the Splunk host from the network immediately and preserve forensic state.
  • Examine the Splunk host for new or modified files, scheduled tasks, and PostgreSQL extension objects that may have been placed by the restore step.
  • Patch Splunk Enterprise to an unaffected version (SVD-2026-0603). There is no vendor-provided workaround; patching is the only mitigation.
  • Block Splunk Web port (default 8000) at the perimeter and restrict access to management networks while patching is in progress.

References

Related rules

to-top