FlawOpen

flawopen.com/Teardowns/cve-2026-105699-langflow-mcp-cross-project-file-disclosure

● CVE-2026-105699 · CVSS v4 7.1 · Alta
Pesquisa · FlawOpen

CVE-2026-105699: Arquivos MCP no Langflow

O Langflow autenticava a conexão MCP, mas não autorizava cada arquivo solicitado. Um usuário podia ler arquivos de fluxos alheios; a versão 1.9.1 corrige a checagem de propriedade.[1], [2], [5]

Vetor CVSSCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
💡 Explicação em Linguagem Simples (ELI5)

Imagine um hotel que confere se você tem uma chave, mas entrega a bagagem de qualquer quarto quando você informa a etiqueta. A chave prova que você pode entrar no hotel, não que a mala de outro hóspede é sua. A leitura MCP do Langflow precisa confirmar o proprietário e o projeto antes de abrir o arquivo.[1]

Conceitos Centrais e Termos

Model Context Protocol (MCP)
Protocolo pelo qual um aplicativo de IA solicita ferramentas e recursos a um servidor. O servidor ainda precisa autorizar cada objeto solicitado.[1]
Endpoint MCP vinculado a projeto
Conexão MCP do Langflow associada a um projeto. Acesso ao endpoint não dá acesso a arquivos de todos os projetos.[1]
resources/read
Operação MCP que solicita ao servidor um recurso identificado por URI.[1]
Autorização por objeto
Verificação de que o usuário autenticado pode acessar o registro ou arquivo específico, e não apenas o endpoint.[1], [6]
CWE-639
Desvio de autorização por chave controlada pelo usuário: mudar um identificador permite alcançar o registro de outra pessoa.[6], [2]

Mapeamentos de frameworks

  • Referência CWE para a fraqueza descrita neste guia: CWE-639

Análise de Causa Raiz

Nas versões afetadas do Langflow, de 1.6.8 a 1.9.0, o transporte MCP vinculado a projeto autenticava o acesso ao ID do projeto na URL da conexão. Depois, o handler resources/read recebia uma URI, extraía um ID de fluxo e um nome de arquivo e os passava a storage_service.get_file() sem confirmar que o fluxo pertencia ao usuário autenticado ou ao projeto atual. A camada de armazenamento lê por namespace e nome; ela não conhece as permissões de usuário e projeto do Langflow. Assim, um usuário com conta e acesso ao endpoint MCP do próprio projeto podia solicitar um arquivo no namespace de fluxo de outra pessoa. O comportamento global separado de resources/list e tools/list podia revelar IDs e facilitar a escolha do alvo, mas não era necessário para a leitura. É uma falha autenticada de confidencialidade entre usuários, não acesso anônimo nem execução de código. O registro CVE atribui CVSS 4.0 7.1 High e CWE-639. O Langflow 1.9.1 corrige o caminho: handle_read_resource() obtém o usuário autenticado e resolve o fluxo exigindo Flow.user_id == current_user.id e, em transporte de projeto, Flow.folder_id == project_id; um namespace não autorizado é rejeitado antes do acesso ao armazenamento. O patch também rejeita separadores e sequências de traversal, verifica contenção nos caminhos de leitura/gravação/exclusão e limita listagens globais ao usuário atual. O teste de regressão do mantenedor nega a leitura do fluxo de outra pessoa.[1], [2], [3], [5]

Fluxo de Ataque Passo a Paso

Passo 1

O usuário autentica no próprio endpoint MCP de projeto

O atacante precisa de uma conta Langflow e acesso a pelo menos uma conexão MCP vinculada a projeto. A falha não contorna o login.[1]

Passo 2

O cliente solicita uma URI de recurso

A chamada resources/read inclui uma URI cujos últimos componentes identificam um namespace de fluxo e um arquivo.[1]

Passo 3

O handler vulnerável confia nos identificadores

handle_read_resource() extraía o ID e o nome, mas não vinculava o fluxo ao usuário nem ao projeto do endpoint.[1], [3]

Passo 4

O armazenamento lê pelo namespace

storage_service.get_file() retornava o arquivo do namespace informado. O armazenamento não tinha contexto de propriedade do usuário.[1]

Passo 5

A resposta cruza a fronteira entre usuários

O solicitante podia receber um arquivo de fluxo de outra pessoa. Listagens globais podiam revelar identificadores, mas não eram necessárias para explorar a falta de autorização.[1], [2]

Código-Fonte: Vulnerável vs. Seguro

Estes são trechos focados em Python do caminho documentado, não um aplicativo independente. O caminho vulnerável tratava o namespace informado pelo cliente como autoridade suficiente. A correção da versão 1.9.1 resolve primeiro o fluxo dentro do usuário autenticado e, em conexões MCP de projeto, do projeto atual; só então o armazenamento pode lê-lo. O patch também valida nomes de arquivo e impõe contenção de caminho na camada de armazenamento para que outros pontos de leitura/gravação/exclusão não contornem a regra. Mantenha a autorização no serviço que conhece o usuário e o projeto; um adaptador de armazenamento não consegue inferir a propriedade. Escopo do trecho: o handler real também permite arquivo do bucket do usuário somente quando o namespace corresponde ao ID do próprio usuário autenticado e o servidor não tem escopo de projeto. O trecho omite esse ramo separado; ele não deve ser interpretado como acesso a qualquer namespace. A suíte de regressão upstream inclui test_handle_read_resource_denies_other_users_flow, que verifica a negação da leitura entre usuários.[1], [3], [4], [7]

Trecho de origem: mcp_utils.py — trechos selecionados do código

IMPLEMENTAÇÃO VULNERÁVEL
# Trecho do caminho de leitura afetado: os IDs analisados eram enviados diretamente ao armazenamento.
content = await storage_service.get_file(
    flow_id=flow_id,
    file_name=filename,
)
PATCH SEGURO E ROBUSTO
# Redução, seguindo a estrutura da fonte, do limite de autorização da versão 1.9.1.
current_user = current_user_ctx.get()
query = select(Flow).where(
    Flow.id == namespace_id,
    Flow.user_id == current_user.id,
)
if project_id is not None:
    query = query.where(Flow.folder_id == project_id)
flow = (await session.exec(query)).first()
if flow is None:
    raise ValueError("Resource not found or access denied")
content = await storage_service.get_file(
    flow_id=namespace_id,
    file_name=filename,
)

Lista de Verificação de Segurança para Engenharia

Fontes