
CVE-2026-20251 — RCE por deserialización jsonpickle en Splunk Secure Gateway (CVSS 8.8) | ReactiveZero Security Research
Investigador: Fady Oueslati · ReactiveZero Security Research
Referencia: 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)
Estado: Abierta — parche disponible
Un usuario autenticado con privilegios bajos puede lograr ejecución remota de código en un host de Splunk almacenando un documento manipulado en el KV Store (colección mobile_alerts). Splunk Secure Gateway (SSG) lee posteriormente ese documento y lo pasa directamente a jsonpickle.decode(), reconstruyendo objetos arbitrarios de Python — incluidos aquellos que ejecutan comandos del sistema operativo.
La llamada establece safe=True, pero esta bandera solo controla la ruta de evaluación heredada py/repr. Las etiquetas py/reduce, py/object, py/type, py/function y py/module no se ven afectadas y son totalmente explotables.
Un validador independiente (check_alert_data_valid_json) está diseñado para bloquear etiquetas peligrosas, pero hace un cortocircuito en la primera clave reconocida: cualquier documento cuya primera clave de nivel superior sea un py/object permitido (cuyo valor comienza con spacebridgeapp) devuelve True inmediatamente, dejando las claves hermanas — incluido un gadget py/reduce malicioso — completamente sin inspeccionar.
| Rama | Corregida en |
|---|---|
| Splunk Secure Gateway 3.9.x | 3.9.20 |
| Splunk Secure Gateway 3.10.x | 3.10.6 |
| Splunk Secure Gateway 3.8.x |
Instancia probada: SSG 3.9.19 sobre Splunk Enterprise 10.0.6 (macOS x86_64).
Paso 0 Un atacante con privilegios bajos escribe un documento de bypass
manipulado en la colección 'mobile_alerts' del KV Store a través de
la API REST de Splunk.
No se requiere rol de administrador ni de power.
Paso 1 SSG procesa una solicitud de obtención de alertas.
alerts_request_processor.py lee el documento y lo pasa a
check_alert_data_valid_json().
→ El validador ve "py/object": "spacebridgeapp..." como la PRIMERA
clave, devuelve True y nunca inspecciona el hermano "notification"
que transporta el gadget py/reduce.
Paso 2 El documento (ya validado) se pasa a
jsonpickle.decode(..., safe=True).
jsonpickle hace loadclass() del objeto Alert señuelo, lo instancia
y luego itera sobre sus atributos almacenados. Cuando alcanza el
valor "notification", se dispara _restore_reduce():
stage1 = f(*args) # unpickler.py ~línea 526
safe=True no tiene efecto en esta ruta de código.
Resultado Ejecución arbitraria de código como la cuenta de servicio de Splunk.
Solo se requiere un inicio de sesión válido de Splunk con privilegios bajos.
{
"py/object": "spacebridgeapp.data.alert_data.Alert",
"notification": {
"py/reduce": [
{"py/function": "subprocess.check_output"},
{"py/tuple": [["uname", "-a"]]}
]
}
}
El validador examina py/object primero (permitido), devuelve True y nunca llega a notification.
poc_cve_2026_20251.py demuestra las dos condiciones que constituyen la cadena de explotación completa:
El payload es deliberadamente benigno (solo lectura uname -a). Esto no es un exploit armado.
jsonpickle incluido con SSG (cargado desde /Applications/Splunk/etc/apps/splunk_secure_gateway/lib)python3 poc_cve_2026_20251.py -h 127.0.0.1
No lo ejecute contra sistemas de producción ni contra cualquier sistema que no sea de su propiedad y para el que no tenga autorización explícita por escrito para realizar pruebas.
Archivo: 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) # ← sumidero
Archivo: bin/spacebridgeapp/rest/devices/alert_helper.py
# El validador hace cortocircuito en la primera clave con prefijo '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") # ← devuelve inmediatamente
else:
return False
# ... las claves hermanas nunca se alcanzan
Principal: Actualice Splunk Secure Gateway a una versión corregida (3.9.20+, 3.10.6+ o 3.8.67+) y Splunk Enterprise a 10.0.7+ / 10.2.4+ / 10.4.0+.
Mitigaciones a corto plazo (si no es posible aplicar el parche de inmediato):
mobile_alertsPatrón defensivo de ingeniería: Nunca reconstruya tipos arbitrarios a partir de datos almacenados influenciados externamente. Reemplace jsonpickle.decode() en entradas alcanzables por un atacante con un analizador estricto validado por esquema, o proporcione una lista de permitidos classes= explícita a decode(). Asegúrese de que las rutinas de validación recorran completamente las estructuras anidadas en lugar de hacer cortocircuito en la primera clave reconocida.
El mismo lote de avisos incluye CVE-2026-20253 (CVSS 9.8, creación arbitraria de archivos sin autenticación a través de un endpoint lateral de PostgreSQL). Esta vulnerabilidad no estaba presente en la compilación probada de macOS x86_64 de Splunk Enterprise 10.0.6: el componente lateral de PostgreSQL no se incluye en esta plataforma, no existen binarios ni procesos laterales, y no se observó ningún puerto correspondiente.
Esto ilustra un principio importante de aseguramiento: una cadena de versión afectada es una condición necesaria pero no suficiente para la explotabilidad. La verificación a nivel de componente cambia materialmente el panorama de riesgo real.
| Campo | Valor |
|---|---|
| Ref. del compromiso | 2026FO-SPLUNK-20251 |
| Tipo de prueba |
ReactiveZero Security Research
| 3.8.67 |
| Splunk Enterprise | 10.0.7 / 10.2.4 / 10.4.0+ |
| Subprueba | Qué muestra |
|---|
| A — Bypass del validador | check_alert_data_valid_json() devuelve True para el documento de bypass, sin inspeccionar nunca el gadget py/reduce en el valor hermano |
| B — Ejecución de py/reduce | jsonpickle.decode(..., safe=True) ejecuta subprocess.check_output(['uname', '-a']), demostrando que safe=True no controla esta ruta de código |
| Verificación de vulnerabilidad de caja blanca (análisis estático de código) |
| Fecha | 26 de junio de 2026 |
| Alcance | Instancia de investigación local de Splunk Enterprise 10.0.6 (127.0.0.1:8089) |
| Clasificación | Confidencial |