RC RANDOM CHAOS

axios CVE-2025-3891: What the Advisories Don't Say About Immutable Images

CVE-2025-3891 in axios allows prototype pollution leading to RCE. This post reveals why deployed container images remain at risk even after patching, due to missing artifact provenance and immutable verification.

· 4 min read

axios prototype pollution RCE - what the advisory skips about containers already running in production

CVE-2025-3891 - UNVERIFIED. Multiple sources reference a critical prototype pollution vector in the axios merge() function affecting versions prior to 1.7.0. CVSS score reported as 9.8. NVD entry and axios maintainer confirmation pending at time of publication. The operational analysis below applies regardless of CVE assignment.

The vulnerability

axios versions through 1.6.x used a recursive merge() function to combine configuration objects. The function did not filter prototype-polluting keys. A JSON body containing __proto__ as a key would modify the prototype chain of the target object during merge.

The primitive: prototype pollution via unvalidated recursive property assignment.

In any Node.js service that merges untrusted input into axios.defaults or a shared config object, an attacker-controlled JSON body like:

{
 "__proto__": {
 "shell": "/bin/sh",
 "argv0": "node",
 "env": { "NODE_OPTIONS": "--require=/tmp/payload.js" }
 }
}

could pollute Object.prototype, affecting every object in the process. If any downstream code path spawns a child process using default options - child_process.exec, child_process.spawn with shell: true - the polluted prototype supplies the execution context. This is not theoretical. The gadget chain from prototype pollution to RCE via child_process is documented and has been exploited in the wild.

The patch in 1.7.0 rejects __proto__, constructor, and prototype keys during recursive merge and uses Object.create(null) as the merge target.

The advisory says update. That is insufficient.

Every container image built with axios <=1.6.x baked the vulnerable code into an immutable artifact. Updating package.json in your repo does not remediate images already deployed.

A typical production environment runs 100+ container images. Unless every image was rebuilt after the patch landed in the dependency tree, vulnerable instances persist behind load balancers, serving traffic.

This is not a patching problem. It is a provenance problem.

Why most teams have no visibility

  • Container registries store images. They do not store build-time dependency manifests.
  • Without artifact signing and provenance metadata attached during CI/CD, there is no way to verify what was baked into an image at build time.
  • SBOMs generated at scan time reflect the scanner’s database, not the image’s actual contents. Timing gaps between build, scan, and deployment create blind spots.
  • Vulnerability scanners that check package versions at runtime miss statically bundled code where node_modules was copied into the image layer.

ATT&CK mapping

  • T1190 - Exploit Public-Facing Application. The prototype pollution is triggered via crafted HTTP request body to any endpoint that merges input into axios config.
  • T1059.007 - Command and Scripting Interpreter: JavaScript. Execution occurs within the Node.js runtime via polluted child_process defaults.
  • T1195.002 - Compromise Software Supply Chain. Applies if the vulnerable version was served from a compromised registry publish rather than a latent code defect.

Detection

Prototype pollution RCE in Node.js is difficult to detect because execution occurs within the legitimate application process.

What to look for:

  • Sysmon Event ID 1 (Process Creation): parent process node or container entrypoint spawning unexpected children - sh, bash, curl, wget, nc. Process lineage of entrypoint → node → sh in a containerized service is the anomaly.
  • Sysmon Event ID 3 (Network Connection): outbound connections from the node process to IPs/ports not in the application’s expected communication pattern. C2 callback after prototype pollution RCE typically uses a direct TCP connection or HTTP GET to an attacker-controlled endpoint.
  • Falco/auditd: unexpected execve syscalls from the container’s PID namespace. Rule: alert on any execve where the process parent is the node runtime and the child binary is not in an allowlist.
  • Application-layer: monitor for JSON request bodies containing __proto__, constructor.prototype, or Object.prototype keys. WAF rules should block these at ingress.

The exploit produces no new binaries on disk, no registry modifications, no PowerShell, no WMI. It looks like normal application behavior until you inspect process lineage.

Remediation

  1. Rebuild every production image from current source with patched dependencies. Do not assume images auto-update.
  2. Audit running containers for axios version. docker exec <container> node -e "console.log(require('axios').VERSION)" on every running instance. Automate across the fleet.
  3. Implement SBOM generation in CI/CD. Attach SBOMs to images in the registry. Tools: Syft, Trivy, or npm sbom.
  4. Enforce image provenance. Sign images at build time (cosign/sigstore). Reject unsigned images at the admission controller.
  5. Deploy WAF rules blocking __proto__ and constructor in JSON request bodies at the ingress layer.
  6. Add Falco rules for unexpected child process spawning from Node.js processes in production containers.

Root cause

The root cause is not axios. Prototype pollution in recursive merge functions has been a known class of vulnerability in JavaScript for over a decade.

The operational root cause is that most organizations treat dependency patching as a source-code activity. It is not. It is an artifact lifecycle activity. Until every deployed artifact can be traced to its build-time dependency manifest and verified against known patch timelines, every vulnerability window leaves a residue of unpatched images running in production.

The advisory tells you to update a package. It does not tell you to audit every artifact built before the patch existed. That gap is where the real exposure lives.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.