
CVE-2026-20251 — Vulnerabilità RCE da deserializzazione jsonpickle in Splunk Secure Gateway (CVSS 8.8) | ReactiveZero Security Research
Ricercatore: Fady Oueslati · ReactiveZero Security Research
Riferimento: 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)
Stato: Aperto — patch disponibile
Un utente autenticato con privilegi bassi può ottenere esecuzione remota di codice su un host Splunk salvando un documento appositamente costruito nel KV Store (collezione mobile_alerts). In seguito, Splunk Secure Gateway (SSG) legge quel documento e lo passa direttamente a jsonpickle.decode(), ricostruendo oggetti Python arbitrari — inclusi quelli che eseguono comandi di sistema operativo.
La chiamata imposta safe=True, ma questo flag controlla solo il percorso legacy di eval py/repr. I tag py/reduce, , , e non sono influenzati e sono pienamente sfruttabili.
py/objectpy/typepy/functionpy/moduleUn validatore separato (check_alert_data_valid_json) dovrebbe bloccare i tag pericolosi, ma corta circuita sulla prima chiave riconosciuta: qualsiasi documento la cui prima chiave di primo livello è un py/object consentito (valore che inizia con spacebridgeapp) restituisce True immediatamente, lasciando le chiavi sorelle — incluso un gadget py/reduce dannoso — completamente non ispezionate.
| Branch | Corretta 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+ |
Istanza testata: SSG 3.9.19 su Splunk Enterprise 10.0.6 (macOS x86_64).
Step 0 Un attaccante con privilegi bassi scrive un documento bypass
appositamente costruito nella collezione KV Store 'mobile_alerts'
tramite la REST API di Splunk.
Nessun ruolo admin o power richiesto.
Step 1 SSG elabora una richiesta di fetch degli alert.
alerts_request_processor.py legge il documento e lo passa a
check_alert_data_valid_json().
→ Il validatore vede "py/object": "spacebridgeapp..." come PRIMA
chiave, restituisce True e non ispeziona mai la chiave sorella
"notification" che trasporta il gadget py/reduce.
Step 2 Il documento (ora validato) viene passato a
jsonpickle.decode(..., safe=True).
jsonpickle loadclass()a l'oggetto Alert esca, lo istanzia,
poi itera i suoi attributi memorizzati. Quando raggiunge il valore
"notification", _restore_reduce() scatta:
stage1 = f(*args) # unpickler.py ~linea 526
safe=True non ha alcun effetto su questo percorso di codice.
Esito Esecuzione arbitraria di codice come account di servizio Splunk.
Richiede solo un login Splunk valido con privilegi bassi.
{
"py/object": "spacebridgeapp.data.alert_data.Alert",
"notification": {
"py/reduce": [
{"py/function": "subprocess.check_output"},
{"py/tuple": [["uname", "-a"]]}
]
}
}
Il validatore esamina prima py/object (consentito), restituisce True e non raggiunge mai notification.
poc_cve_2026_20251.py dimostra le due condizioni che costituiscono l'intera catena di exploit:
| Sub-prova | Cosa mostra |
|---|---|
| A — Bypass del validatore | check_alert_data_valid_json() restituisce True per il documento bypass, non ispezionando mai il gadget py/reduce nel valore fratello |
| B — Esecuzione di py/reduce | jsonpickle.decode(..., safe=True) esegue subprocess.check_output(['uname', '-a']), dimostrando che safe=True non controlla questo percorso di codice |
Il payload è deliberatamente benigno (uname -a in sola lettura). Non è un exploit armato.
jsonpickle incluso in SSG (caricato da /Applications/Splunk/etc/apps/splunk_secure_gateway/lib)python3 poc_cve_2026_20251.py -h 127.0.0.1
Non eseguire contro sistemi di produzione o qualsiasi sistema che non possiedi e per il quale non hai un'autorizzazione scritta esplicita al test.
File: bin/spacebridgeapp/request/alerts_request_processor.py
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
# Il validatore corto-circuita sulla prima chiave con prefisso 'py':
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") # ← restituisce immediatamente
else:
return False
# ... le chiavi sorelle non vengono mai raggiunte
Primario: Aggiornare Splunk Secure Gateway a una versione corretta (3.9.20+, 3.10.6+ o 3.8.67+) e Splunk Enterprise a 10.0.7+ / 10.2.4+ / 10.4.0+.
Mitigazioni a breve termine (se l'applicazione della patch non è immediatamente possibile):
mobile_alertsPattern di ingegneria difensiva: non ricostruire mai tipi arbitrari da dati memorizzati influenzati esternamente. Sostituire jsonpickle.decode() su input raggiungibile da un attaccante con un parser rigoroso e validato tramite schema, oppure fornire una allow-list esplicita classes= a decode(). Assicurarsi che le routine di validazione attraversino completamente le strutture nidificate invece di corto-circuitare sulla prima chiave riconosciuta.
Lo stesso batch di advisory include CVE-2026-20253 (CVSS 9.8, creazione arbitraria di file non autenticata tramite un endpoint sidecar PostgreSQL). Questa vulnerabilità non era presente sulla build macOS x86_64 testata di Splunk Enterprise 10.0.6: il componente sidecar PostgreSQL non viene distribuito su questa piattaforma, non esistono binari o processi sidecar e non è stata osservata alcuna porta corrispondente.
Questo illustra un importante principio di garanzia: una stringa di versione interessata è una condizione necessaria ma non sufficiente per lo sfruttamento. La verifica a livello di componente cambia sostanzialmente il quadro di rischio reale.
| Campo | Valore |
|---|---|
| Riferimento engagement | 2026FO-SPLUNK-20251 |
| Tipo di test | Verifica di vulnerabilità White-Box (Analisi statica del codice) |
| Data | 26 giugno 2026 |
| Ambito | Istanza di ricerca locale Splunk Enterprise 10.0.6 (127.0.0.1:8089) |
| Classificazione | Riservato |
ReactiveZero Security Research