Skip to content

Commit dfd552d

Browse files
1 parent 605c8d3 commit dfd552d

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-pjwm-pj3p-43mv",
4+
"modified": "2026-05-29T15:59:30Z",
5+
"published": "2026-05-29T15:59:30Z",
6+
"aliases": [
7+
"CVE-2026-44492"
8+
],
9+
"summary": "axios's shouldBypassProxy does not recognize IPv4-mapped IPv6 addresses, allowing NO_PROXY bypass (incomplete fix for CVE-2025-62718)",
10+
"details": "### Summary\nshouldBypassProxy, introduced in v1.15.0 to fix CVE-2025-62718, does not normalise IPv4-mapped IPv6 addresses. When NO_PROXY lists an IPv4 address such as `127.0.0.1` or `169.254.169.254`, a request URL using the IPv4-mapped IPv6 form (`::ffff:7f00:1`, `::ffff:a9fe:a9fe`) still routes through the configured proxy. Node.js resolves these addresses to the underlying IPv4 host, so the request reaches the internal service via the proxy rather than being blocked.\n\n### Details\nlib/helpers/shouldBypassProxy.js (v1.15.0): \n\n```javascript \n const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); \n const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); \n \n // normalizeNoProxyHost strips brackets and trailing dots, but not ::ffff: prefix \n return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost)); \n```\n \nThe WHATWG URL parser canonicalises `http://[::ffff:127.0.0.1]/` to hostname `[::ffff:7f00:1]`. After bracket-stripping: `::ffff:7f00:1`. This string does not match 127.0.0.1 in NO_PROXY and is not in LOOPBACK_ADDRESSES, so shouldBypassProxy returns false and the proxy is used. proxy-from-env (called before shouldBypassProxy) has the same gap - it does not equate ::ffff:7f00:1 with 127.0.0.1 - so neither layer catches the bypass.\n\n### PoC\n```javascript\n\n// NO_PROXY=127.0.0.1,localhost,::1 HTTP_PROXY=http://attacker:8080\nimport shouldBypassProxy from 'axios/lib/helpers/shouldBypassProxy.js'; \n \n// All three should return true (bypass proxy). Only the first two do. \nconsole.log(shouldBypassProxy('http://127.0.0.1/')); // true [OK] \nconsole.log(shouldBypassProxy('http://[::1]/')); // true [OK] \nconsole.log(shouldBypassProxy('http://[::ffff:127.0.0.1]/')); // false <- bypass \nconsole.log(shouldBypassProxy('http://[::ffff:7f00:1]/')); // false <- bypass\n\n``` \n \nNode.js routes ::ffff:7f00:1 to 127.0.0.1: \n\n``` \n// net.connect({ host: '::ffff:7f00:1', port: 80 }) reaches a service \n// bound to 127.0.0.1:80 — confirmed on Node.js v24, Linux and macOS. \n``` \nCloud metadata SSRF: ::ffff:a9fe:a9fe = ::ffff:169.254.169.254. If NO_PROXY=169.254.169.254 is set to block IMDS access, a request to http://[::ffff:a9fe:a9fe]/latest/meta-data/ bypasses it. \n \n#### Fix \n \nCanonicalise IPv4-mapped IPv6 in normalizeNoProxyHost before any comparison: \n \n ```javascript \nconst ipv4MappedDotted = /^::ffff:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$/i; \nconst ipv4MappedHex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i; \n \nfunction hexToIPv4(a, b) { \n const hi = parseInt(a, 16), lo = parseInt(b, 16); \n return `${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff}`; \n} \n \nconst normalizeNoProxyHost = (hostname) => { \n if (!hostname) return hostname; \n if (hostname[0] === '[' && hostname.at(-1) === ']')\n hostname = hostname.slice(1, -1); \n hostname = hostname.replace(/\\.+$/, '').toLowerCase();\n \n let m; \n if ((m = hostname.match(ipv4MappedDotted))) return m[1]; \n if ((m = hostname.match(ipv4MappedHex))) return hexToIPv4(m[1], m[2]); \n return hostname; \n};\n\n```\n\n### Impact\nAny application that sets NO_PROXY to exclude internal or metadata endpoints and uses an HTTP/HTTPS proxy can have those exclusions bypassed by a URL using IPv4-mapped IPv6 notation. The attacker must control the request URL. In cloud environments with instance metadata services, this can lead to credential exfiltration.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "axios"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "1.0.0"
29+
},
30+
{
31+
"fixed": "1.16.0"
32+
}
33+
]
34+
}
35+
]
36+
},
37+
{
38+
"package": {
39+
"ecosystem": "npm",
40+
"name": "axios"
41+
},
42+
"ranges": [
43+
{
44+
"type": "ECOSYSTEM",
45+
"events": [
46+
{
47+
"introduced": "0"
48+
},
49+
{
50+
"fixed": "0.32.0"
51+
}
52+
]
53+
}
54+
],
55+
"database_specific": {
56+
"last_known_affected_version_range": "<= 0.31.1"
57+
}
58+
}
59+
],
60+
"references": [
61+
{
62+
"type": "WEB",
63+
"url": "https://github.com/axios/axios/security/advisories/GHSA-pjwm-pj3p-43mv"
64+
},
65+
{
66+
"type": "ADVISORY",
67+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62718"
68+
},
69+
{
70+
"type": "PACKAGE",
71+
"url": "https://github.com/axios/axios"
72+
}
73+
],
74+
"database_specific": {
75+
"cwe_ids": [
76+
"CWE-918"
77+
],
78+
"severity": "HIGH",
79+
"github_reviewed": true,
80+
"github_reviewed_at": "2026-05-29T15:59:30Z",
81+
"nvd_published_at": null
82+
}
83+
}

0 commit comments

Comments
 (0)