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 堅牢化

これはソース構造に沿った簡略例で、上流の逐語的な差分ではありません。脆弱な処理はクライアント名を保存パスとして扱います。修正パターンは名前を検証し、ストレージ層で正規化した宛先が正規化したルート配下にあるか確認します。上流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)

エンジニアリング&システム堅牢化チェックリスト

参考資料