flawopen.com/Teardowns/cve-2026-5027-langflow-multipart-file-write
Langflow CVE-2026-5027: 업로드 경로 순회 취약점
CVE-2026-5027은 경로 순회로 Langflow 업로드 영역 밖에 파일을 쓸 수 있었습니다. GTIG가 악용을 보고했습니다. 1.9.0 이상으로 업데이트하세요.[1], [2], [6]
우편실에서 발신자가 소포의 라벨과 목적지를 정할 수 있다고 생각해 보세요. 직원이 최종 경로를 확인하지 않고 라벨을 우편실 폴더에 붙이면 소포가 우편실 밖에 놓일 수 있습니다. Langflow의 업로드 경로는 multipart 파일명을 신뢰했고, 저장 계층은 최종 경로가 허용된 폴더 안에 있는지 별도로 확인하지 않았습니다.[2], [3]
핵심 개념 및 용어
CVE-2026-5027- Langflow POST /api/v2/files의 multipart 파일명 경로 순회에 Tenable이 할당한 식별자입니다.
CWE-22, CVSS 3.1 8.8이며 낮은 권한이 필요합니다.[1], [2]
프레임워크 매핑
- 이 가이드에서 설명하는 약점의 CWE 참조: CWE-22
근본 원인 분석 (Root Cause)
Tenable의 CVE-2026-5027 기록은 POST /api/v2/files가 경로 순회 요소가 포함된 multipart 파일명을 받아 의도한 디렉터리 밖에 쓰는 문제를 설명합니다. 보안 경계는 API 경로와 로컬 저장소에 걸쳐 있습니다. URL 매개변수를 검증해도 multipart Content-Disposition에서 읽은 파일명은 검증되지 않으며, 이름을 폴더에 결합하는 것만으로 경로 제한을 증명할 수 없습니다. CVE 벡터는 낮은 권한(PR:L)을 요구하므로 CVE 자체가 인증을 우회한다고 쓰지 마세요. Google GTIG는 CVE-2026-5027을 실제 공격에서 악용된 것으로 확인된 취약점에 포함하지만, 공개 요약이 보편적인 코드 실행을 입증하지는 않습니다. 임의 파일 쓰기의 더 큰 영향은 쓰기 가능한 경로, 프로세스 권한, 배포 설정에 달려 있습니다. 이를 보장된 RCE가 아닌 조건부 영향으로 다루세요. Langflow의 관련 GHSA는 CVE-2026-33309를 기재하며 역시 1.9.0 수정 버전을 안내합니다. 이 문서는 Tenable과 GTIG가 사용하는 CVE-2026-5027을 따르고 식별자 차이를 명시합니다. Langflow의 병합된 패치는 API 계층에서 이름을 검증하고 디렉터리 생성 전을 포함해 저장 계층에서 정규 경로 제한을 적용합니다.[1], [2], [3], [4], [5], [6]
단계별 공격 실행 흐름
소스 코드 비교: 취약한 구현 vs 보안 패치
소스 코드 형태를 따른 축약 예시이며 upstream의 문자 그대로의 diff는 아닙니다. 취약한 흐름은 클라이언트 파일명을 저장 경로로 사용합니다. 수정 패턴은 이름을 검증한 뒤 저장 계층에서 정규화한 대상이 정규화한 루트 아래에 있는지 확인합니다. upstream PR은 구분자와 제어 문자도 거부하고 디렉터리 생성 전에 제한을 확인합니다. HTTP 계층이 입력을 검사하더라도 저장 계층 검사를 유지하세요. 예시는 Path.is_relative_to를 지원하는 Python이 필요하며 공격자가 쓸 수 있는 디렉터리의 검사/사용 경쟁 조건을 단독으로 막지는 못합니다. 운영 환경에서는 경쟁 조건에 강한 파일 작업과 제한된 권한을 사용하세요. 핵심은 파일명 블랙리스트만이 아니라 경로 제한입니다.[3], [2], [4], [7]
소스 코드 발췌: Langflow api/v2/files.py 및 services/storage/local.py — 보안 경계 축약 예시
# 취약한 데이터 흐름을 단순화한 예입니다.
new_filename = file.filename
file_path = folder_path / new_filename
await write_upload(file, file_path)
from pathlib import Path
def confined_upload_path(root: Path, client_name: str | None) -> Path:
name = Path(client_name or "").name
if not name or name in {".", ".."} or chr(0) in name:
raise ValueError("invalid upload filename")
base = root.resolve()
target = (base / name).resolve()
if not target.is_relative_to(base):
raise ValueError("upload path escapes storage root")
return target
# 생성하거나 쓰기 직전 저장소 경계에서 호출합니다.
target = confined_upload_path(upload_root, file.filename)
await write_upload(file, target)
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓Langflow를 1.9.0 이상으로 업데이트하고 배포 이미지의 langflow 및 langflow-base 설치 버전을 확인하세요.[2], [4]
- ✓URL 매개변수와 별도로 multipart 파일명을 검증하고 구분자, 제어 문자, 빈 이름, 잘못된 인코딩을 거부하세요.[1], [2], [3]
- ✓저장 계층에서 대상을 정규화하고 디렉터리 생성과 파일 쓰기 전에 업로드 루트 아래에 포함되는지 확인하세요.[3], [4], [7]
- ✓순회, 절대 경로, 대체 구분자, 널 바이트, Unicode, 심볼릭 링크 대상을 테스트하고 임시 루트 밖에 파일이 생기지 않는지 확인하세요.[3], [7]
- ✓전용 최소 권한 서비스 계정으로 Langflow를 실행하고 업로드는 실행 불가로 유지하며 코드, 예약 작업 디렉터리, 자격 증명과 분리하세요.[2], [4]
- ✓인터넷 노출과 업로드 로그에서 루트 밖 이름을 확인하세요. 호스트 접근 증거가 있을 때만 조사하고 비밀을 교체하세요.[6], [2]
- ✓식별자 매핑을 명시하세요. Tenable/GTIG는 CVE-2026-5027을, Langflow 관련 GHSA는 CVE-2026-33309를 기재합니다. 기록 확인 없이 심각도나 버전을 합치지 마세요.[1], [2], [4], [5], [6]