flawopen.com/Incidents/gitlab-commits-api-path-traversal-september-2026
CVE-2026-85706 : divulgation de fichiers GitLab
Le parseur GitLab pouvait sélectionner des fichiers serveur sans authentification ; des échappements invalides révélaient des fragments dans l’erreur.[1], [3]
Imaginez un agent d’accueil qui accepte le numéro d’une armoire écrit par un visiteur au lieu du document posé dans le bac prévu. Avant de vérifier son badge, il ouvre cette armoire ; si une ligne est illisible, il la répète à voix haute dans son message d’erreur. Ouvrir le document est une première faute, en réciter le contenu une seconde.[1]
Concepts Clés et Termes
Upload metadata- Les chemins file.path ou metadata.path fournis par la requête ne doivent pas remplacer un tampon enregistré par le système de téléversement fiable.[1]
Percent decoding- Dans un formulaire, % suivi de deux chiffres hexadécimaux est décodé ; une séquence invalide peut provoquer une erreur contenant le fragment concerné.[1]
CWE-22 / CVSS- Le CNA classe
CVE-2026-85706comme traversée de chemin, CVSS 10.0 Critique. La frontière franchie est la sélection d’un fichier serveur par le client.[3]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-22
- CISA KEV — ajouté le 2026-09-11 — CVE-2026-85706
Chronologie de l'Incident
GitLab a publié les correctifs 19.1.8, 19.2.6 et 19.3.2.[2]
L’avis a été complété par les rétroportages 18.11.12 et 19.0.9.[2]
Déroulement de l'Attaque Étape par Étape
Sélection avant authentification
Les métadonnées du téléversement pouvaient remplacer le tampon temporaire attendu par un chemin serveur avant le contrôle d’identité.[1]
Fichier traité comme formulaire
Avec application/x-www-form-urlencoded, le contenu sélectionné devenait des paramètres de formulaire, et non une réponse normale du commit.[1]
Fragment repris dans l’erreur
Un échappement pourcentage invalide pouvait insérer le fragment fautif dans une erreur. Les séparateurs & et = en limitent l’étendue ; lire ne signifie pas divulguer.[1]
Enquête
Reliez les entrées de l’API Rails et de Workhorse avec correlation_id ; comparez api_error à written_bytes pour déterminer si des octets ont été envoyés dans la réponse.[1]
Code Source : Vulnérable vs Sécurisé
Modèle pédagogique en Python, ni code source ni correctif GitLab. L’exemple vulnérable utilise uniquement des tampons en mémoire et traite tout % comme une erreur ; il ne reproduit ni la validation réelle ni le découpage exact des fragments. Le modèle corrigé authentifie en premier, refuse les chemins fournis, accepte seulement les octets du système de téléversement fiable, borne leur taille et rend des erreurs génériques. Appliquez le correctif du fournisseur : un contrôle dans la route arrive trop tard si un intermédiaire a déjà lu le fichier.[1]
from urllib.parse import unquote_to_bytes
def parse_upload(authenticated, metadata, buffers):
# Fixtures inoffensifs en mémoire ; aucun accès au système de fichiers.
body = buffers.get(metadata.get("path"), b"")
if b"%" in body:
return 400, body.decode("ascii", errors="replace")
if not authenticated:
return 401, "Unauthorized"
return 200, unquote_to_bytes(body)
from urllib.parse import unquote_to_bytes
MAX_BYTES = 4096
HEX = frozenset(b"0123456789abcdefABCDEF")
def parse_upload(authenticated, metadata, registered_body):
# registered_body provient du middleware de téléversement de confiance,
# jamais d’un chemin choisi par les paramètres de la requête.
if not authenticated:
return 401, "Unauthorized"
if metadata or type(registered_body) is not bytes:
return 400, "Invalid upload"
if len(registered_body) > MAX_BYTES:
return 413, "Upload too large"
for i, byte in enumerate(registered_body):
if byte == 37 and (i + 2 >= len(registered_body) or
registered_body[i+1] not in HEX or
registered_body[i+2] not in HEX):
return 400, "Invalid upload"
return 200, unquote_to_bytes(registered_body)
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Mettez à jour vers une version corrigée et prise en charge. Seuils par branche : 18.11.12, 19.0.9, 19.1.8, 19.2.6 et 19.3.2 ; les versions touchées commencent à 18.7. Vérifiez votre version dans l’avis officiel.[2], [3]
- ✓Testez des requêtes anonymes avec des marqueurs inoffensifs en mémoire : aucune recherche de tampon avant authentification et aucun marqueur dans les réponses ou journaux ordinaires. Rejetez les chemins avant l’analyse.[1]
- ✓Conservez les journaux de l’API Rails et de Workhorse ; reliez les entrées avec
correlation_id, puis comparezapi_erroretwritten_bytes. Examinez les valeurs décodées de file.path et metadata.path au lieu d’écarter une ligne entière à cause d’un chemin de téléversement légitime. Une lecture ne prouve pas une divulgation : selon GitLab, un fragment reflété dans api_error, corroboré par written_bytes de Workhorse, indique des octets renvoyés ; le statut HTTP seul ne tranche pas.[1] - ✓Limitez la rotation aux identifiants présents dans une divulgation confirmée ou raisonnablement suspectée. Si
db_key_baseouotp_key_basepeut être concerné, consultez le support GitLab avant toute rotation : GitLab indique qu’il n’existe actuellement aucune procédure de rotation prise en charge pourdb_key_baseet avertit qu’une rotation non prise en charge peut entraîner une perte de données. L’absence de journaux ne prouve pas l’absence de divulgation.[1]