FlawOpen

flawopen.com/Teardowns/cve-2026-5027-langflow-multipart-file-write

● CVE-2026-5027 · CVSS 8.8[1], [2] · 높음
보안 연구 · FlawOpen

Langflow CVE-2026-5027: 업로드 경로 순회 취약점

CVE-2026-5027은 경로 순회로 Langflow 업로드 영역 밖에 파일을 쓸 수 있었습니다. GTIG가 악용을 보고했습니다. 1.9.0 이상으로 업데이트하세요.[1], [2], [6]

CVSS 벡터CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H[1], [2]
💡 알기 쉬운 설명 (ELI5)

우편실에서 발신자가 소포의 라벨과 목적지를 정할 수 있다고 생각해 보세요. 직원이 최종 경로를 확인하지 않고 라벨을 우편실 폴더에 붙이면 소포가 우편실 밖에 놓일 수 있습니다. Langflow의 업로드 경로는 multipart 파일명을 신뢰했고, 저장 계층은 최종 경로가 허용된 폴더 안에 있는지 별도로 확인하지 않았습니다.[2], [3]

핵심 개념 및 용어

CVE-2026-5027
Langflow POST /api/v2/files의 multipart 파일명 경로 순회에 Tenable이 할당한 식별자입니다. CWE-22, CVSS 3.1 8.8이며 낮은 권한이 필요합니다.[1], [2]
Multipart 파일명
multipart 업로드의 Content-Disposition에서 클라이언트가 제공하는 이름입니다. 신뢰할 수 없는 입력이며 안전한 파일 경로가 아닙니다.[1], [2]
경로 제한
파일 생성 또는 쓰기 전에 후보 경로를 정규화해 허용된 저장 디렉터리 아래에 남는지 입증하는 검사입니다.[3], [4], [7]
식별자 참고
Langflow의 관련 GHSA는 CVE-2026-33309를 기재하고 Tenable과 Google GTIG는 CVE-2026-5027을 사용합니다. 이 문서는 GTIG 식별자를 따르며 메타데이터를 임의로 합치지 않고 두 기록을 출처에 남깁니다.[4], [5], [6]

프레임워크 매핑

  • 이 가이드에서 설명하는 약점의 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]

단계별 공격 실행 흐름

단계 1

낮은 권한의 사용자가 업로드 API에 접근

CVE-2026-5027의 CVSS 벡터는 낮은 권한을 요구합니다. 영향 경로는 POST /api/v2/files이며 기록은 인증 우회를 입증하지 않습니다.[1], [2]

단계 2

요청에 multipart 파일명이 포함

파일명은 클라이언트가 제어하는 Content-Disposition 메타데이터에서 옵니다. URL 매개변수 검증은 이 별도 입력을 정화하지 않습니다.[1], [2], [4]

단계 3

경로가 저장 경계를 넘어감

정규 경로 제한 검사가 없으면 순회 요소가 업로드 폴더 밖으로 해석될 수 있습니다.[1], [2], [3], [7]

단계 4

루트 밖에 파일이 기록됨

문서화된 영향은 임의 파일 쓰기입니다. 코드 실행은 쓰기 가능한 경로, 프로세스 권한, 배포 구성에 달려 있으며 모든 업로드가 RCE로 이어지는 것은 아닙니다.[1], [2], [6]

단계 5

두 검증 경계를 모두 수정

Langflow는 API 파일명 검증과 저장 계층의 정규 경로 제한을 1.9.0 릴리스 라인에 병합했습니다. 업데이트하고 설치된 패키지와 실행 이미지를 확인하세요.[2], [3], [4]

소스 코드 비교: 취약한 구현 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)

엔지니어링 및 시스템 보안 강화 체크리스트

출처