GHSA-xhjh-pmcv-23jw
Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams
Details
# Vulnerability Disclosure: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams
## Summary
The `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.
This 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.
**Severity:** Low (CVSS 3.7) **Affected Versions:** All versions containing this charMap entry **Vulnerable Component:** `lib/helpers/AxiosURLSearchParams.js:21`
## CWE
- **CWE-626:** Null Byte Interaction Error (Poison Null Byte) - **CWE-116:** Improper Encoding or Escaping of Output
## CVSS 3.1
**Score: 3.7 (Low)**
Vector: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N`
| Metric | Value | Justification | |---|---|---| | Attack Vector | Network | Attacker controls input parameters remotely | | 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 | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Impact limited to HTTP request URL | | Confidentiality | None | No confidentiality impact | | Integrity | Low | Null byte in URL can cause truncation in C-based backends, but requires a vulnerable downstream parser | | Availability | None | No availability impact |
## Vulnerable Code
**File:** `lib/helpers/AxiosURLSearchParams.js`, lines 13-26
```javascript function encode(str) { const charMap = { '!': '%21', // literal → encoded (SAFE direction) "'": '%27', // literal → encoded (SAFE direction) '(': '%28', // literal → encoded (SAFE direction) ')': '%29', // literal → encoded (SAFE direction) '~': '%7E', // literal → encoded (SAFE direction) '%20': '+', // standard transformation (SAFE) '%00': '\x00', // LINE 21: encoded → raw null byte (UNSAFE direction!) }; return encodeURIComponent(str).replace(/[!'()~]|%20|%00/g, function replacer(match) { return charMap[match]; }); } ```
### Why the Standard Flow Is NOT Affected
```javascript // buildURL.js:36 — uses its OWN encode function (lines 14-20), not AxiosURLSearchParams's const _encode = (options && options.encode) || encode; // buildURL's encode
// buildURL.js:53 — passes buildURL's encode to AxiosURLSearchParams new AxiosURLSearchParams(params, _options).toString(_encode); // external encoder used
// AxiosURLSearchParams.js:48 — when encoder is provided, internal encode is NOT used const _encode = encoder ? function(value) { return encoder.call(this, value, encode); } : encode; // ^^^^^^ // internal encode passed as 2nd arg but only used if // the external encoder explicitly delegates to it ```
## Proof of Concept
```javascript import AxiosURLSearchParams from './lib/helpers/AxiosURLSearchParams.js'; import buildURL from './lib/helpers/buildURL.js';
// Test 1: Direct AxiosURLSearchParams (VULNERABLE path) const params = new AxiosURLSearchParams({ file: 'test\x00.txt' }); const result = params.toString(); // NO encoder → uses internal encode with charMap console.log('Direct toString():', JSON.stringify(result)); // Output: "file=test\u0000.txt" (contains raw null byte) console.log('Hex:', Buffer.from(result).toString('hex')); // Output: 66696c653d74657374002e747874 (00 = null byte)
// Test 2: Via buildURL (NOT vulnerable — standard axios flow) const url = buildURL('http://example.com/api', { file: 'test\x00.txt' }); console.log('Via buildURL:', url); // Output: http://example.com/api?file=test%00.txt (%00 preserved safely) ```
## Verified PoC Output
``` Direct toString(): "file=test\u0000.txt" Contains raw null byte: true Hex: 66696c653d74657374002e747874
Via buildURL: http://example.com/api?file=test%00.txt Contains raw null byte: false Contains safe %00: true ```
## Impact Analysis
**Primary impact is limited** because the standard axios request flow is not affected. However:
- **Direct API users:** Applications using `AxiosURLSearchParams` directly for custom serialization are affected - **Custom paramsSerializer:** A `paramsSerializer.encode` that delegates to the internal encoder triggers the bug - **Code defect signal:** The directional inconsistency in charMap is a clear coding error with no legitimate use case
If null bytes reach a downstream C-based parser, impacts include URL truncation, WAF bypass, and log injection.
## Recommended Fix
Remove the `%00` entry from charMap and update the regex:
```javascript function encode(str) { const charMap = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+', // REMOVED: '%00': '\x00' }; return encodeURIComponent(str).replace(/[!'()~]|%20/g, function replacer(match) { // ^^^^ removed |%00 return charMap[match]; }); } ```
## Resources
- [CWE-626: Null Byte Interaction Error](https://cwe.mitre.org/data/definitions/626.html) - [CWE-116: Improper Encoding or Escaping of Output](https://cwe.mitre.org/data/definitions/116.html) - [OWASP: Embedding Null Code](https://owasp.org/www-community/attacks/Embedding_Null_Code) - [Axios GitHub Repository](https://github.com/axios/axios)
## Timeline
| Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-16 | Report revised: documented standard-flow limitation, corrected CVSS | | TBD | Report submitted to vendor via GitHub Security Advisory |
Are you affected?
Enter the version of the package you're using.