← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-44494 · CWE-441 · Disclosed 2026-05-29

axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a loaded tow hitch, not the truck that breaks through the gate

CVE-2026-44494 is a prototype-pollution gadget in axios's Node HTTP adapter, not a direct prototype-pollution source by itself. In affected versions >=1.0.0, <1.16.0, axios can inherit a polluted Object.prototype.proxy value because config.proxy is read through the prototype chain; that can route outbound axios traffic through an attacker-controlled proxy and expose credentials, request bodies, URLs, and responses.

The vendor's HIGH 8.7 score captures the *impact* once the chain is assembled, but overstates the *standalone enterprise urgency*. The decisive friction is that the attacker first needs a separate way to pollute Object.prototype inside the same Node.js runtime; without that first-stage bug, axios does nothing dangerous on its own. That makes this a chained, second-stage library risk rather than a clean unauthenticated internet-to-RCE style event.

"Serious impact if chained, but axios is the trigger, not the entry point; this is not a standalone remote compromise."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Land a prototype-pollution source in the same Node process

The attacker first needs a separate vulnerable parser/merge/path-setter in the application stack, such as a request parser or utility library that can write attacker-controlled properties onto Object.prototype. This is the real entry point; axios is only useful after that first-stage bug succeeds. Typical weaponized sources are qs-style parser abuse or unsafe deep-merge behavior discussed in the prototype-pollution research literature.
Conditions required:
  • Attacker can send crafted data into the application
  • A separate prototype-pollution source exists somewhere in the same runtime
  • That first-stage source is reachable in production
Where this breaks in practice:
  • No exploit exists if the app has no reachable prototype-pollution source
  • Modern schema validation and safe parsers often kill the chain before pollution happens
  • This prerequisite already implies application-layer exposure to attacker-controlled input
Detection/coverage: SAST/SCA can often flag the source vulnerability, but scanners aimed only at axios will miss whether the end-to-end chain is actually reachable.
STEP 02

Pollute Object.prototype.proxy

Once the source bug lands, the attacker sets a malicious proxy object on Object.prototype, pointing to an attacker-controlled host and port. This is the exact gadget state described in the advisory: axios later reads config.proxy with normal property access and inherits the polluted value if no own proxy property is set.
Conditions required:
  • Prototype pollution succeeded
  • The polluted process continues serving requests after the write
  • Axios requests are made without an explicit own proxy override
Where this breaks in practice:
  • Some apps restart workers frequently, limiting persistence
  • Some code paths always set proxy explicitly, which blocks this inheritance path
  • Browser-side axios use is less relevant here; the dangerous path is the Node HTTP adapter
Detection/coverage: EDR rarely sees this as a distinct event. Runtime instrumentation or test harnesses that inspect prototype state can catch it; most vuln scanners cannot.
STEP 03

Trigger axios HTTP adapter proxy routing

A normal application request then causes axios to build outbound HTTP traffic. In vulnerable versions, the Node HTTP adapter reads inherited config.proxy and passes it into setProxy(), transparently steering requests to the attacker's proxy instead of the intended destination.
Conditions required:
  • The target service uses axios on Node.js
  • The relevant code path makes outbound HTTP(S) requests
  • The process is still polluted when the request is sent
Where this breaks in practice:
  • No impact if the service never makes outbound axios calls
  • Strict egress policy or proxy allowlists can block arbitrary attacker proxy destinations
  • Service-to-service traffic on private networks may be hard for an attacker to monetize without useful credentials in transit
Detection/coverage: Network telemetry can reveal unexpected proxy destinations, abnormal CONNECT usage, or outbound requests to unapproved hosts. Package scanners only prove version exposure, not runtime abuse.
STEP 04

Intercept credentials and tamper with responses

With the proxy in path, the attacker can observe authorization headers, cookies, API keys, request bodies, internal hostnames, and returned data, then modify responses before the app consumes them. The blast radius is process-wide for axios traffic in that runtime, which is why the impact is real even though the exploit chain is narrow.
Conditions required:
  • Sensitive axios traffic exists in the process
  • Proxy redirection is not blocked by egress controls
  • The attacker can meaningfully use the intercepted material
Where this breaks in practice:
  • If outbound traffic is low-value or non-sensitive, impact drops sharply
  • TLS, certificate validation, and proxy architecture may limit straightforward content interception in some deployments
  • Short-lived credentials reduce post-capture usefulness
