Aller au contenu

Dépôt .git exposé : reconstruire les sources d'une application

Un dossier .git laissé accessible à la racine d’un site expose bien plus que le code en production. Il contient l’historique complet du dépôt, les branches, les commits et chaque version des fichiers. À partir de là, on reconstruit les sources entières de l’application, y compris ce qui n’était pas censé être publié : secrets, clés, fichiers de configuration supprimés dans un commit ultérieur.

Il ne faut jamais laisser le .git accessible, ni même l’embarquer dans une image ou une archive de déploiement. Sa présence expose la totalité du dépôt et permet d’en reconstruire le contenu.

Quelques éléments internes suffisent à comprendre la structure :

  • HEAD pointe sur la branche ou le commit courant. C’est le point d’entrée pour savoir où l’on se trouve dans l’historique.
  • refs/ et packed-refs listent les branches et les tags, avec le commit sur lequel chacun pointe. On voit ainsi tout ce qui a été livré.
  • objects/ contient les objets Git, la matière première. C’est lui qui permet de dumper l’intégralité des sources et de les reconstruire, pour ensuite naviguer entre branches et commits.

Un simple accès au fichier HEAD confirme la présence du dépôt :

Fenêtre de terminal
curl -s http://exemple.fr/.git/HEAD
# ref: refs/heads/master → le .git est exposé

Si le serveur autorise le listing de répertoire, http://exemple.fr/.git/ affiche directement l’arborescence. Le plus souvent, le listing est désactivé mais les fichiers restent accessibles un par un, ce que les outils suivants exploitent.

GitTools regroupe les scripts adaptés à ce scénario.

Fenêtre de terminal
git clone https://github.com/internetwache/GitTools.git

Le script Dumper récupère tout le dossier .git localement, en devinant et téléchargeant les fichiers internes un à un lorsque le listing est désactivé :

Fenêtre de terminal
./GitTools/Dumper/gitdumper.sh http://exemple.fr/.git/ ./dump

Le script Extractor rejoue chaque commit récupéré et reconstruit l’arborescence des fichiers à chaque état de l’historique :

Fenêtre de terminal
./GitTools/Extractor/extractor.sh ./dump ./commits

Chaque commit est déplié dans un sous-dossier de ./commits, ce qui permet de retrouver les sources et de comparer les états successifs. C’est souvent dans un commit intermédiaire, avant qu’un secret ne soit « retiré », qu’on trouve la donnée sensible.

Git ne stocke pas toujours ses objets un par un. Pour économiser de la place, il regroupe périodiquement de nombreux objets dans un packfile, ce qui donne un couple de fichiers dans objects/pack/ :

  • .pack contient les objets compressés et deltifiés ensemble. Plutôt que de stocker chaque version d’un fichier en entier, Git n’enregistre que les différences par rapport à une version de référence.
  • .idx est l’index du pack. Il associe chaque empreinte d’objet à sa position dans le .pack, pour que Git retrouve un objet sans parcourir tout le fichier.

Quand le dump ne ramène que ces fichiers, on reconstruit le dépôt à la main. On initialise un dépôt vide :

Fenêtre de terminal
git init

On déplace les fichiers téléchargés dans le sous-dossier interne que Git vient de créer :

Fenêtre de terminal
mv pack-89a81675f*.pack pack-89a81675f*.idx .git/objects/pack/

Puis on demande à Git de reconstruire l’arborescence complète à partir du pack :

Fenêtre de terminal
git fsck --full

git fsck vérifie la cohérence de la base d’objets et signale les objets accessibles, y compris les commits « pendants ». On peut ensuite jongler entre branches et commits avec git log, git checkout et git show pour extraire les sources.

Le dossier .git n’a rien à faire dans la racine servie. La correction de fond est de déployer sans lui, mais une défense en profondeur bloque son accès au niveau du serveur.

# Nginx
location ~ /\.git {
deny all;
return 404;
}
# Apache, .htaccess
RedirectMatch 404 /\.git

Le déploiement ne doit pas embarquer le dépôt. Exporter une arborescence sans historique élimine le problème à la source :

Fenêtre de terminal
# Extraire les fichiers suivis, sans le .git
git archive --format=tar HEAD | tar -x -C /var/www/monapp

Un .git exposé ne livre des secrets que s’il en contient. Garder les clés et mots de passe hors du dépôt (variables d’environnement, gestionnaire de secrets) et scanner l’historique limite l’impact d’une exposition.

Un .git accessible sur un serveur web équivaut à publier le dépôt entier, historique compris. HEAD et refs/ révèlent la structure, objects/ permet de tout reconstruire, y compris les fichiers supprimés dans un commit ultérieur. La reconstruction est automatisée par GitTools, et même réduit à un couple .pack / .idx, le dépôt se réassemble avec git fsck. La seule vraie parade est de ne jamais servir ce dossier.