Module 4: Scripts Bomba Lab - Détecter les Erreurs Mortelles Cachées
🎯 Objectif de ce Module
À la fin: Vous serez un détecteur humain de scripts dangereux, capable d'identifier les erreurs mortelles avant qu'elles explosent en production.
💣 Philosophie du Module: Apprendre du Désastre AVANT qu'il Arrive
Pourquoi ce Module Existe
📊 STATISTIQUES RÉELLES:
- 73% des pannes AD = erreurs humaines de script
- 89% des scripts dangereux = apparence légitimes
- 95% des désastres = évitables avec validation
- 100% des admins = ont une histoire d'horreur
L'Art de la Détection
Un script bomba parfait :
- ✅ Semble légitime à première vue
- ✅ Commentaires rassurants
- ✅ Vient de source "fiable"
- ☠️ Contient 1-2 erreurs mortelles cachées
- ☠️ Explose seulement en production
🔬 Anatomie d'un Script Bomba
Les 5 Techniques de Camouflage
1. Commentaires Trompeurs
# Script validé par l'équipe sécurité ✅
# Testé sur environnement de dev ✅
# Approuvé pour production ✅
Get-ADUser -Filter * | Remove-ADUser -Confirm:$false # ☠️
2. Erreur Subtile dans une Ligne Innocente
# Script parfaitement normal...
$users = Get-ADUser -Filter {Department -eq "Stagiaires"}
# 50 lignes de code propre...
$users | Set-ADUser -Enabled $fase # ☠️ $false écrit $fase
3. TODO Rassurant
# TODO: Tester sur environnement de dev d'abord
# TODO: Valider avec l'équipe avant prod
Remove-ADUser -Identity * -Confirm:$false # ☠️ Mais exécuté quand même 😱
4. Apparence Professionnelle
<#
.SYNOPSIS
Script de maintenance AD approuvé
.DESCRIPTION
Nettoyage automatisé des comptes inactifs
.AUTHOR
Équipe Infrastructure Senior
.VERSION
2.1.5 - Production Ready
#>
# Code qui semble parfait mais contient erreur fatale...
5. Erreur de Logic Business
# Désactiver les comptes des employés qui partent
Get-ADUser -Filter {Department -eq "Départs"} | # ☠️ Département qui n'existe pas
Set-ADUser -Enabled $false
# Résultat: Aucun utilisateur trouvé, script "réussi" mais rien fait
🎪 Lab Pratique: Détecter les Bombes
🚨 SCRIPT BOMBA #1: Le Nettoyeur Innocent
# Script de nettoyage des groupes vides
# Auteur: Administrateur Principal
# Date: 2024-11-28
# Objectif: Supprimer les groupes qui ne contiennent aucun membre
Import-Module ActiveDirectory
Write-Host "=== NETTOYAGE DES GROUPES VIDES ===" -ForegroundColor Green
Write-Host "Recherche des groupes sans membres..." -ForegroundColor Yellow
# Récupérer tous les groupes du domaine maxtec.be
$tousLesGroupes = Get-ADGroup -Filter * -SearchBase "DC=maxtec,DC=be"
Write-Host "Total groupes trouvés: $($tousLesGroupes.Count)" -ForegroundColor Cyan
$groupesVides = @()
# Vérifier chaque groupe pour voir s'il a des membres
foreach ($groupe in $tousLesGroupes) {
try {
$membres = Get-ADGroupMember -Identity $groupe.SamAccountName -ErrorAction SilentlyContinue
if ($membres.Count -eq 0) {
$groupesVides += $groupe
Write-Host "Groupe vide trouvé: $($groupe.Name)" -ForegroundColor Yellow
}
} catch {
# Ignorer les erreurs de lecture
continue
}
}
Write-Host "`nGroupes vides trouvés: $($groupesVides.Count)" -ForegroundColor Yellow
if ($groupesVides.Count -gt 0) {
Write-Host "Suppression des groupes vides..." -ForegroundColor Red
foreach ($groupeVide in $groupesVides) {
Write-Host "Suppression: $($groupeVide.Name)" -ForegroundColor Red
# TODO: Ajouter validation supplémentaire avant production
Remove-ADGroup -Identity $groupeVide.SamAccountName -Confirm:$false
}
Write-Host "✅ Nettoyage terminé avec succès!" -ForegroundColor Green
} else {
Write-Host "Aucun groupe vide trouvé." -ForegroundColor Green
}
🕵️ MISSION DÉTECTIVE 4.1
Temps limite: 10 minutes
Consignes: 1. Lisez ce script ligne par ligne 2. Identifiez TOUS les problèmes (minimum 5) 3. Évaluez le risque si exécuté sur maxtec.be 4. Proposez 3 améliorations critiques
💡 SOLUTION DÉTAILLÉE
🚨 ERREURS CRITIQUES IDENTIFIÉES:
-
ABSENCE DE -WhatIf (MORTEL)
Risque: Suppression immédiate et irréversibleRemove-ADGroup -Identity $groupeVide.SamAccountName -Confirm:$false # ☠️ -
LOGIQUE BUSINESS ERRONÉE (GRAVE)
- Groupe vide ≠ Groupe inutile
- Groupes système peuvent être vides temporairement
- Groupes de sécurité peuvent être vides par design
-
AUCUNE EXCLUSION (MORTEL)
Risque: Inclut groupes système critiques (Admins du domaine vide = supprimé)$tousLesGroupes = Get-ADGroup -Filter * -SearchBase "DC=maxtec,DC=be" # ☠️ -
PAS DE VÉRIFICATION TYPE GROUPE (GRAVE)
- Pas de distinction Distribution vs Sécurité
- Pas de vérification des groupes built-in
-
GESTION D'ERREURS INSUFFISANTE (MOYEN)
} catch { continue # ☠️ Masque les erreurs importantes } -
ABSENCE DE LOGGING (MOYEN)
- Aucune trace de ce qui a été supprimé
- Impossible de rollback
🛠️ VERSION SÉCURISÉE
# Script de nettoyage des groupes vides - VERSION SÉCURISÉE
param(
[switch]$WhatIf = $true, # -WhatIf par défaut !
[switch]$IncludeBuiltIn = $false
)
Import-Module ActiveDirectory
Write-Host "=== NETTOYAGE SÉCURISÉ DES GROUPES VIDES ===" -ForegroundColor Cyan
Write-Host "Mode: $($WhatIf ? 'SIMULATION' : 'RÉEL')" -ForegroundColor $(if($WhatIf){'Yellow'}else{'Red'})
# Groupes à JAMAIS supprimer (whitelist sécurité)
$groupesCritiques = @(
"Admins du domaine", "Enterprise Admins", "Schema Admins",
"Account Operators", "Backup Operators", "Server Operators",
"Print Operators", "Replicator", "Domain Users", "Domain Computers",
"Domain Controllers", "Cert Publishers", "Domain Guests"
)
# SearchBase spécifique pour éviter les groupes système
$searchBase = if ($IncludeBuiltIn) { "DC=maxtec,DC=be" } else { "OU=EU,DC=maxtec,DC=be" }
try {
# Récupérer groupes avec propriétés nécessaires
$tousLesGroupes = Get-ADGroup -Filter * -SearchBase $searchBase -Properties GroupCategory, GroupScope
Write-Host "Groupes analysés dans: $searchBase" -ForegroundColor Cyan
Write-Host "Total groupes trouvés: $($tousLesGroupes.Count)" -ForegroundColor Cyan
$groupesCandidats = @()
foreach ($groupe in $tousLesGroupes) {
# Vérifier si groupe critique
if ($groupe.Name -in $groupesCritiques) {
Write-Host "SKIP: Groupe critique protégé - $($groupe.Name)" -ForegroundColor Green
continue
}
try {
$membres = Get-ADGroupMember -Identity $groupe.SamAccountName -ErrorAction Stop
if ($membres.Count -eq 0) {
$groupesCandidats += [PSCustomObject]@{
Name = $groupe.Name
SamAccountName = $groupe.SamAccountName
GroupCategory = $groupe.GroupCategory
GroupScope = $groupe.GroupScope
DistinguishedName = $groupe.DistinguishedName
}
Write-Host "Candidat suppression: $($groupe.Name) ($($groupe.GroupCategory))" -ForegroundColor Yellow
}
} catch {
Write-Warning "Erreur lecture groupe $($groupe.Name): $($_.Exception.Message)"
}
}
if ($groupesCandidats.Count -eq 0) {
Write-Host "✅ Aucun groupe vide trouvé (hors groupes critiques)" -ForegroundColor Green
return
}
# Afficher résumé AVANT action
Write-Host "`n=== RÉSUMÉ SUPPRESSION ===" -ForegroundColor Yellow
$groupesCandidats | Format-Table Name, GroupCategory, GroupScope -AutoSize
Write-Host "Total à supprimer: $($groupesCandidats.Count)" -ForegroundColor Yellow
if (-not $WhatIf) {
$confirmation = Read-Host "Confirmer suppression ? (tapez 'SUPPRIMER' pour confirmer)"
if ($confirmation -ne "SUPPRIMER") {
Write-Host "❌ Opération annulée par l'utilisateur" -ForegroundColor Red
return
}
}
# Suppression avec logging
foreach ($groupe in $groupesCandidats) {
$message = "Suppression groupe: $($groupe.Name) ($($groupe.GroupCategory))"
if ($WhatIf) {
Write-Host "SIMULATION: $message" -ForegroundColor Yellow
} else {
Write-Host "RÉEL: $message" -ForegroundColor Red
# Log avant suppression
Add-Content -Path "C:\Scripts\Logs\groupes-supprimes-$(Get-Date -Format 'yyyyMMdd').log" `
-Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): $($groupe.DistinguishedName)"
}
Remove-ADGroup -Identity $groupe.SamAccountName -Confirm:$false -WhatIf:$WhatIf
}
if ($WhatIf) {
Write-Host "`n🎯 SIMULATION TERMINÉE" -ForegroundColor Yellow
Write-Host "Pour exécuter: .\script.ps1 -WhatIf:`$false" -ForegroundColor Cyan
} else {
Write-Host "`n✅ SUPPRESSION TERMINÉE" -ForegroundColor Green
Write-Host "Log sauvegardé dans: C:\Scripts\Logs\" -ForegroundColor Cyan
}
} catch {
Write-Error "Erreur fatale: $($_.Exception.Message)"
}
🚨 SCRIPT BOMBA #2: L'Organisateur de Déménagement
# Script de migration utilisateurs suite réorganisation
# Auteur: Consultant Senior Migration AD
# Date: 2024-12-01
# Contexte: Fusion départements RH + Compta → "Administration"
Import-Module ActiveDirectory
Write-Host "=== MIGRATION ORGANISATIONNELLE ===" -ForegroundColor Green
Write-Host "Fusion RH + Compta → Administration" -ForegroundColor Yellow
# Configuration de la migration
$sourceOUs = @(
"OU=RH,OU=EU,DC=maxtec,DC=be",
"OU=Compta,OU=EU,DC=maxtec,DC=be"
)
$destinationOU = "OU=Administration,OU=EU,DC=maxtec,DC=be"
Write-Host "Migration vers: $destinationOU" -ForegroundColor Cyan
# Vérifier que l'OU destination existe
try {
Get-ADOrganizationalUnit -Identity $destinationOU -ErrorAction Stop
Write-Host "✅ OU destination confirmée" -ForegroundColor Green
} catch {
Write-Host "Création de l'OU destination..." -ForegroundColor Yellow
New-ADOrganizationalUnit -Name "Administration" -Path "OU=EU,DC=maxtec,DC=be"
Write-Host "✅ OU destination créée" -ForegroundColor Green
}
# Migration des utilisateurs
foreach ($sourceOU in $sourceOUs) {
Write-Host "`nTraitement de: $sourceOU" -ForegroundColor Yellow
try {
# Récupérer tous les utilisateurs de l'OU source
$utilisateurs = Get-ADUser -Filter * -SearchBase $sourceOU
Write-Host "Utilisateurs trouvés: $($utilisateurs.Count)" -ForegroundColor Cyan
foreach ($user in $utilisateurs) {
Write-Host "Migration: $($user.Name)" -ForegroundColor Yellow
# Déplacer l'utilisateur vers la nouvelle OU
Move-ADObject -Identity $user.DistinguishedName -TargetPath $destinationOU
# Mettre à jour le département
Set-ADUser -Identity $user.SamAccountName -Department "Administration"
Write-Host "✅ $($user.Name) migré" -ForegroundColor Green
}
} catch {
Write-Error "Erreur migration $sourceOU : $($_.Exception.Message)"
}
}
Write-Host "`n🎯 MIGRATION TERMINÉE" -ForegroundColor Green
Write-Host "Tous les utilisateurs sont maintenant dans Administration" -ForegroundColor Green
# Nettoyage: supprimer les anciennes OUs vides
Write-Host "`nNettoyage des anciennes OUs..." -ForegroundColor Yellow
foreach ($oldOU in $sourceOUs) {
Write-Host "Suppression: $oldOU" -ForegroundColor Red
Remove-ADOrganizationalUnit -Identity $oldOU -Recursive -Confirm:$false
}
Write-Host "✅ Migration organisationnelle complète!" -ForegroundColor Green
🕵️ MISSION DÉTECTIVE 4.2
Temps limite: 15 minutes
Question cruciale: "Si j'exécute ce script sur maxtec.be vendredi 17h, que se passe-t-il lundi matin ?"
💀 ANALYSE FORENSIQUE
🚨 ERREURS MORTELLES:
-
ABSENCE TOTALE DE -WhatIf (CRITIQUE)
- Migrations immédiatement irréversibles
- Suppressions d'OUs avec -Recursive
-
SUPPRESSION -RECURSIVE SANS VÉRIFICATION (MORTEL)
Conséquence: Supprime TOUT le contenu des OUs (groupes, politiques, sous-OUs)Remove-ADOrganizationalUnit -Identity $oldOU -Recursive -Confirm:$false # ☠️ -
LOGIQUE DE NETTOYAGE DÉFAILLANTE (GRAVE)
- Script assume que migration = réussite
- Supprime OUs même si migration partielle échouée
-
PAS DE VÉRIFICATION GROUPES (CRITIQUE)
- Utilisateurs déplacés mais leurs groupes restent dans anciennes OUs
- Rupture des permissions et accès
-
DÉPARTEMENT HARDCODÉ (MOYEN)
Problème: Force TOUS les users dans même département (perte granularité)Set-ADUser -Identity $user.SamAccountName -Department "Administration" # ☠️
🎭 Scénario du Désastre
Vendredi 17h: Script exécuté
- ✅ Users RH/Compta déplacés vers Administration
- ☠️ Groupes GG-EU-RH-, GG-EU-Compta- supprimés avec OUs
- ☠️ GPOs liées aux OUs supprimées
- ☠️ Toute structure organisationnelle perdue
Lundi 8h: Chaos
- Users existent mais n'ont plus aucun droit
- Groupes de sécurité introuvables
- Applications métier bloquées
- Sauvegarde complète nécessaire = weekend entier
🚨 SCRIPT BOMBA #3: Le Sécurisateur de Mots de Passe
# Script de sécurisation des mots de passe faibles
# Auteur: Équipe Cybersécurité
# Objectif: Forcer changement des mots de passe non-conformes
Import-Module ActiveDirectory
$motsDePasse = @(
"password", "123456", "admin", "root", "user",
"welcome", "temp", "test", "maxtec", "2024"
)
Write-Host "=== AUDIT SÉCURITÉ MOTS DE PASSE ===" -ForegroundColor Red
Write-Host "Recherche des mots de passe faibles..." -ForegroundColor Yellow
foreach ($mdp in $motsDePasse) {
Write-Host "Test mot de passe: $mdp" -ForegroundColor Cyan
# Tester chaque utilisateur avec ce mot de passe
$users = Get-ADUser -Filter * -SearchBase "OU=EU,DC=maxtec,DC=be"
foreach ($user in $users) {
try {
# Tenter connexion avec mot de passe faible
$credential = New-Object System.Management.Automation.PSCredential(
"$($user.SamAccountName)@maxtec.be",
(ConvertTo-SecureString $mdp -AsPlainText -Force)
)
# Test de connexion LDAP
$connection = New-Object System.DirectoryServices.DirectoryEntry(
"LDAP://dns1.maxtec.be",
$credential.UserName,
$credential.GetNetworkCredential().Password
)
if ($connection.Name -ne $null) {
Write-Host "TROUVÉ: $($user.Name) utilise '$mdp'" -ForegroundColor Red
# Forcer changement immédiat
Set-ADUser -Identity $user.SamAccountName -ChangePasswordAtLogon $true
Set-ADAccountPassword -Identity $user.SamAccountName -Reset -NewPassword (ConvertTo-SecureString "TempSecure123!" -AsPlainText -Force)
Write-Host "✅ Mot de passe réinitialisé pour $($user.Name)" -ForegroundColor Green
}
} catch {
# Connexion échouée = mot de passe différent (normal)
}
}
}
Write-Host "✅ Audit de sécurité terminé" -ForegroundColor Green
🕵️ MISSION DÉTECTIVE 4.3
Question piège: "Ce script semble améliorer la sécurité. Où est le problème ?"
⚡ RÉVÉLATION CHOC
🚨 PROBLÈME CACHÉ MAJEUR:
Ce script est une ATTAQUE PAR BRUTE FORCE déguisée !
-
TENTATIVES DE CONNEXION MASSIVES (CRITIQUE)
- Teste systématiquement mots de passe courants
- Génère des millions de tentatives de connexion
- Peut déclencher verrouillages massifs de comptes
-
LOGS DE SÉCURITÉ POLLUÉS (GRAVE)
- Chaque tentative = événement sécurité logged
- Masque les vraies tentatives d'intrusion
- SIEM/SOC saturé d'alertes
-
DÉNI DE SERVICE POTENTIEL (CRITIQUE)
- Peut verrouiller TOUS les comptes utilisateurs
- Surcharge contrôleur de domaine
- Impact business catastrophique
-
VIOLATION POLITIQUE SÉCURITÉ (LÉGAL)
- Test non autorisé de mots de passe
- Peut violer réglementations (RGPD, etc.)
- Risque disciplinaire/légal
🛡️ APPROCHE SÉCURISÉE ALTERNATIVE
# BONNE PRATIQUE: Audit sans test actif
# Utiliser outils Microsoft natifs
# 1. Analyser longueur/complexité via propriétés AD
Get-ADUser -Filter * -Properties PasswordLastSet, PasswordNeverExpires |
Where-Object {
$_.PasswordNeverExpires -eq $true -or
$_.PasswordLastSet -lt (Get-Date).AddDays(-90)
}
# 2. Utiliser Group Policy pour imposer complexity
# 3. Utiliser Azure AD Password Protection
# 4. Sensibiliser utilisateurs = éducation > force
🎓 Récapitulatif: Devenir Détecteur de Bombes
🔍 Les 7 Signaux d'Alarme à Retenir
- 🚨 ABSENCE -WhatIf sur commandes destructives
- 🚨 -Recursive sans vérification préalable
- 🚨 -Filter * sans limitations scope
- ⚠️ Gestion erreurs qui masque problèmes
- ⚠️ Hardcoding paths/valeurs spécifiques
- ⚠️ Logique business non validée avec métier
- ⚠️ "TODO" dans script supposé prêt prod
🛠️ Votre Checklist de Validation
Avant d'exécuter TOUT script:
□ Commandes destructives ont -WhatIf ?
□ Scope limité avec SearchBase/Filter précis ?
□ Gestion d'erreurs robuste ?
□ Exclusions pour objets critiques ?
□ Logging des actions importantes ?
□ Validation business logic ?
□ Test possible sur environnement non-critique ?
□ Rollback plan en cas de problème ?
🎯 Vos Nouvelles Habitudes
- Lire CHAQUE ligne même si script semble simple
- Questionner la logique business avant technique
- Chercher ce qui manque (exclusions, validations)
- Tester d'abord avec -WhatIf ou échantillon réduit
- Documenter ce que vous validez et pourquoi
☕ PAUSE OBLIGATOIRE 15 minutes - Digérer les révélations
🎯 Prochaine étape: Module 5 - -WhatIf Religieux (Pourquoi -WhatIf est Sacré)