VDB
KO
LOW 3.7

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.

Affected packages

npm / axios
Introduced in: 1.0.0 Fixed in: 1.15.1
Fix npm install axios@1.15.1
npm / axios
Introduced in: 0 Fixed in: 0.31.1
Fix npm install axios@0.31.1

References