{"id":"CVE-2026-59163","aliases":[],"url":"https://o3.security/vulnerability/CVE-2026-59163","summary":"Mnemosyne has JWT signature verification bypass sync server that allows authentication bypass","details":"### Summary\n\nThe Mnemosyne sync server's authentication check decoded JWT bearer tokens but never verified their HMAC-SHA256 signatures. Any well-formed token was accepted, allowing an unauthenticated attacker to impersonate any user and read or modify their sync data.\n\n**Severity: Critical**\n\nCVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1\n\nAssumes the sync server endpoint is network-reachable. If your deployment is localhost-only, the score drops substantially and severity becomes High or Medium depending on local exposure. Confirm your threat model.\n\n### Affected versions\n\nAll mnemosyne versions exposing the sync server endpoint, up to and including v3.10.0.\n\n### Patched versions\n\nv3.10.1 (commit a0b6b871 on branch security/jwt-signature-verification)\n\n___\n\n### Description\n\nThe sync server uses JWT bearer tokens to authenticate clients. Prior to v3.10.1, the auth check in mnemosyne/core/sync_server.py parsed the JWT's header and payload using base64 decoding, then passed the token to a jwt library call with options that effectively disabled signature verification. The server accepted any well-formed token regardless of the signature, including tokens with alg: none and tokens signed with the wrong key.\n\nThe fix in v3.10.1 replaces the broken decode with a from-scratch HS256 verifier using only the Python standard library:\n\n- Constant-time signature comparison via hmac.compare_digest\n- Strict alg: HS256 check, rejecting none and other algorithms\n- UTC-aware exp validation with leeway\n- Loud errors with specific failure reasons\n- Type validation of decoded payload before use\n\n### Impact\n\nAn attacker with network access to the sync server can:\n\n- Forge a JWT for any user_id without knowing the secret\n- Authenticate as that user to /sync/status, /sync/push, and /sync/pull\n- Read the victim's sync state\n- Push malicious sync state to corrupt the victim's local database\n- Pivot within a shared deployment (multi-user sync server)\n\nConfidentiality and integrity of sync data are fully compromised for the duration of exposure. There is no impact on the server's availability.\n\n### Reproduction\n\n```python\nimport base64\nimport json\nimport requests\n\n# Forge a JWT for any user. No secret required.\ndef forge_jwt(user_id):\n    header = base64.urlsafe_b64encode(\n        json.dumps({\"alg\": \"HS256\", \"typ\": \"JWT\"}).encode()\n    ).rstrip(b\"=\")\n    payload = base64.urlsafe_b64encode(\n        json.dumps({\"user_id\": user_id, \"exp\": 9999999999}).encode()\n    ).rstrip(b\"=\")\n    sig = b\"\"\n    return f\"{header.decode()}.{payload.decode()}.\"\n\nr = requests.get(\n    \"https://target.example.com/sync/status\",\n    headers={\"Authorization\": f\"Bearer {forge_jwt('victim-user-id')}\"},\n)\nprint(r.status_code, r.json())\n```\n\nA 200 OK response with valid sync status payload confirms the bypass. The attack requires no credentials, no secret, and no prior access.\n\n### Mitigation\n\nUpgrade to v3.10.1.\n\nFor users who cannot upgrade immediately:\n\n- Restrict network access to the sync server endpoint to trusted clients only. Firewall, reverse proxy with mTLS, or localhost bind with SSH tunnel are all viable.\n- The vulnerability is not exploitable against an unreachable endpoint.\n\n### Workarounds\n\nNone. The patch is required to restore authentication integrity.\n\n### Credits\n\n- Reporter: Denis Hache (dplush). Reported via private channel on 2026-06-13 with full reproduction and a coordinated disclosure window.\n- Fix: Denis Hache\n\n### Timeline\n- 2026-06-13: Initial report received from Denis via private channel.","published":"2026-09-18T17:54:26Z","modified":"2026-09-18T18:00:04.814756334Z","cvss":{"score":9.1,"severity":"CRITICAL","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"PyPI","name":"mnemosyne-memory","fixedVersion":"3.10.1"}],"fix":{"url":"https://github.com/mnemosyne-oss/mnemosyne/pull/373","label":"mnemosyne-oss/mnemosyne#373"},"references":[{"type":"WEB","url":"https://github.com/AxDSan/mnemosyne/security/advisories/GHSA-xcw4-53cc-hv32"},{"type":"WEB","url":"https://github.com/mnemosyne-oss/mnemosyne/pull/373"},{"type":"WEB","url":"https://github.com/mnemosyne-oss/mnemosyne/commit/a0b6b8711a1a485304971710dc3571e29ff9dbeb"},{"type":"PACKAGE","url":"https://github.com/AxDSan/mnemosyne"},{"type":"WEB","url":"https://github.com/mnemosyne-oss/mnemosyne/releases/tag/v3.10.1"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-18T18:00:04.814756334Z"}}