Detection/coverage: Look for anomalous outbound proxy hosts, unexpected authentication failures, changed upstream response patterns, and suspicious DNS or egress logs. No internet scanner will see this because axios is an embedded library.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo CISA KEV listing and no authoritative public evidence of active exploitation found in the reviewed sources.
Proof-of-concept availabilityA public verified PoC is embedded in the GitHub advisory / OSV record, including Object.prototype.proxy pollution and request interception.
EPSSNo reliable FIRST EPSS score was surfaced in the reviewed primary sources for this CVE yet; treat EPSS as unavailable/immature, not as risk-reducing evidence.
KEV status and datesNot KEV-listed. Public disclosure/publishing date is 2026-05-29.
CVSS vector meaningCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N correctly flags high attack complexity; the hidden reality behind that AC:H is 'requires a separate prototype-pollution source in the same runtime.'
Affected versionsGitHub/OSV list axios npm versions >= 1.0.0, < 1.16.0 as affected.
Fixed versionsUpgrade to 1.16.0 or later. For this npm library, distro-style backport guidance is generally not applicable; verify your lockfile/SBOM, not an OS package feed.
Scanning and exposure realityShodan/Censys/FOFA are basically irrelevant here. Axios is a dependency, not a network appliance; exposure must be measured with SCA/SBOM/runtime inventory, not internet census data.
Researcher / reporterThe GitHub advisory was published in axios/axios and attributes publication to jasonsaayman on 2026-05-29.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (6.3/10)

The single biggest downgrading factor is that axios is not the initial foothold: exploitation requires a separate prototype-pollution vulnerability reachable in the same Node.js process. That sharply narrows the exposed population from 'everyone running axios' to 'services running vulnerable axios *and* a reachable pollution source *and* valuable outbound traffic,' which is not a HIGH-at-scale patch emergency.

HIGH Technical impact once the exploit chain is completed
HIGH Assessment that this is a chained gadget rather than a standalone axios compromise
MEDIUM Population-level prevalence of reachable first-stage prototype-pollution sources in enterprise Node services

Why this verdict

  • Downgrade for attacker position: this is not unauthenticated remote -> axios compromise by itself. The attacker must first gain a remote path to pollute Object.prototype through another bug in the same runtime.
  • Downgrade for exposure population: axios is not internet-discoverable as a service. Real exposure is only the subset of Node applications that both use vulnerable axios and have a reachable first-stage prototype-pollution source.
  • Downgrade for chain fragility: modern controls like input validation, safer parsers, dependency hygiene, worker restarts, and egress filtering can break the chain at multiple points before the proxy hijack ever occurs.
  • Keep it at MEDIUM, not LOW: if the chain exists, impact is nasty. The attacker can transparently proxy all axios traffic in that process, steal credentials, and tamper with responses without noisy application errors.

Why not higher?

A higher rating would fit only if axios were the initial exploit primitive or if there were confirmed in-the-wild campaigns chaining this reliably at scale. Here, the vulnerability is a second-stage gadget that depends on another reachable bug and on meaningful outbound axios traffic, so the real-world target set is much smaller than the vendor score suggests.

Why not lower?

This is not harmless version-churn. Once the prerequisite pollution exists, the attacker gets a powerful credential-harvesting and response-tampering position inside the service's trust boundary, and axios is deployed widely enough that many enterprises will find some meaningful exposure in Node estates.

05 · Compensating Control

What to do — in priority order.

  1. Sweep for first-stage prototype-pollution sources — Hunt for reachable parser/merge/path-setter flaws in the same Node services that use axios, because those are the real exploit enablers. There is no mitigation SLA for this MEDIUM finding — go straight to the 365-day remediation window, but do this first on internet-facing services and credential-heavy integrations.
  2. Restrict outbound egress to approved destinations — Force application tiers to use approved proxies or explicit allowlists so a polluted config.proxy cannot send traffic to arbitrary attacker infrastructure. There is no mitigation SLA for this MEDIUM finding, so implement this as part of normal network-hardening work inside the remediation window, prioritizing services that handle secrets or call internal APIs.
  3. Alert on unexpected proxying and new egress paths — Monitor for unapproved outbound proxy hosts, unusual CONNECT behavior, and Node services suddenly talking to unfamiliar internet destinations. There is no mitigation SLA for this MEDIUM finding; treat this as detection engineering that reduces dwell time while you clean up package exposure.
  4. Gate CI on axios version floor — Add a policy check that rejects builds or lockfiles containing axios below 1.16.0, because library vulnerabilities like this persist through dependency drift more than through host drift. There is no mitigation SLA for this MEDIUM finding, but this is a cheap control that prevents reintroduction during the remediation window.
What doesn't work
  • WAF-only thinking doesn't help much; the dangerous part is the server-side runtime state after prototype pollution, not an obvious network signature at the edge.
  • Cleaning only HTTP_PROXY / HTTPS_PROXY environment variables is insufficient; this gadget abuses config.proxy inheritance, not just environment-based proxy configuration.
  • Perimeter internet scanning won't tell you who is exposed; axios is a dependency problem, so you need SBOM/SCA/runtime inventory, not Shodan screenshots.
06 · Verification

Crowdsourced verification payload.

Run this on a developer workstation, CI runner, or application host against a source tree or deployed app directory that contains package-lock.json, npm-shrinkwrap.json, node_modules, or package.json. Invoke it as python3 check_axios_cve_2026_44494.py /path/to/app; no elevated privileges are required unless the target directory is unreadable to your account.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_axios_cve_2026_44494.py
# Detects axios exposure to CVE-2026-44494.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/error

import json
import os
import re
import sys
from typing import List, Optional, Tuple

AFFECTED_MIN = (1, 0, 0)
PATCHED = (1, 16, 0)


