漏洞描述:

Axios 是一个基于 Promise 的 HTTP 客户端,可在浏览器和 Node.js 环境中运行,用于向后端 API 发送异步请求,支持请求/响应拦截、数据转换、自动 JSON 处理等特性,是目前前端开发中最流行的 HTTP 请求库。

Axios 1.15.0 版本之前存在一个SSRF漏洞,未授权的攻击者可以构造畸形的HTTP结构发送至非预期目标,导致敏感信息泄露。

影响版本:Axios < 1.15.0

漏洞前言:

原型污染(Prototype Pollution):攻击者通过输入数据修改了 JavaScript 对象的原型(Object.prototype),导致所有对象都继承了恶意属性。

利用条件:

  • 输入可控 攻击者能控制传给应用的参数(如 HTTP body、querystring、表单数据)。
  • 解析库使用 应用使用 JSON.parse()、qs.parse()、lodash.merge() 等库来处理用户输入。
  • 未过滤特殊键 应用没有对 __proto__、constructor 等特殊属性做过滤或清理。
  • Axios配置合并 应用使用 Axios,并且在请求时会合并配置对象(mergeConfig),从而继承污染后的属性。
  • Header注入可利用 攻击者注入的 header 值中包含 \r\n,能让 HTTP 请求被拆分成多段。
  • 代理或中间件存在 请求经过 Nginx、负载均衡或其他代理,允许请求走私生效。

漏洞效果:

请求走私导致ssrf,可造成由服务端发起的http请求
请求走私:攻击者可构造特殊请求,使前端转发一个请求,而后端将其拆分为多个请求,从而绕过安全控制。
例:

GET /test HTTP/1.1
Host: xxx
User-Agent: Mozilla/5.0
Accept: */*
X-Test: value\r\nGET /admin HTTP/1.1\r\nHost: localhost\r\n

攻击链流程图

┌───────────────┐
│ 攻击者输入数据 │
│ (query/body)  │
└───────┬───────┘
        │ 带有 __proto__ 字段
        ▼
┌───────────────┐
│ 解析库处理输入 │
│ JSON.parse /  │
│ qs.parse /    │
│ lodash.merge  │
└───────┬───────┘
        │ 未过滤 __proto__
        ▼
┌───────────────┐
│ 原型污染成功  │
│ Object.prototype│
│ 被注入 headers │
└───────┬───────┘
        │ 普通对象继承恶意属性
        ▼
┌───────────────┐
│ Axios 合并配置 │
│ mergeConfig    │
└───────┬───────┘
        │ header 中出现攻击者注入值代表污染成功
        ▼
┌───────────────┐
│ Header 注入   │
│ 值包含 \r\n   │
└───────┬───────┘
        │ 请求走私,请求被拆分成多段
        ▼
┌───────────────┐
│ 请求走私成功  │
│ 第一段正常    │
│ 第二段伪造    │
└───────────────┘

漏洞复现:

参考https://github.com/pjt3591oo/CVE-2026-40175-poc。只解析test-prototype-pollution.js脚本即可

一、构造payload

58-66行定义payload。71行使用JSON.parse解析payload,JSON.parse默认是安全的,这里__proto__只是一个普通字符串 key

step1和step2输出结果。payload将会在headers中注入一个X-Smuggle,值为一个http请求数据包,Host填自身,这里填的imds,也可以写localhost。请求这个路径是 模拟窃取IAM凭证。

二、原型污染

关键点在于使用vulnerableMerge函数,通过递归调用,将攻击者传入的属性直接写入到全局的 Object.prototype。这里是写入 Object.prototype.headers。

为什么这里target[key]也就是target["__proto__"] 返回的是 Object.prototype?

  • 在75行调用vulnerableMerge传入的target为{}
  • 对于普通对象 {},它的原型就是 Object.prototype。
  • 所以 ({})["__proto__"] == Object.prototype

为什么用这个函数?

为了演示污染原理,和axios合并配置一样,没有过滤 __proto__

Axios 合并配置缺陷

  • Axios 在执行请求时会调用 mergeConfig 合并默认配置和用户配置。
  • 由于它没有过滤原型链上的属性,如 __proto__ ,攻击者污染的属性就会被“继承”进来。
  • 例如:污染 headers,让所有请求都带上攻击者指定的头。

Axios 1.14.1中存在缺陷的mergeConfig逻辑(简化版):

function mergeConfig(defaultConfig, userConfig) {
  const config = {};
  
  // 合并默认配置
  for (const key in defaultConfig) {
    config[key] = defaultConfig[key];
  }
  
  // 合并用户配置
  for (const key in userConfig) {
    // ❌ 致命缺陷:没有检查key是否是对象自身的属性
    // 会遍历原型链上的所有属性
    config[key] = userConfig[key];
  }
  
  return config;
}

这意味着,只要Object.prototype被污染了任何与Axios配置相关的属性(如headers、method、url等),这些属性都会被自动应用到所有Axios请求中,无论这些请求是硬编码的还是动态生成的。

三、验证原型污染和请求走私

创建一个新的空对象 fresh, 如果原型污染成功,这个对象会继承被注入到 Object.prototype 的属性。

打印 fresh.headers。

  • 如果污染失败,这里是 undefined。
  • 如果污染成功,这里会显示攻击者注入的 headers 对象

step3输出结果,此时fresh.headers存在注入的X-Smuggle,原型污染成功。CRLF返回true代表 header 值里有 ,能够请求走私。

四、发起请求,CRLF注入导致HTTP请求走私

再次创建一个新的空对象 options,提取 options.headers(先前验证已经注入了X-Smuggle头) 赋值给taintedHeaders,这样它就变成了一个 普通对象的自有属性,不再依赖原型链 。接着 delete 清理掉全局污染,避免继续影响 Axios 内部逻辑, 关键点:如果不删除,全局污染会让 Axios 在合并配置时崩溃。

使用taintedHeaders作为header,adapter: rawSocketAdapter表示:HTTP 请求不走默认逻辑,而是自定义。

step4结果:可以看到taintedHeaders依旧存在X-Smuggle头,

五、请求响应

输出响应

服务端返回两个响应,第二个响应包存在:FAKE-IMDSv2-TOKEN-deadbeef1234, 说明伪造请求确实到达了模拟 IMDS 服务,并返回了假凭证。

进入容器内请求验证,响应内容一样,证明请求走私成功

参考链接:

https://github.com/pjt3591oo/CVE-2026-40175-poc

https://blog.csdn.net/weixin_42376192/article/details/160135877