{"id":"CVE-2026-42040","aliases":["GHSA-xhjh-pmcv-23jw"],"url":"https://o3.security/vulnerability/CVE-2026-42040","summary":"Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams","details":"# Vulnerability Disclosure: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams\n\n## Summary\n\nThe `encode()` function in `lib/helpers/AxiosURLSearchParams.js` contains a character mapping (`charMap`) at line 21 that **reverses** the safe percent-encoding of null bytes. After `encodeURIComponent('\\x00')` correctly produces the safe sequence `%00`, the charMap entry `'%00': '\\x00'` converts it back to a raw null byte.\n\nThis is a clear encoding defect: every other charMap entry encodes in the safe direction (literal → percent-encoded), while this single entry decodes in the opposite (dangerous) direction.\n\n**Severity:** Low (CVSS 3.7)\n**Affected Versions:** All versions containing this charMap entry\n**Vulnerable Component:** `lib/helpers/AxiosURLSearchParams.js:21`\n\n## CWE\n\n- **CWE-626:** Null Byte Interaction Error (Poison Null Byte)\n- **CWE-116:** Improper Encoding or Escaping of Output\n\n## CVSS 3.1\n\n**Score: 3.7 (Low)**\n\nVector: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N`\n\n| Metric | Value | Justification |\n|---|---|---|\n| Attack Vector | Network | Attacker controls input parameters remotely |\n| Attack Complexity | High | Standard axios request flow (`buildURL`) uses its own `encode` function which does NOT have this bug. Only triggered via direct `AxiosURLSearchParams.toString()` without an encoder, or via custom `paramsSerializer` delegation |\n| Privileges Required | None | No authentication needed |\n| User Interaction | None | No user interaction required |\n| Scope | Unchanged | Impact limited to HTTP request URL |\n| Confidentiality | None | No confidentiality impact |\n| Integrity | Low | Null byte in URL can cause truncation in C-based backends, but requires a vulnerable downstream parser |\n| Availability | None | No availability impact |\n\n## Vulnerable Code\n\n**File:** `lib/helpers/AxiosURLSearchParams.js`, lines 13-26\n\n```javascript\nfunction encode(str) {\n  const charMap = {\n    '!': '%21',     // literal → encoded (SAFE direction)\n    \"'\": '%27',     // literal → encoded (SAFE direction)\n    '(': '%28',     // literal → encoded (SAFE direction)\n    ')': '%29',     // literal → encoded (SAFE direction)\n    '~': '%7E',     // literal → encoded (SAFE direction)\n    '%20': '+',     // standard transformation (SAFE)\n    '%00': '\\x00',  // LINE 21: encoded → raw null byte (UNSAFE direction!)\n  };\n  return encodeURIComponent(str).replace(/[!'()~]|%20|%00/g, function replacer(match) {\n    return charMap[match];\n  });\n}\n```\n\n### Why the Standard Flow Is NOT Affected\n\n```javascript\n// buildURL.js:36 — uses its OWN encode function (lines 14-20), not AxiosURLSearchParams's\nconst _encode = (options && options.encode) || encode;  // buildURL's encode\n\n// buildURL.js:53 — passes buildURL's encode to AxiosURLSearchParams\nnew AxiosURLSearchParams(params, _options).toString(_encode);  // external encoder used\n\n// AxiosURLSearchParams.js:48 — when encoder is provided, internal encode is NOT used\nconst _encode = encoder ? function(value) { return encoder.call(this, value, encode); } : encode;\n//                                                                              ^^^^^^\n//                                           internal encode passed as 2nd arg but only used if\n//                                           the external encoder explicitly delegates to it\n```\n\n## Proof of Concept\n\n```javascript\nimport AxiosURLSearchParams from './lib/helpers/AxiosURLSearchParams.js';\nimport buildURL from './lib/helpers/buildURL.js';\n\n// Test 1: Direct AxiosURLSearchParams (VULNERABLE path)\nconst params = new AxiosURLSearchParams({ file: 'test\\x00.txt' });\nconst result = params.toString();  // NO encoder → uses internal encode with charMap\nconsole.log('Direct toString():', JSON.stringify(result));\n// Output: \"file=test\\u0000.txt\" (contains raw null byte)\nconsole.log('Hex:', Buffer.from(result).toString('hex'));\n// Output: 66696c653d74657374002e747874  (00 = null byte)\n\n// Test 2: Via buildURL (NOT vulnerable — standard axios flow)\nconst url = buildURL('http://example.com/api', { file: 'test\\x00.txt' });\nconsole.log('Via buildURL:', url);\n// Output: http://example.com/api?file=test%00.txt  (%00 preserved safely)\n```\n\n## Verified PoC Output\n\n```\nDirect toString(): \"file=test\\u0000.txt\"\nContains raw null byte: true\nHex: 66696c653d74657374002e747874\n\nVia buildURL: http://example.com/api?file=test%00.txt\nContains raw null byte: false\nContains safe %00: true\n```\n\n## Impact Analysis\n\n**Primary impact is limited** because the standard axios request flow is not affected. However:\n\n- **Direct API users:** Applications using `AxiosURLSearchParams` directly for custom serialization are affected\n- **Custom paramsSerializer:** A `paramsSerializer.encode` that delegates to the internal encoder triggers the bug\n- **Code defect signal:** The directional inconsistency in charMap is a clear coding error with no legitimate use case\n\nIf null bytes reach a downstream C-based parser, impacts include URL truncation, WAF bypass, and log injection.\n\n## Recommended Fix\n\nRemove the `%00` entry from charMap and update the regex:\n\n```javascript\nfunction encode(str) {\n  const charMap = {\n    '!': '%21',\n    \"'\": '%27',\n    '(': '%28',\n    ')': '%29',\n    '~': '%7E',\n    '%20': '+',\n    // REMOVED: '%00': '\\x00'\n  };\n  return encodeURIComponent(str).replace(/[!'()~]|%20/g, function replacer(match) {\n    //                                           ^^^^ removed |%00\n    return charMap[match];\n  });\n}\n```\n\n## Resources\n\n- [CWE-626: Null Byte Interaction Error](https://cwe.mitre.org/data/definitions/626.html)\n- [CWE-116: Improper Encoding or Escaping of Output](https://cwe.mitre.org/data/definitions/116.html)\n- [OWASP: Embedding Null Code](https://owasp.org/www-community/attacks/Embedding_Null_Code)\n- [Axios GitHub Repository](https://github.com/axios/axios)\n\n## Timeline\n\n| Date | Event |\n|---|---|\n| 2026-04-15 | Vulnerability discovered during source code audit |\n| 2026-04-16 | Report revised: documented standard-flow limitation, corrected CVSS |\n| TBD | Report submitted to vendor via GitHub Security Advisory |","published":"2026-04-24T17:40:31.125Z","modified":"2026-08-12T03:51:18.279808399Z","cvss":{"score":3.7,"severity":"LOW","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N"},"epss":{"score":0.00217,"percentile":0.12278,"asOf":"2026-08-14"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"axios","fixedVersion":"1.15.1"},{"ecosystem":"npm","name":"axios","fixedVersion":"0.31.1"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/42xxx/CVE-2026-42040.json"},{"type":"ADVISORY","url":"https://github.com/axios/axios/security/advisories/GHSA-xhjh-pmcv-23jw"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-42040"},{"type":"PACKAGE","url":"https://github.com/axios/axios"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:18.279808399Z"}}