def parse_semver(version: str) -> Optional[Tuple[int, int, int]]:
    if not version:
        return None
    v = version.strip()
    v = re.sub(r'^[^0-9]+', '', v)  # drop ^ ~ >= <= v etc.
    m = re.match(r'^(\d+)\.(\d+)\.(\d+)', v)
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def version_is_vulnerable(version: str) -> Optional[bool]:
    sv = parse_semver(version)
    if sv is None:
        return None
    return AFFECTED_MIN <= sv < PATCHED


def collect_from_package_lock(obj, results: List[Tuple[str, str]]):
    if isinstance(obj, dict):
        if obj.get('name') == 'axios' and 'version' in obj:
            results.append(('lockfile', str(obj['version'])))
        packages = obj.get('packages')
        if isinstance(packages, dict):
            for path, meta in packages.items():
                if path.endswith('node_modules/axios') and isinstance(meta, dict) and 'version' in meta:
                    results.append((f'lockfile:{path}', str(meta['version'])))
        deps = obj.get('dependencies')
        if isinstance(deps, dict):
            for name, meta in deps.items():
                if name == 'axios' and isinstance(meta, dict) and 'version' in meta:
                    results.append(('lockfile:dependencies.axios', str(meta['version'])))
                collect_from_package_lock(meta, results)


def collect_from_package_json(obj, results: List[Tuple[str, str]]):
    if not isinstance(obj, dict):
        return
    for section in ('dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'):
        deps = obj.get(section)
        if isinstance(deps, dict) and 'axios' in deps:
            results.append((f'package.json:{section}', str(deps['axios'])))


def read_json(path: str):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception:
        return None


def find_versions(root: str) -> List[Tuple[str, str]]:
    results: List[Tuple[str, str]] = []

    for name in ('package-lock.json', 'npm-shrinkwrap.json'):
        p = os.path.join(root, name)
        if os.path.isfile(p):
            data = read_json(p)
            if data is not None:
                collect_from_package_lock(data, results)

    p = os.path.join(root, 'package.json')
    if os.path.isfile(p):
        data = read_json(p)
        if data is not None:
            collect_from_package_json(data, results)

    p = os.path.join(root, 'node_modules', 'axios', 'package.json')
    if os.path.isfile(p):
        data = read_json(p)
        if isinstance(data, dict) and 'version' in data:
            results.append(('node_modules/axios/package.json', str(data['version'])))

    # Deduplicate while preserving order
    seen = set()
    deduped = []
    for item in results:
        if item not in seen:
            deduped.append(item)
            seen.add(item)
    return deduped


def main():
    if len(sys.argv) != 2:
        print('UNKNOWN - usage: python3 check_axios_cve_2026_44494.py /path/to/app')
        sys.exit(3)

    root = sys.argv[1]
    if not os.path.isdir(root):
        print(f'UNKNOWN - path not found or not a directory: {root}')
        sys.exit(3)

    findings = find_versions(root)
    if not findings:
        print('UNKNOWN - axios version not found in package-lock.json, npm-shrinkwrap.json, package.json, or node_modules')
        sys.exit(2)

    vulnerable = []
    patched = []
    unknown = []

    for source, version in findings:
        verdict = version_is_vulnerable(version)
        if verdict is True:
            vulnerable.append((source, version))
        elif verdict is False:
            patched.append((source, version))
        else:
            unknown.append((source, version))

    if vulnerable:
        details = '; '.join([f'{src}={ver}' for src, ver in vulnerable])
        print(f'VULNERABLE - axios version(s) below 1.16.0 detected: {details}')
        sys.exit(1)

    if patched:
        details = '; '.join([f'{src}={ver}' for src, ver in patched])
        if unknown:
            details += ' | unresolved ranges: ' + '; '.join([f'{src}={ver}' for src, ver in unknown])
        print(f'PATCHED - no installed axios version below 1.16.0 found: {details}')
        sys.exit(0)

    details = '; '.join([f'{src}={ver}' for src, ver in unknown])
    print(f'UNKNOWN - axios referenced but version range could not be resolved automatically: {details}')
    sys.exit(2)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, treat this as a dependency-chain cleanup item, not a fleet-wide fire drill. Run an SCA/SBOM sweep for axios <1.16.0, prioritize Node services that both accept attacker-controlled input and make high-value outbound API calls, and document that there is no noisgate mitigation SLA — go straight to the 365-day remediation window for this MEDIUM finding; the noisgate remediation SLA is ≤365 days for the actual upgrade to 1.16.0+. If you discover a reachable prototype-pollution source in the same service, reclassify that application locally and pull it forward.

Sources

  1. GitHub Advisory GHSA-35jp-ww65-95wh
  2. OSV record for GHSA-35jp-ww65-95wh / CVE-2026-44494
  3. Axios release v1.16.0
  4. CISA Known Exploited Vulnerabilities Catalog
  5. FIRST EPSS API documentation
  6. MDN prototype pollution overview
  7. GHunter paper on prototype pollution gadgets
  8. Snyk package versions for axios
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.