Splunk Secure Gateway jsonpickle Deserialization RCE (CVE-2026-20251)
by Fady Oueslati (ReactiveZero Security Research) · 2026-07-05
- Severity
- High
- CVE
- CVE-2026-20251
- Category
- web
- Affected product
- Splunk Secure Gateway (SSG) app on Splunk Enterprise
- Affected versions
- SSG 3.9.x < 3.9.20, 3.10.x < 3.10.6, 3.8.x < 3.8.67 (tested on SSG 3.9.19 / Splunk Enterprise 10.0.6)
- Disclosed
- 2026-07-05
- Patch status
- unpatched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-06 |
| Author / Researcher | Fady Oueslati (ReactiveZero Security Research) |
| CVE / Advisory | CVE-2026-20251 |
| Category | web |
| Severity | High |
| CVSS Score | 8.8 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| Status | PoC |
| Tags | splunk, deserialization, jsonpickle, rce, kv-store, python, validator-bypass, spacebridge |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Splunk Secure Gateway (SSG) app on Splunk Enterprise |
| Versions Affected | SSG 3.9.x < 3.9.20, 3.10.x < 3.10.6, 3.8.x < 3.8.67 (tested on SSG 3.9.19 / Splunk Enterprise 10.0.6) |
| Language / Platform | Python (jsonpickle), Splunk app backend |
| Authentication Required | Yes (any low-privileged authenticated Splunk user) |
| Network Access Required | Yes |
Summary
Splunk Secure Gateway lets mobile clients fetch alert data that is stored in the App Key Value Store and later reconstructed into Python objects using the jsonpickle library. A low-privileged authenticated user can write a crafted document to the mobile_alerts KV Store collection that, once read back by SSG, is deserialized via jsonpickle.decode(..., safe=True). The safe=True flag only blocks the legacy py/repr eval path and does nothing to stop py/reduce, py/object, py/type, py/function, or py/module tags from instantiating arbitrary classes and invoking arbitrary callables. A companion validator meant to catch dangerous tags returns as soon as it sees a permitted py/object key as the first key in the document, so a py/reduce gadget placed in a sibling field is never inspected. The included PoC demonstrates both halves of the chain — the validator bypass and the unsafe jsonpickle execution — using a benign uname -a payload rather than a weaponized one.
Vulnerability Details
Root Cause
check_alert_data_valid_json() in alert_helper.py short-circuits on the first py-prefixed key it encounters; a benign py/object lure as the first key causes it to return True without walking sibling keys, one of which can carry a py/reduce gadget that jsonpickle.decode(..., safe=True) will still execute since safe=True only guards the unrelated py/repr path.
Attack Vector
- Attacker authenticates to Splunk with any low-privileged account (no admin/power role needed).
- Attacker writes a crafted JSON document to the
mobile_alertsKV Store collection via the Splunk REST API. The document’s first key is a permittedpy/objectlure (e.g.spacebridgeapp.data.alert_data.Alert); a sibling key holds apy/reducegadget referencing a callable such assubprocess.check_output. - When SSG later processes an alert fetch,
alerts_request_processor.pyreads the document and callscheck_alert_data_valid_json(), which returnsTrueafter only inspecting the lure key. - The document is passed to
jsonpickle.decode(json.dumps(alert_json[0]), safe=True). jsonpickle instantiates the lure class, then iterates its stored attributes; on reaching the gadget value,_restore_reduce()executesstage1 = f(*args), invoking the attacker-chosen callable with attacker-chosen arguments.
Impact
Remote code execution as the Splunk service account, achievable by any authenticated low-privilege user with KV Store write access — no admin role or user interaction required.
Environment / Lab Setup
Target: Splunk Enterprise 10.0.6 (macOS x86_64) with Splunk Secure Gateway 3.9.19
Attacker: Python 3, a low-privileged Splunk account, network access to the target's REST API (127.0.0.1:8089 in the researcher's lab)
Proof of Concept
PoC Script
See
poc_cve_2026_20251.pyin this folder.
| |
The script inlines the exact check_alert_data_valid_json() logic shipped in SSG 3.9.19 and proves, in two steps, that the bypass document passes validation and that jsonpickle.decode(..., safe=True) still executes a py/reduce gadget (benign subprocess.check_output(['uname', '-a'])). It does not attack a live Splunk instance directly; it demonstrates the two conditions that compose the full chain.
Detection & Indicators of Compromise
Signs of compromise:
- Unexpected child processes spawned by the Splunk service account around alert-fetch requests.
- KV Store
mobile_alertsdocuments containingpy/reduce/py/functiontags or attribute nesting inconsistent with the legitimateAlertschema. - SSG logs showing alert documents with nested
py-prefixed keys beyond the expected lure object.
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade Splunk Secure Gateway to 3.9.20+ / 3.10.6+ / 3.8.67+, and Splunk Enterprise to 10.0.7+ / 10.2.4+ / 10.4.0+ |
| Interim mitigation | Disable the Splunk Secure Gateway app if unused; restrict KV Store write access via least-privilege roles and review mobile_alerts collection ACLs; avoid deserializing externally-influenced data with jsonpickle.decode() absent a classes= allow-list |
References
Notes
Mirrored from https://github.com/reactivezero/CVE-2026-20251 on 2026-07-05.
| |