README.md
Rendering markdown...
{
"engagement": {
"customer": "ReactiveZero Security Research",
"customer_short": "ReactiveZero",
"classification": "Confidential",
"reference": "2026FO-SPLUNK-20251",
"version": "1.0",
"date": "6/26/2026",
"author": "Fady Oueslati",
"test_type": "White-Box Vulnerability Verification (Static Code Analysis)",
"date_start": "June 26, 2026",
"date_end": "June 26, 2026",
"kickoff_date": "June 26, 2026",
"application_name": "Splunk Enterprise 10.0.6 with Splunk Secure Gateway 3.9.19",
"scope_domains": [
"Local Splunk Enterprise 10.0.6 research instance (127.0.0.1, management port 8089)",
"Splunk Secure Gateway app, version 3.9.19",
"App Key Value Store (KV Store) — mongod on port 8191"
],
"personal_data": [
"None — isolated local research instance with no production or personal data"
],
"positive_findings": "Verification was performed against a deliberately under-patched local research instance to confirm the reachability of CVE-2026-20251. It is worth noting that the most severe vulnerability in the same advisory batch, CVE-2026-20253 (CVSS 9.8, unauthenticated arbitrary file creation/truncation via a PostgreSQL sidecar service endpoint), was found NOT to be present on this deployment: the macOS x86_64 build of Splunk Enterprise 10.0.6 does not ship the PostgreSQL sidecar component, no sidecar binaries or processes exist on the host, and no corresponding listening port was observed. Although the installed version number falls within the affected range published by Splunk, the vulnerable component is simply absent on this platform, so CVE-2026-20253 is not exploitable here. This illustrates an important assurance principle: an affected version string is a necessary but not sufficient condition for exploitability, and component-level verification materially changes the real risk picture."
},
"findings": [
{
"title": "Unsafe jsonpickle Deserialization of KV Store Alert Data in Splunk Secure Gateway (CVE-2026-20251)",
"overview": "A low-privileged authenticated user can achieve remote code execution on the Splunk host by storing crafted alert data in the KV Store, which Splunk Secure Gateway later reconstructs into arbitrary Python objects via jsonpickle.",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"scope": "Splunk Secure Gateway app 3.9.19 on Splunk Enterprise 10.0.6 (KV Store collection 'mobile_alerts')",
"type": "",
"status": "Open",
"description": [
"The Splunk Secure Gateway (SSG) app deserializes App Key Value Store (KV Store) alert documents using the jsonpickle library, which reconstructs arbitrary Python objects from JSON. Because an attacker with only low-privilege access can write documents into the KV Store collection that feeds this deserialization sink, crafted data is reconstructed into attacker-chosen Python objects, resulting in remote code execution in the context of the Splunk process. The advisory batch also covers CVE-2026-20253, CVE-2026-20258 and CVE-2026-20252; this finding documents CVE-2026-20251 (CVSS 8.8).",
"Affected component: Splunk Secure Gateway, bin/spacebridgeapp/request/alerts_request_processor.py",
"Verification method: White-box static source analysis of the installed app (version 3.9.19, app state 'enabled') on a local Splunk Enterprise 10.0.6 instance. No weaponized exploit was executed; reachability was established by tracing the data flow from the attacker-writable KV Store collection to the deserialization sink and by demonstrating that the compensating input validator can be bypassed.",
"The deserialization sink. In alerts_request_processor.py the application reads an alert document directly from the 'mobile_alerts' KV Store collection and passes it to jsonpickle.decode():\n\n alert_json = await response.json()\n if not check_alert_data_valid_json(alert_json[0]):\n raise SpacebridgeApiRequestError(\"alert_data is not valid\", ...)\n alert = jsonpickle.decode(json.dumps(alert_json[0]), safe=True)\n\nThe document content originates from the KV Store and is therefore attacker-influenced for any user able to write to that collection.",
"The 'safe=True' flag does not prevent object instantiation. Although the call sets safe=True, this flag in the bundled jsonpickle only governs the legacy 'py/repr' eval() path. The unpickler's tag dispatcher (jsonpickle/unpickler.py) routes 'py/object', 'py/reduce', 'py/type', 'py/function' and 'py/module' tags to handlers that call loadclass() and instantiate or invoke arbitrary classes regardless of the safe flag. In particular, _restore_reduce() executes the classic __reduce__ primitive with the line 'stage1 = f(*args)', allowing an attacker-specified callable to be invoked with attacker-specified arguments. With no 'classes' allow-list supplied to decode(), any importable class can be loaded.",
"The compensating validator is bypassable. The function check_alert_data_valid_json() (bin/spacebridgeapp/rest/devices/alert_helper.py) is intended to reject dangerous tags: it returns False for tags other than 'py/id' and 'py/object', and constrains 'py/object' values to classes beginning with 'spacebridgeapp'. However, the validation loop returns immediately upon encountering the first 'py'-prefixed key. When that first key is a permitted 'py/object' (value starting with 'spacebridgeapp'), the function returns True without inspecting the object's sibling keys. Because the attacker controls key insertion order in the stored JSON document, a malicious 'py/reduce' gadget placed in a sibling value of a benign-looking 'spacebridgeapp' object is never validated, yet is fully reconstructed (and executed) by jsonpickle's _restore_object path. The net effect is a deterministic validator bypass leading to arbitrary object instantiation.",
"Chain summary: low-privilege write to the 'mobile_alerts' KV Store collection -> validator short-circuits on a permitted py/object key -> nested py/reduce gadget in a sibling value evades validation -> jsonpickle reconstructs and invokes the gadget -> code execution as the Splunk service account. KV Store (mongod) was confirmed listening on port 8191 and the SSG app was confirmed enabled on the test instance.",
"Affected versions (per advisory): Splunk Secure Gateway 3.10 (below 3.10.6), 3.9 (below 3.9.20), and 3.8 (below 3.8.67). The tested instance runs 3.9.19, which is below the fixed 3.9.20 and therefore affected."
],
"figures": [],
"risk": "Successful exploitation yields remote code execution in the context of the Splunk process, which typically runs with broad access to indexed data, configuration, credentials and the underlying host. Because Splunk is frequently the central SIEM aggregating logs from across the estate, compromise of this component can expose security telemetry, enable tampering with or deletion of audit data, and provide a pivot point deeper into the environment. Critically, the privilege barrier is low — no administrative or power role is required — so any authenticated foothold, including a compromised low-privilege account, can be escalated to full host compromise. The impact spans confidentiality, integrity and availability of the Splunk deployment.",
"recommendation": "ReactiveZero recommends upgrading Splunk Secure Gateway to a fixed version (3.9.20 or later on the 3.9 branch, 3.10.6+, or 3.8.67+) as the primary remediation, and upgrading Splunk Enterprise to a fixed release (10.0.7 / 10.2.4 or later) to address the remainder of the advisory batch. Where immediate patching is not possible, disable the Splunk Secure Gateway app if it is not in active use, and restrict who can write to KV Store collections by enforcing least-privilege roles and reviewing collection-level access controls. As a defensive engineering pattern, deserialization of stored data should never reconstruct arbitrary types: replace jsonpickle.decode() on externally-influenced data with a strict, schema-validated parser or supply an explicit class allow-list, and ensure validation routines fully traverse nested structures rather than short-circuiting on the first recognized key.",
"summary_recommendation": "Upgrade Splunk Secure Gateway to 3.9.20+ (or 3.10.6+/3.8.67+) and Splunk Enterprise to a fixed release. Avoid deserializing externally-influenced KV Store data into arbitrary Python objects; use schema validation or a class allow-list and enforce least-privilege KV Store access."
}
]
}