Splunk Enterprise PostgreSQL Recovery Endpoint Injection Artifacts

Detects CVE-2026-20253 exploit artifacts against the Splunk Enterprise PostgreSQL sidecar recovery endpoints via complementary signals. Where endpoint or Network Packet Capture request-body logging is available, the rule matches PostgreSQL connection-string injection keywords, suspicious backupFile destinations, and known filesystem artifacts used to pivot from backup/restore primitives to file write or RCE. It also detects vulnerable recovery endpoint probing and empty-password Basic auth credentials observed in public exploit tooling.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/06/15"
  3integration = ["endpoint", "network_traffic", "zeek", "suricata", "azure", "gcp"]
  4maturity = "production"
  5updated_date = "2026/06/15"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects CVE-2026-20253 exploit artifacts against the Splunk Enterprise PostgreSQL sidecar recovery endpoints via
 11complementary signals. Where endpoint or Network Packet Capture request-body logging is available, the rule matches
 12PostgreSQL connection-string injection keywords, suspicious `backupFile` destinations, and known filesystem artifacts
 13used to pivot from backup/restore primitives to file write or RCE. It also detects vulnerable recovery endpoint probing
 14and empty-password Basic auth credentials observed in public exploit tooling.
 15"""
 16false_positives = [
 17    """
 18    Authorized red-team or penetration testing tooling exercising the CVE-2026-20253 exploit chain. Legitimate Splunk
 19    user activity should not produce PostgreSQL connection-string keywords, suspicious filesystem targets, empty Basic
 20    auth credentials, or unauthenticated 400 responses on the recovery endpoints.
 21    """,
 22]
 23from = "now-9m"
 24index = [
 25    "logs-endpoint.events.network*",
 26    "logs-network_traffic.http*",
 27    "logs-zeek.http*",
 28    "logs-suricata.eve*",
 29    "logs-azure.application_gateway*",
 30    "logs-gcp.loadbalancing_logs*",
 31]
 32language = "kuery"
 33license = "Elastic License v2"
 34name = "Splunk Enterprise PostgreSQL Recovery Endpoint Injection Artifacts"
 35note = """## Triage and analysis
 36
 37> **Disclaimer**:
 38> 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.
 39
 40### Investigating Splunk Enterprise PostgreSQL Recovery Endpoint Injection Artifacts
 41
 42This rule fires on distinct signals from the CVE-2026-20253 exploit chain and closely related recovery endpoint abuse:
 43
 44**Body injection** (endpoint/WAF/proxy sources): A POST to the recovery endpoints with a body
 45containing PostgreSQL connection-string keywords (`hostaddr=`, `host=`, `port=`, `passfile=`,
 46`dbname=`, `user=`, `password=`, `sslmode=`, or `service=`), `backupFile` path traversal, suspicious
 47absolute destinations (`/tmp/`, `/var/tmp/`, `/dev/shm/`, `/opt/splunk/etc/apps/`, cron paths, or SSH
 48authorized keys), or known filesystem artifacts (`.pgpass`, `/opt/splunk/etc/apps/`). The `database`
 49JSON field is passed directly to `pg_dump` or `pg_restore` as a connection string, so
 50attacker-supplied keywords override the hardcoded local configuration. The `backupFile` parameter
 51controls the dump file path, enabling arbitrary file placement.
 52
 53**Auth credential artifact** (Zeek sources): A POST to the recovery endpoints with an empty password
 54in the HTTP Basic auth header (`url.password : ""`). The exploit passes the Basic auth username
 55directly to `pg_dump` or `pg_restore` as a PostgreSQL username — any value works, so this branch does
 56not filter on specific usernames.
 57
 58**Vulnerable endpoint probing**: A POST to `/v1/postgres/recovery/backup` or
 59`/v1/postgres/recovery/restore` returning HTTP 400. Public probes use this response distinction because
 60vulnerable handlers parse the request and return 400, while patched or protected paths return 401.
 61
 62### Possible investigation steps
 63
 64- Check `http.response.status_code`. A `400` indicates the request reached the vulnerable sidecar
 65  handler; a `401` indicates access was blocked or the host is patched. A `400` on the backup or
 66  restore path is consistent with probing, and a `400` with injection content in the body is a
 67  high-confidence indicator of active exploitation.
 68- Identify which injection artifact triggered the rule:
 69  - `hostaddr=` or `host=` in `database` → server-side database pivot; check for outbound connections
 70    from the Splunk host to port 5432 or the attacker-supplied `port=` value.
 71  - `port=`, `user=`, `password=`, `sslmode=`, or `service=` in `database` → generic libpq connection
 72    string smuggling; review the full body for remote database or credential manipulation.
 73  - `passfile=/opt/splunk/var/packages/data/postgres/.pgpass` → local PostgreSQL credential reuse;
 74    this exact string is the public restore-chain artifact from watchTowr.
 75  - `backupFile` with `../` traversal or suspicious absolute paths (`/tmp/`, `/var/tmp/`, `/dev/shm/`,
 76    `/opt/splunk/etc/apps/`, cron paths, or SSH authorized keys) → arbitrary file placement; check for
 77    new or modified files at those paths.
 78  - `dbname=template1` with `passfile=` → the published two-stage restore payload.
 79- Correlate with host telemetry: new files under `/opt/splunk/etc/apps/`, `/opt/splunk/var/packages/`,
 80  or `/tmp/` created around the request time.
 81- Check for outbound PostgreSQL connections from the Splunk host following any `hostaddr=` or `host=`
 82  injection (see companion rule "Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence").
 83- Verify whether the target Splunk host is running an affected version (10.0.0-10.0.6 or
 84  10.2.0-10.2.3). Splunk Enterprise 10.4 and Splunk Cloud are not affected.
 85
 86### False positive analysis
 87
 88- No legitimate Splunk user workflow sends PostgreSQL connection-string keywords or filesystem paths
 89  to these recovery endpoints. This rule has very low false positive potential when the path filter
 90  is in scope.
 91
 92### Response and remediation
 93
 94- Any confirmed body injection on the recovery endpoints should be treated as active exploitation.
 95  Isolate the Splunk host immediately and preserve forensic state.
 96- Check for files placed by `backupFile` traversal and SQL objects loaded by the restore step.
 97- Patch Splunk Enterprise to an unaffected version. There is no vendor-provided workaround for
 98  CVE-2026-20253.
 99"""
100references = [
101    "https://nvd.nist.gov/vuln/detail/CVE-2026-20253",
102    "https://advisory.splunk.com/advisories/SVD-2026-0603",
103    "https://labs.watchtowr.com/why-use-app-level-auth-when-every-database-has-auth-splunk-enterprise-cve-2026-20253-pre-auth-rce/",
104    "https://attack.mitre.org/techniques/T1190/",
105]
106risk_score = 73
107rule_id = "2a7823db-0bc2-48f6-aa2f-e6aef233c6dc"
108setup = """## Setup
109
110This rule covers two detection paths with different telemetry requirements:
111
112**Body injection branch** (`http.request.body.content`): Populated by:
113- Elastic Defend (`logs-endpoint.events.network*`) on the Splunk host, capturing HTTP network
114  events at the endpoint level
115- Network Packet Capture (`logs-network_traffic.http*`) with HTTP body capture enabled for the
116  Splunk recovery paths
117
118Avoid enabling broad request-body logging without masking or filtering — bodies can contain
119credentials and PII. Scope capture to specific URL paths (e.g., `*/splunkd/*`) where possible.
120
121**Auth artifact branch** (`url.password`, `url.username`): Populated by:
122- Zeek (`logs-zeek.http*`) - parses HTTP Basic auth headers natively, no additional configuration
123
124**Vulnerable endpoint probing branch** (`http.response.status_code`): Populated by Network Packet
125Capture, Zeek, Suricata, Azure Application Gateway, and GCP Load Balancing where HTTP response
126metadata is visible to the sensor.
127
128Splunk Web listens on TCP port 8000 by default (`web.conf` `httpport`), which is included in the
129default Network Packet Capture/Packetbeat HTTP port list. Add any custom Splunk Web `httpport` value
130to the HTTP protocol configuration. Splunk's management service defaults to TCP port 8089
131(`mgmtHostPort`) and commonly uses TLS; add 8089 only if management/API traffic is directly exposed or
132visible to the sensor after decryption. If the PostgreSQL sidecar is directly exposed or monitored
133locally on TCP port 5435, add port 5435 as an HTTP port as well. Zeek and Suricata can identify
134plaintext HTTP on non-standard ports through protocol detection when their HTTP analyzers are enabled.
135For TLS deployments, the sensor must observe decrypted HTTP, sit downstream of TLS termination, or use
136proxy or load balancer logs that expose the HTTP path, method, and status code. Body-content detection
137still requires request-body capture for the Splunk recovery paths.
138
139Use the companion rule "Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence" for detections that work without
140request-body capture or auth header logging.
141"""
142severity = "high"
143tags = [
144    "Domain: Network",
145    "Use Case: Threat Detection",
146    "Use Case: Vulnerability",
147    "Use Case: Network Security Monitoring",
148    "Tactic: Initial Access",
149    "Data Source: Azure",
150    "Data Source: Elastic Defend",
151    "Data Source: GCP",
152    "Data Source: Google Cloud Platform",
153    "Data Source: Network Packet Capture",
154    "Data Source: Network Traffic",
155    "Data Source: Zeek",
156    "Data Source: Suricata",
157    "Resources: Investigation Guide",
158]
159timestamp_override = "event.ingested"
160type = "query"
161
162query = '''
163http.request.method:POST and
164url.path:("*splunkd/__raw/v1/postgres/recovery/*" or "/v1/postgres/recovery/*") and
165(
166  http.request.body.content:(
167    "*\"backupFile\"*../*" or
168    "*\"backupFile\"*/dev/shm/*" or
169    "*\"backupFile\"*/etc/cron*" or
170    "*\"backupFile\"*/home/*/.ssh/*" or
171    "*\"backupFile\"*/opt/splunk/bin/scripts/*" or
172    "*\"backupFile\"*/opt/splunk/etc/apps/*" or
173    "*\"backupFile\"*/root/*" or
174    "*\"backupFile\"*/tmp/*" or
175    "*\"backupFile\"*/var/tmp/*" or
176    "*\"backupFile\"*authorized_keys*" or
177    "*\"database\"*dbname=*" or
178    "*\"database\"*host=*" or
179    "*\"database\"*hostaddr=*" or
180    "*\"database\"*passfile=*" or
181    "*\"database\"*password=*" or
182    "*\"database\"*port=*" or
183    "*\"database\"*service=*" or
184    "*\"database\"*sslmode=*" or
185    "*\"database\"*user=*" or
186    "*/opt/splunk/etc/apps/*" or
187    "*/opt/splunk/var/packages/data/postgres/.pgpass*"
188  ) or
189  data_stream.dataset:zeek.http and url.password:"" or
190  data_stream.dataset:(azure.application_gateway or gcp.loadbalancing_logs or network_traffic.http or suricata.eve or zeek.http) and
191  url.path:(
192    "*splunkd/__raw/v1/postgres/recovery/backup" or
193    "*splunkd/__raw/v1/postgres/recovery/restore" or
194    /v1/postgres/recovery/backup or
195    /v1/postgres/recovery/restore
196  ) and
197  http.response.status_code:400
198)
199'''
200
201
202[[rule.threat]]
203framework = "MITRE ATT&CK"
204[[rule.threat.technique]]
205id = "T1190"
206name = "Exploit Public-Facing Application"
207reference = "https://attack.mitre.org/techniques/T1190/"
208
209
210[rule.threat.tactic]
211id = "TA0001"
212name = "Initial Access"
213reference = "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 Recovery Endpoint Injection Artifacts

This rule fires on distinct signals from the CVE-2026-20253 exploit chain and closely related recovery endpoint abuse:

Body injection (endpoint/WAF/proxy sources): A POST to the recovery endpoints with a body containing PostgreSQL connection-string keywords (hostaddr=, host=, port=, passfile=, dbname=, user=, password=, sslmode=, or service=), backupFile path traversal, suspicious absolute destinations (/tmp/, /var/tmp/, /dev/shm/, /opt/splunk/etc/apps/, cron paths, or SSH authorized keys), or known filesystem artifacts (.pgpass, /opt/splunk/etc/apps/). The database JSON field is passed directly to pg_dump or pg_restore as a connection string, so attacker-supplied keywords override the hardcoded local configuration. The backupFile parameter controls the dump file path, enabling arbitrary file placement.

Auth credential artifact (Zeek sources): A POST to the recovery endpoints with an empty password in the HTTP Basic auth header (url.password : ""). The exploit passes the Basic auth username directly to pg_dump or pg_restore as a PostgreSQL username — any value works, so this branch does not filter on specific usernames.

Vulnerable endpoint probing: A POST to /v1/postgres/recovery/backup or /v1/postgres/recovery/restore returning HTTP 400. Public probes use this response distinction because vulnerable handlers parse the request and return 400, while patched or protected paths return 401.

Possible investigation steps

  • Check http.response.status_code. A 400 indicates the request reached the vulnerable sidecar handler; a 401 indicates access was blocked or the host is patched. A 400 on the backup or restore path is consistent with probing, and a 400 with injection content in the body is a high-confidence indicator of active exploitation.
  • Identify which injection artifact triggered the rule:
    • hostaddr= or host= in database → server-side database pivot; check for outbound connections from the Splunk host to port 5432 or the attacker-supplied port= value.
    • port=, user=, password=, sslmode=, or service= in database → generic libpq connection string smuggling; review the full body for remote database or credential manipulation.
    • passfile=/opt/splunk/var/packages/data/postgres/.pgpass → local PostgreSQL credential reuse; this exact string is the public restore-chain artifact from watchTowr.
    • backupFile with ../ traversal or suspicious absolute paths (/tmp/, /var/tmp/, /dev/shm/, /opt/splunk/etc/apps/, cron paths, or SSH authorized keys) → arbitrary file placement; check for new or modified files at those paths.
    • dbname=template1 with passfile= → the published two-stage restore payload.
  • Correlate with host telemetry: new files under /opt/splunk/etc/apps/, /opt/splunk/var/packages/, or /tmp/ created around the request time.
  • Check for outbound PostgreSQL connections from the Splunk host following any hostaddr= or host= injection (see companion rule "Splunk Enterprise PostgreSQL Backup-to-Restore Potential RCE Sequence").
  • 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

  • No legitimate Splunk user workflow sends PostgreSQL connection-string keywords or filesystem paths to these recovery endpoints. This rule has very low false positive potential when the path filter is in scope.

Response and remediation

  • Any confirmed body injection on the recovery endpoints should be treated as active exploitation. Isolate the Splunk host immediately and preserve forensic state.
  • Check for files placed by backupFile traversal and SQL objects loaded by the restore step.
  • Patch Splunk Enterprise to an unaffected version. There is no vendor-provided workaround for CVE-2026-20253.

References

Related rules

to-top