lightrag-hku: SSRF via IPv6-transition address bypass (NAT64, IPv4-compatible, 6to4) of the native-markdown image-download guard
High7.1CVE-2026-85740 · Published Sep 22, 2026
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| lightrag-hku PyPI | < 1.5.5 | 1.5.5 |
Details and references
## Summary LightRAG's native markdown parser downloads external images referenced by an uploaded markdown or textpack document. The only SSRF guard, `_validated_addresses()` in `lightrag/parser/markdown/parser.py`, resolves the image host and rejects it when the resolved IP is not `is_global`. That check is evaluated on the raw resolved address and never decodes IPv6 transition wrappers that embed an internal IPv4. Python's `ipaddress` classifies a NAT64 (`64:ff9b::/96` and the RFC 8215 `64:ff9b:1::/48` prefix), IPv4-compatible (`::a.b.c.d`), or 6to4 (`2002::/16`) literal that wraps an internal IPv4 as globally routable, so the guard passes it. On a host with NAT64/DNS64 routing the request is then delivered to the embedded internal target (loopback, RFC1918, or a cloud metadata endpoint), and the fetched body is ingested. The plain and IPv4-mapped (`::ffff:`) forms of the same internal address are correctly blocked, so this is an encoding that defeats the existing filter. ## Affected component and versions - Package: `lightrag-hku` (LightRAG), the native markdown image-download path. - Component: `lightrag/parser/markdown/parser.py`, guard `_validated_addresses()` (the `if not (ip.is_global or ...)` check), reached from `_download()` -> `_build_guarded_opener().open(req)`. - Enabled by default: `download_enabled = _env_bool("NATIVE_MD_IMAGE_DOWNLOAD_ENABLED", True)`. - Affected: LightRAG `<= 1.5.4` (latest release at time of report, commit `9a45b64`). - Precondition: the caller can upload a document (API key via `Depends(combined_auth)`); the host has NAT64/DNS64 routing for the encoded address to reach the internal endpoint. ## Vulnerable code vs the guarded sibling Evaluated on the exact resolved addresses (CPython `ipaddress`): | Form | address | is_global | guard verdict | |---|---|---|---| | plain internal 127.0.0.1 | `127.0.0.1` | False | BLOCK (correct) | | IPv4-mapped | `::ffff:7f00:1` | False | BLOCK (correct) | | NAT64 64:ff9b::/96 | `64:ff9b::7f00:1` | True | PASS (bypass) | | NAT64 RFC8215 64:ff9b:1::/48 | `64:ff9b:1::7f00:1` | True | PASS (bypass) | | IPv4-compatible ::a.b.c.d | `::7f00:1` | True | PASS (bypass) | | 6to4 2002::/16 | `2002:7f00:1::` | True | PASS (bypass) | The guard already blocks the plain and IPv4-mapped internal forms (the forms the author tested), proving the intent is to reject internal destinations; it never canonicalizes the other transition wrappers, so they pass. > **Note , CPython version dependency of the table.** The `is_global` verdicts above depend on the interpreter. CPython gh-113171 (backported to 3.10.14 / 3.11.9 / 3.12.3+, ~2024-04) added `64:ff9b:1::/48` and `2002::/16` to `ipaddress._private_networks`, so on any current patch release those two rows return `is_global = False` and are already blocked by the stdlib before the guard is reached. The **`64:ff9b::/96` well-known NAT64 prefix and the IPv4-compatible `::/96` form bypass on every current CPython** , and `64:ff9b::/96` is exactly the prefix real DNS64/NAT64 deployments use, so the vulnerability stands. The four-row table reflects a pre-gh-113171 interpreter. The fix classifies by the embedded IPv4 and therefore closes all forms regardless of interpreter version. ## Severity High. CWE-918 (Server-Side Request Forgery). CVSS 3.1 vector `AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N`, base score 7.1. AC:H reflects the NAT64/DNS64 network precondition for end-to-end reach; PR:L because document upload requires the API key; S:C because the request pivots into an internal network segment; C:H for internal/metadata/secret disclosure. ## Proof of concept (deployed, both directions, verbatim) Environment: three Docker containers on an IPv6-enabled network - an internal victim (`10.66.0.2:80`, RFC1918) serving a unique secret, a DNS64/NAT64 gateway owning `64:ff9b::/96` that forwards `[64:ff9b::0a42:0002]:80` to `10.66.0.2:80`, and an attacker host with a client-side route `64:ff9b::/96 via <gateway>`. This models a client on
- CVSS 3.1
- CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-918
- Also known as
- CVE-2026-85740
- github.com/HKUDS/LightRAG/security/advisories/GHSA-vv3m-f8x4-7377
- nvd.nist.gov/vuln/detail/CVE-2026-85740
- github.com/HKUDS/LightRAG/pull/3426
- github.com/HKUDS/LightRAG/commit/9207e7fd10cf6df3cf26e3dd1921490b34a2f132
- github.com/HKUDS/LightRAG/commit/a25862177a9b00e0edb870778a42b4155924de09
- github.com/HKUDS/LightRAG/commit/c598545aa0084a427bcb19a84ade5e0ec31fa673
- github.com/HKUDS/LightRAG
- github.com/HKUDS/LightRAG/releases/tag/v1.5.5
More lightrag advisories
All| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Sep 22 | lightrag-hku: Sensitive Information Exposure Through Raw Exception Messages in API Error Responses CVE-2026-85709Medium5.3fixed in 1.5.5 | Medium5.3 | 1.5.5 |
| Sep 22 | lightrag-hku: Plaintext Passwords Compared Without Constant-Time Function CVE-2026-85725Medium5.9fixed in 1.5.5 | Medium5.9 | 1.5.5 |
| Sep 22 | lightrag-hku: No Rate Limiting on /login Endpoint Allows Brute-Force Attacks CVE-2026-85734Critical9.1fixed in 1.5.5 | Critical9.1 | 1.5.5 |
| Sep 22 | lightrag-hku: Stored Cross-Site Scripting (XSS) in the LightRAG WebUI chat/answer renderer via ingested content CVE-2026-86062Medium6.1fixed in 1.5.5 | Medium6.1 | 1.5.5 |
| Jul 20 | LightRAG is Vulnerable to Authentication Bypass: hardcoded DEFAULT_TOKEN_SECRET and public /auth-status defeat LIGHTRAG_API_KEY protection CVE-2026-61740Criticalfixed in 1.5.4 | Critical | 1.5.4 |
| Jul 20 | LightRAG: CORS Wildcard + Credentials Enables Any-Origin Credentialed Requests CVE-2026-61736Critical9.3fixed in 1.5.4 | Critical9.3 | 1.5.4 |