FlawOpen

flawopen.com/Incidents/gitlab-commits-api-path-traversal-september-2026

● GitLab · CVE-2026-85706 · September 2026 · CVSS 10.0 · Crítica
Pesquisa · FlawOpen

CVE-2026-85706: exposição de arquivos no GitLab

No GitLab, o parser de uploads podia selecionar arquivos sem autenticação; escapes inválidos expunham trechos em erros, sem divulgar o arquivo.[1], [3]

Vetor CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N[3]
💡 Explicação em Linguagem Simples (ELI5)

Imagine um atendente que aceita a localização de um armário escrita por um visitante em vez do documento na bandeja de entrada. Antes de conferir o crachá, ele abre o armário; se uma linha for ilegível, repete a linha em voz alta ao explicar o erro. Abrir o documento é uma falha; repetir seu conteúdo é outra.[1]

Conceitos Centrais e Termos

Upload metadata
O caminho file.path ou metadata.path fornecido na requisição não deve substituir um buffer registrado pelo processamento confiável de uploads.[1]
Percent decoding
Na análise de formulários, % seguido de dois dígitos hexadecimais é decodificado; uma sequência inválida pode produzir um erro que inclui o trecho problemático.[1]
CWE-22 / CVSS
O CNA classifica o CVE-2026-85706 como travessia de caminho, CVSS 10.0 Crítico. O limite violado é a seleção de arquivos do servidor controlada pelo solicitante.[3]

Mapeamentos de frameworks

Linha do Tempo do Incidente

2026-09-10

O GitLab lançou 19.1.8, 19.2.6 e 19.3.2 com a correção de segurança.[2]

2026-09-23

O aviso de lançamento passou a incluir as correções retroativas 18.11.12 e 19.0.9.[2]

Fluxo de Ataque Passo a Paso

Passo 1

Seleção antes da autenticação

Os metadados do upload podiam substituir o buffer temporário esperado por um caminho do servidor antes da autenticação.[1]

Passo 2

Conteúdo tratado como parâmetros

Com application/x-www-form-urlencoded, o arquivo selecionado era analisado como dados de formulário, não devolvido como resposta normal do commit.[1]

Passo 3

Reflexo no erro

Um escape percentual inválido podia inserir o trecho correspondente no erro. Os delimitadores & e = afetam a extensão; uma leitura isolada não prova divulgação.[1]

Passo 4

Investigação

Associe os registros da API Rails e do Workhorse por correlation_id; compare api_error com written_bytes para avaliar se bytes foram enviados na resposta.[1]

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

Modelo ilustrativo em Python, não é código nem correção do GitLab. O exemplo vulnerável usa apenas buffers em memória e trata qualquer % como erro, sem reproduzir a validação percentual real nem a escolha exata do trecho. A versão corrigida autentica antes de processar, rejeita metadados de caminho, aceita apenas bytes de um upload confiável, limita o tamanho e devolve erros genéricos. Instale a correção do fabricante: autenticar dentro da rota seria tarde se um middleware anterior já tivesse lido o arquivo.[1]

IMPLEMENTAÇÃO VULNERÁVEL
from urllib.parse import unquote_to_bytes

def parse_upload(authenticated, metadata, buffers):
    # Fixtures inofensivos em memória; sem acesso ao sistema de arquivos.
    body = buffers.get(metadata.get("path"), b"")
    if b"%" in body:
        return 400, body.decode("ascii", errors="replace")
    if not authenticated:
        return 401, "Unauthorized"
    return 200, unquote_to_bytes(body)
PATCH SEGURO E ROBUSTO
from urllib.parse import unquote_to_bytes

MAX_BYTES = 4096
HEX = frozenset(b"0123456789abcdefABCDEF")

def parse_upload(authenticated, metadata, registered_body):
    # registered_body vem do middleware de upload confiável,
    # nunca de um caminho escolhido por parâmetros da requisição.
    if not authenticated:
        return 401, "Unauthorized"
    if metadata or type(registered_body) is not bytes:
        return 400, "Invalid upload"
    if len(registered_body) > MAX_BYTES:
        return 413, "Upload too large"
    for i, byte in enumerate(registered_body):
        if byte == 37 and (i + 2 >= len(registered_body) or
                registered_body[i+1] not in HEX or
                registered_body[i+2] not in HEX):
            return 400, "Invalid upload"
    return 200, unquote_to_bytes(registered_body)

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

Fontes