Resumen

El servidor de sincronización de Mnemosyne decodificaba tokens JWT de tipo bearer pero nunca verificaba sus firmas HMAC-SHA256. Cualquier token bien formado era aceptado, lo que permitía a un atacante no autenticado suplantar a cualquier usuario y leer o modificar sus datos de sincronización.

Severidad: Crítica

CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1

Esta puntuación asume que el endpoint del servidor de sincronización es accesible por red. Si el despliegue está limitado a localhost, la puntuación disminuye considerablemente y la severidad pasa a Alta o Media según la exposición local. Se recomienda revisar el modelo de amenazas de cada entorno.

Versiones afectadas

Todas las versiones de Mnemosyne que exponen el endpoint del servidor de sincronización, hasta v3.10.0 inclusive.

Versiones con parche

v3.10.1 (commit a0b6b871 en la rama security/jwt-signature-verification).

Descripción técnica

El servidor de sincronización utiliza tokens JWT de tipo bearer para autenticar a los clientes. Antes de v3.10.1, la comprobación de autenticación en mnemosyne/core/sync_server.py analizaba la cabecera y el payload del JWT mediante decodificación base64 y, a continuación, pasaba el token a una llamada de la librería jwt con opciones que deshabilitaban efectivamente la verificación de firma. El servidor aceptaba cualquier token bien formado independientemente de la firma, incluyendo tokens con alg: none y tokens firmados con una clave incorrecta.

La corrección introducida en v3.10.1 reemplaza la decodificación defectuosa por un verificador HS256 construido desde cero utilizando únicamente la librería estándar de Python, con las siguientes garantías:

  • Comparación de firma en tiempo constante mediante hmac.compare_digest
  • Comprobación estricta de alg: HS256, rechazando none y otros algoritmos
  • Validación de exp con conciencia de zona horaria UTC y margen de tolerancia
  • Errores explícitos con motivos de fallo específicos
  • Validación de tipos del payload decodificado antes de su uso

Impacto

Un atacante con acceso de red al servidor de sincronización puede:

  • Forjar un JWT para cualquier user_id sin conocer el secreto
  • Autenticarse como ese usuario en /sync/status, /sync/push y /sync/pull
  • Leer el estado de sincronización de la víctima
  • Enviar un estado de sincronización malicioso para corromper la base de datos local de la víctima
  • Moverse lateralmente dentro de un despliegue compartido (servidor de sincronización multiusuario)

La confidencialidad e integridad de los datos de sincronización quedan completamente comprometidas durante el periodo de exposición. No existe impacto sobre la disponibilidad del servidor.

Reproducción

python
import base64
import json
import requests

# Forja un JWT para cualquier usuario. No se requiere secreto.
def forge_jwt(user_id):
    header = base64.urlsafe_b64encode(
        json.dumps({"alg": "HS256", "typ": "JWT"}).encode()
    ).rstrip(b"=")
    payload = base64.urlsafe_b64encode(
        json.dumps({"user_id": user_id, "exp": 9999999999}).encode()
    ).rstrip(b"=")
    sig = b""
    return f"{header.decode()}.{payload.decode()}."

r = requests.get(
    "https://target.example.com/sync/status",
    headers={"Authorization": f"Bearer {forge_jwt('victim-user-id')}"},
)
print(r.status_code, r.json())

Una respuesta 200 OK con un payload de estado de sincronización válido confirma el bypass. El ataque no requiere credenciales, secreto ni acceso previo.

Mitigación

Actualizar a v3.10.1.

Para usuarios que no puedan actualizar de forma inmediata:

  • Restringir el acceso de red al endpoint del servidor de sincronización únicamente a clientes de confianza. Son opciones viables un firewall, un proxy inverso con mTLS o un bind a localhost con túnel SSH.
  • La vulnerabilidad no es explotable contra un endpoint inaccesible.

Soluciones alternativas

Ninguna. El parche es necesario para restaurar la integridad de la autenticación.

Créditos

  • Reportado por: Denis Hache (dplush). Comunicado por canal privado el 2026-06-13 con reproducción completa y ventana de divulgación coordinada.
  • Corrección: Denis Hache

Cronología

  • 2026-06-13: Recepción del informe inicial de Denis por canal privado.