Que fait Analyse Forensique des Conteneurs Docker ?
Quand l'utiliser
- Lors de l'investigation d'un conteneur Docker ou d'un hôte conteneur compromis
- Pour analyser des images Docker malveillantes téléchargées depuis des registres
- Lors de la réponse à un incident impliquant des failles de sécurité dans des applications conteneurisées
- Lors de l'examen de tentatives d'évasion de conteneur ou d'escalade de privilèges
- Pour auditer les configurations de conteneurs et identifier les mauvaises configurations
Prérequis
- Accès à l'interface CLI Docker sur le poste d'investigation
- Accès au système de fichiers de l'hôte Docker (image forensique ou en direct)
- Compréhension du système de fichiers en couches Docker (overlay2, aufs)
dive,docker-explorer, oucontainer-diffpour l'analyse d'image- Connaissance de la configuration du démon Docker et de la sécurité du socket
- Trivy ou Grype pour l'analyse de vulnérabilités des images conteneur
Workflow
Étape 1 : Préserver l'état du conteneur et les preuves
# Lister tous les conteneurs (y compris arrêtés)
docker ps -a --no-trunc > /cas/cas-2024-001/docker/liste_conteneurs.txt
# Inspecter le conteneur compromis
ID_CONTENEUR="abc123def456"
docker inspect $ID_CONTENEUR > /cas/cas-2024-001/docker/inspection_conteneur.json
# Exporter le système de fichiers du conteneur sous forme d'archive tar (préserve l'état actuel)
docker export $ID_CONTENEUR > /cas/cas-2024-001/docker/export_conteneur.tar
# Créer une image à partir de l'état actuel du conteneur
docker commit $ID_CONTENEUR preuve-forensique:cas-2024-001
docker save preuve-forensique:cas-2024-001 > /cas/cas-2024-001/docker/image_conteneur.tar
# Capturer les logs du conteneur
docker logs $ID_CONTENEUR --timestamps > /cas/cas-2024-001/docker/logs_conteneur.txt 2>&1
# Capturer les processus en cours (si le conteneur est toujours en cours d'exécution)
docker top $ID_CONTENEUR > /cas/cas-2024-001/docker/processus_conteneur.txt
# Capturer les connexions réseau
docker exec $ID_CONTENEUR netstat -tlnp 2>/dev/null > /cas/cas-2024-001/docker/reseau_conteneur.txt
# Copier des fichiers spécifiques depuis le conteneur
docker cp $ID_CONTENEUR:/var/log/ /cas/cas-2024-001/docker/conteneur_var_log/
docker cp $ID_CONTENEUR:/tmp/ /cas/cas-2024-001/docker/conteneur_tmp/
docker cp $ID_CONTENEUR:/etc/passwd /cas/cas-2024-001/docker/motdepasse_conteneur
# Hacher toutes les preuves exportées
sha256sum /cas/cas-2024-001/docker/*.tar > /cas/cas-2024-001/docker/empreintes_preuves.txt
Étape 2 : Analyser les couches de l'image conteneur
# Installer dive pour l'analyse interactive des couches d'image
wget https://github.com/wagoodman/dive/releases/latest/download/dive_linux_amd64.deb
sudo dpkg -i dive_linux_amd64.deb
# Analyser les couches d'image de manière interactive
dive preuve-forensique:cas-2024-001
# Analyse non interactive des couches
dive preuve-forensique:cas-2024-001 --ci --json /cas/cas-2024-001/docker/analyse_dive.json
# Extraire et examiner chaque couche individuellement
mkdir -p /cas/cas-2024-001/docker/couches/
tar -xf /cas/cas-2024-001/docker/image_conteneur.tar -C /cas/cas-2024-001/docker/couches/
# Lister le manifeste de l'image et l'ordre des couches
cat /cas/cas-2024-001/docker/couches/manifest.json | python3 -m json.tool
# Examiner chaque couche pour détecter les modifications
for couche in /cas/cas-2024-001/docker/couches/*/layer.tar; do
echo "=== Couche : $(dirname $couche | xargs basename) ==="
tar -tf "$couche" | head -20
echo "..."
done
# Utiliser container-diff pour comparer avec l'image de base originale
# Installer container-diff
curl -LO https://storage.googleapis.com/container-diff/latest/container-diff-linux-amd64
chmod +x container-diff-linux-amd64
# Comparer l'image validée avec l'originale
./container-diff-linux-amd64 diff daemon://nginx:latest daemon://preuve-forensique:cas-2024-001 \
--type=file --type=apt --type=history --json \
> /cas/cas-2024-001/docker/diff_conteneur.json
Étape 3 : Examiner les artefacts de l'hôte Docker
# Répertoire de données Docker (par défaut : /var/lib/docker/)
RACINE_DOCKER="/mnt/preuves/var/lib/docker"
# Examiner les couches du système de fichiers overlay2
ls -la $RACINE_DOCKER/overlay2/
# Trouver le système de fichiers fusionné du conteneur
HACHAGE_CONTENEUR=$(docker inspect $ID_CONTENEUR --format '{{.GraphDriver.Data.MergedDir}}' 2>/dev/null)
# Ou manuellement à partir de l'image forensique :
# Chercher dans /var/lib/docker/containers/<id_conteneur>/config.v2.json
# Analyser les fichiers de configuration du conteneur
cat $RACINE_DOCKER/containers/$ID_CONTENEUR/config.v2.json | python3 -m json.tool \
> /cas/cas-2024-001/docker/config_conteneur.json
# Vérifier la configuration du démon Docker
cat /mnt/preuves/etc/docker/daemon.json 2>/dev/null > /cas/cas-2024-001/docker/config_daemon.json
# Examiner le journal des événements Docker
cat $RACINE_DOCKER/containers/$ID_CONTENEUR/*.log > /cas/cas-2024-001/docker/logs_json_conteneur.txt
# Vérifier les montages de volumes (accès potentiel au système de fichiers hôte)
python3 << 'PYEOF'
import json
with open('/cas/cas-2024-001/docker/inspection_conteneur.json') as f:
donnees = json.load(f)
inspection = donnees[0] if isinstance(donnees, list) else donnees
print("=== ANALYSE DE SÉCURITÉ DU CONTENEUR ===\n")
# Vérifier les montages
print("Montages de Volumes :")
for montage in inspection.get('Mounts', []):
rw = "LECTURE-ÉCRITURE" if montage.get('RW') else "LECTURE SEULE"
print(f" {montage.get('Source', 'N/A')} -> {montage.get('Destination', 'N/A')} ({rw})")
if montage.get('Source') in ('/', '/etc', '/var', '/root') and montage.get('RW'):
print(f" ATTENTION : Chemin sensible de l'hôte monté en lecture-écriture !")
# Vérifier le mode privilégié
config_hote = inspection.get('HostConfig', {})
if config_hote.get('Privileged'):
print("\nATTENTION : Le conteneur fonctionnait en mode PRIVILÉGIÉ !")
# Vérifier les capacités
caps_ajoutees = config_hote.get('CapAdd', [])
if caps_ajoutees:
print(f"\nCapacités ajoutées : {caps_ajoutees}")
caps_dangereuses = ['SYS_ADMIN', 'SYS_PTRACE', 'NET_ADMIN', 'SYS_MODULE']
for cap in caps_ajoutees:
if cap in caps_dangereuses:
print(f" ATTENTION : Capacité dangereuse : {cap}")
# Vérifier l'espace de noms PID
if config_hote.get('PidMode') == 'host':
print("\nATTENTION : Le conteneur partage l'espace de noms PID de l'hôte !")
# Vérifier le mode réseau
if config_hote.get('NetworkMode') == 'host':
print("\nATTENTION : Le conteneur partage l'espace de noms réseau de l'hôte !")
# Vérifier l'utilisateur
utilisateur = inspection.get('Config', {}).get('User', 'root (par défaut)')
print(f"\nExécuté sous l'utilisateur : {utilisateur}")
# Vérifier les variables d'environnement pour les secrets
vars_env = inspection.get('Config', {}).get('Env', [])
print(f"\nVariables d'environnement : {len(vars_env)}")
for env in vars_env:
cle = env.split('=')[0]
if any(s in cle.upper() for s in ['PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'CREDENTIAL']):
print(f" SENSIBLE : {cle}=***MASQUÉ***")
PYEOF
Étape 4 : Analyser les modifications du système de fichiers du conteneur
# Comparer le système de fichiers du conteneur avec l'image d'origine
docker diff $ID_CONTENEUR > /cas/cas-2024-001/docker/changements_fs.txt
# A = Ajouté, C = Modifié, D = Supprimé
# Analyser les changements
python3 << 'PYEOF'
ajoutes = []
modifies = []
supprimes = []
with open('/cas/cas-2024-001/docker/changements_fs.txt') as f:
for ligne in f:
ligne = ligne.strip()
if ligne.startswith('A '):
ajoutes.append(ligne[2:])
elif ligne.startswith('C '):
modifies.append(ligne[2:])
elif ligne.startswith('D '):
supprimes.append(ligne[2:])
print(f"Fichiers ajoutés : {len(ajoutes)}")
print(f"Fichiers modifiés : {len(modifies)}")
print(f"Fichiers supprimés : {len(supprimes)}")
# Signaler les ajouts suspects
suspects = [f for f in ajoutes if any(s in f for s in
['/tmp/', '/dev/shm/', '/root/', '.sh', '.py', '.elf', 'reverse', 'shell', 'backdoor', 'porte dérobée'])]
if suspects:
print(f"\nFichiers ajoutés suspects :")
for f in suspects:
print(f" {f}")
# Signaler les modifications suspectes
suspects_modifies = [f for f in modifies if any(s in f for s in
['/etc/passwd', '/etc/shadow', '/etc/crontab', '/etc/ssh', '.bashrc'])]
if suspects_modifies:
print(f"\nFichiers modifiés suspects :")
for f in suspects_modifies:
print(f" {f}")
PYEOF
# Extraire et examiner l'export du conteneur
mkdir -p /cas/cas-2024-001/docker/fs_conteneur/
tar -xf /cas/cas-2024-001/docker/export_conteneur.tar -C /cas/cas-2024-001/docker/fs_conteneur/
# Rechercher les webshells et fichiers malveillants
find /cas/cas-2024-001/docker/fs_conteneur/tmp/ -type f -exec file {} \;
find /cas/cas-2024-001/docker/fs_conteneur/ -name "*.php" -newer /cas/cas-2024-001/docker/fs_conteneur/etc/hostname
Étape 5 : Analyser les vulnérabilités et générer un rapport
# Analyser l'image pour les vulnérabilités connues
trivy image preuve-forensique:cas-2024-001 \
--format json \
--output /cas/cas-2024-001/docker/scan_vulnerabilites.json
# Analyser le système de fichiers exporté
trivy fs /cas/cas-2024-001/docker/fs_conteneur/ \
--format table \
--output /cas/cas-2024-001/docker/vulnerabilites_fs.txt
# Rechercher des secrets dans l'image
trivy image preuve-forensique:cas-2024-001 \
--scanners secret \
--format json \
--output /cas/cas-2024-001/docker/scan_secrets.json
Concepts Clés
| Concept | Description |
|---|---|
| Couches d'image | Couches de système de fichiers en lecture seule empilées pour former l'image conteneur |
| overlay2 | Pilote de stockage Docker par défaut utilisant un système de fichiers union pour les couches |
| Diff conteneur | Comparaison des modifications du système de fichiers d'exécution par rapport à l'image d'origine |
| Mode privilégié | Conteneur avec toutes les capacités de l'hôte (contourne la plupart des isolations) |
| Socket Docker | Socket Unix (/var/run/docker.sock) contrôlant le démon Docker |
| Évasion de conteneur | Technique pour sortir de l'isolation du conteneur vers l'hôte |
| Montages de volumes | Chemins du système de fichiers hôte rendus accessibles à l'intérieur du conteneur |
| Historique d'image | Enregistrement des instructions Dockerfile utilisées pour construire chaque couche |
Outils et Systèmes
| Outil | Objectif |
|---|---|
| docker inspect | Informations détaillées sur la configuration et l'état du conteneur |
| docker diff | Affiche les modifications du système de fichiers dans un conteneur en cours/exécution |
| dive | Outil interactif d'analyse des couches d'image Docker |
| container-diff | Outil Google pour comparer le contenu des images conteneur |
| Trivy | Analyseur de vulnérabilités pour les images conteneur et les systèmes de fichiers |
| docker-explorer | Outil forensique pour l'analyse hors ligne des artefacts Docker |
| Sysdig | Surveillance de la sécurité d'exécution et forensique des conteneurs |
| Falco | Détection des menaces en temps réel pour les conteneurs et Kubernetes |
Scénarios Courants
Scénario 1 : Compromission d'un conteneur d'application Web Exporter le système de fichiers du conteneur, identifier les webshells dans la racine web, analyser les journaux d'accès pour les tentatives d'exploitation, vérifier les fichiers ajoutés et les configurations modifiées, examiner les connexions réseau pour les communications C2, examiner les capacités du conteneur pour les voies d'escalade.
Scénario 2 : Attaque sur la chaîne d'approvisionnement via une image malveillante Analyser les couches d'image avec dive pour identifier quelle couche a ajouté du contenu malveillant, comparer avec l'image de base officielle à l'aide de container-diff, vérifier l'historique de l'image pour les commandes RUN suspectes, rechercher les portes dérobées et les mineurs de cryptomonnaie intégrés, tracer le téléchargement de l'image à partir des journaux du registre.
Scénario 3 : Enquête sur une évasion de conteneur Vérifier si le conteneur a été exécuté en mode privilégié ou avec des capacités dangereuses, examiner les points de montage du système de fichiers hôte pour un accès non autorisé, examiner le montage du socket Docker permettant l'abus Docker-in-Docker, analyser les journaux système de l'hôte pour les indicateurs d'évasion de conteneur, rechercher les artefacts d'exploitation du noyau.
Scénario 4 : Cryptojacking dans un environnement conteneurisé Identifier les conteneurs à forte consommation CPU, exporter et analyser l'image du conteneur pour les binaires de minage, vérifier les images non autorisées dans le registre, examiner les événements de création de conteneur pour les déploiements malveillants, examiner les connexions réseau pour les communications avec les pools de minage.
Format de Sortie
Résumé de l'Analyse Forensique Docker :
Conteneur : abc123def456 (nginx-app)
Image : company/web-app:v2.1
Statut : En cours d'exécution (démarré le 2024-01-10 09:00 UTC)
Hôte : docker-host-01.corp.local
Configuration de Sécurité :
Privilégié : Non
Capacités Ajoutées : NET_ADMIN (ATTENTION)
Montages de Volumes : /var/log -> /host-logs (RW)
Mode Réseau : bridge
Utilisateur : root (ATTENTION)
Modifications du Système de Fichiers :
Ajoutés : 23 fichiers (5 suspects)
Modifiés : 12 fichiers (2 suspects)
Supprimés : 0 fichiers
Résultats Suspects :
/tmp/reverse.sh - Script reverse shell (Ajouté)
/var/www/html/.hidden/shell.php - Webshell PHP (Ajouté)
/etc/crontab - Modifié (entrée cron de persistance ajoutée)
/root/.ssh/authorized_keys - Modifié (clé non autorisée ajoutée)
Analyse de Vulnérabilités :
Critique : 3 (CVE-2024-xxxx dans l'image de base)
Élevée : 12
Moyenne : 34
Preuves : /cas/cas-2024-001/docker/