---
title: "Hardening Windows Server — CIS Benchmark"
domain: security
subdomain: hardening
type: snippet
tags: [hardening, windows, cis, gpo, applocker, audit, security-policy, server]
difficulty: intermediate
status: stable
updated: "2025-05-14"
---
## Comptes et authentification

```powershell
# Renommer le compte Administrator intégré
Rename-LocalUser -Name "Administrator" -NewName "{{ADMIN_ALIAS}}"

# Désactiver le compte Guest
Disable-LocalUser -Name "Guest"

# Politique de mots de passe
net accounts /minpwlen:14 /maxpwage:90 /minpwage:1 /uniquepw:24 /lockoutthreshold:5 /lockoutduration:30 /lockoutwindow:30

# Vérifier la politique actuelle
net accounts
```

```powershell
# Désactiver NTLM v1 via registre
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "LmCompatibilityLevel" -Value 5
# Valeur 5 = NTLMv2 only, refuse LM et NTLMv1
# Équivalent GPO : Network security: LAN Manager authentication level = NTLMv2 only
```

```powershell
# Credential Guard — activation (nécessite Virtualization Based Security)
# Via GPO : Computer Configuration > Administrative Templates > System > Device Guard
# Enable Virtualization Based Security = Enabled
# Credential Guard Configuration = Enabled with UEFI lock

# Vérification après redémarrage
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
  Select-Object -Property SecurityServicesRunning
# 1 = Credential Guard actif
```

<Warning>
Avant de forcer NTLMv2 only (LmCompatibilityLevel = 5), vérifier qu'aucun système legacy (Windows XP, anciens NAS, imprimantes réseau) ne dépend de NTLM v1 ou LM — ils perdront l'accès réseau immédiatement.
</Warning>

## Audit et journalisation

```powershell
# Activer toutes les catégories d'audit
auditpol /set /category:* /success:enable /failure:enable

# Catégories critiques en détail
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Account Lockout" /success:enable /failure:enable
auditpol /set /subcategory:"Process Creation" /success:enable /failure:enable
auditpol /set /subcategory:"Scheduled Task" /success:enable /failure:enable
auditpol /set /subcategory:"Security Group Management" /success:enable /failure:enable
auditpol /set /subcategory:"User Account Management" /success:enable /failure:enable
auditpol /set /subcategory:"Service" /success:enable /failure:enable

# Vérifier la configuration
auditpol /get /category:*
```

Event IDs critiques à surveiller :

| Event ID | Description |
|---|---|
| **4624** | Connexion réussie |
| **4625** | Échec de connexion |
| **4648** | Connexion avec credentials explicites (runas) |
| **4688** | Création d'un processus (avec command line) |
| **4698** | Création d'une tâche planifiée |
| **4720** | Création d'un compte utilisateur |
| **4726** | Suppression d'un compte utilisateur |
| **7045** | Nouveau service installé |
| **4104** | PowerShell ScriptBlock logging |

```powershell
# Activer le logging de la ligne de commande dans 4688
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit" `
  -Name "ProcessCreationIncludeCmdLine_Enabled" -Value 1 -Type DWord

# PowerShell ScriptBlock Logging (Event ID 4104)
$psLogPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $psLogPath -Force | Out-Null
Set-ItemProperty -Path $psLogPath -Name "EnableScriptBlockLogging" -Value 1

# Dimensionner le Security log à 1 Go minimum
wevtutil sl Security /ms:1073741824

# Vérifier la taille configurée
wevtutil gl Security | Select-String "maxSize"
```

## Services à désactiver

```powershell
# Désactiver SMBv1 (vecteur WannaCry, NotPetya)
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
Set-SmbClientConfiguration -EnableSMB1Protocol $false -Force

# Vérifier
Get-SmbServerConfiguration | Select-Object EnableSMB1Protocol, EnableSMB2Protocol

# Services inutiles à désactiver
$services = @(
  "Spooler",          # Print Spooler (si pas d'impression sur ce serveur)
  "TlntSvr",          # Telnet
  "RemoteRegistry",   # Remote Registry
  "TFTPD",            # TFTP
  "WinHttpAutoProxySvc"
)

foreach ($svc in $services) {
  if (Get-Service -Name $svc -ErrorAction SilentlyContinue) {
    Stop-Service -Name $svc -Force
    Set-Service -Name $svc -StartupType Disabled
    Write-Host "$svc désactivé"
  }
}

# Désactiver LLMNR via registre
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" `
  -Name "EnableMulticast" -Value 0

# Désactiver NetBIOS over TCP/IP (via WMI, à appliquer sur chaque interface)
$adapters = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True"
foreach ($adapter in $adapters) {
  $adapter.SetTcpipNetbios(2)  # 2 = Disable NetBIOS
}
```

## Pare-feu Windows

```powershell
# Activer le pare-feu sur tous les profils
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True

# Bloquer LLMNR (UDP 5355)
New-NetFirewallRule -DisplayName "Block LLMNR" -Direction Inbound `
  -Protocol UDP -LocalPort 5355 -Action Block

# Bloquer NetBIOS (UDP 137-138, TCP 139)
New-NetFirewallRule -DisplayName "Block NetBIOS UDP 137" -Direction Inbound `
  -Protocol UDP -LocalPort 137 -Action Block
New-NetFirewallRule -DisplayName "Block NetBIOS UDP 138" -Direction Inbound `
  -Protocol UDP -LocalPort 138 -Action Block
New-NetFirewallRule -DisplayName "Block NetBIOS TCP 139" -Direction Inbound `
  -Protocol TCP -LocalPort 139 -Action Block

# Restreindre WinRM à un sous-réseau de gestion
Set-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)" `
  -RemoteAddress "{{MGMT_SUBNET}}"
```

```powershell
# netsh — vérifier l'état du pare-feu
netsh advfirewall show allprofiles state

# Bloquer tout le trafic sortant non autorisé (optionnel, restrictif)
netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound
```

## AppLocker — Whitelisting

```powershell
# Activer le service Application Identity (prérequis AppLocker)
Set-Service -Name AppIDSvc -StartupType Automatic
Start-Service AppIDSvc

# Créer les règles par défaut (allow Admins + Everyone sur %ProgramFiles%)
Get-AppLockerPolicy -Effective | Test-AppLockerPolicy -Path "C:\Windows\System32\cmd.exe" -User Everyone

# Générer les règles par défaut via GPO (recommandé)
# Computer Configuration > Windows Settings > Security Settings > Application Control Policies > AppLocker
# Create Default Rules pour : Executable Rules, Windows Installer Rules, Script Rules
```

```xml
<!-- Exemple de règle AppLocker XML — Publisher rule pour Microsoft Office -->
<RuleCollection Type="Exe" EnforcementMode="Enforced">
  <FilePublisherRule Id="{{GUID}}" Name="Allow signed Microsoft Office"
    Description="Allow executables signed by Microsoft Corporation"
    UserOrGroupSid="S-1-1-0" Action="Allow">
    <Conditions>
      <FilePublisherCondition PublisherName="O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"
        ProductName="*" BinaryName="*">
        <BinaryVersionRange LowSection="*" HighSection="*" />
      </FilePublisherCondition>
    </Conditions>
  </FilePublisherRule>
</RuleCollection>
```

## GPO Hardening — Security Options

Chemin GPO : `Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options`

| Paramètre | Valeur recommandée |
|---|---|
| Interactive logon: Message for users attempting to log on | Texte d'avertissement légal |
| Interactive logon: Do not display last user name | Enabled |
| Network access: Do not allow anonymous enumeration of SAM accounts | Enabled |
| Network access: Restrict anonymous access to Named Pipes and Shares | Enabled |
| User Account Control: Only elevate executables that are signed and validated | Enabled |
| User Account Control: Run all administrators in Admin Approval Mode | Enabled |
| User Account Control: Behavior of the elevation prompt for administrators | Prompt for credentials on the secure desktop |
| Accounts: Rename administrator account | `ADMIN_ALIAS` (ex: `sysadm`) |
| Accounts: Rename guest account | `GUEST_ALIAS` (ex: `invité-désactivé`) |
| Microsoft network server: Digitally sign communications (always) | Enabled |
| Microsoft network client: Digitally sign communications (always) | Enabled |

## Windows Defender & ASR

```powershell
# S'assurer que la protection en temps réel est active
Set-MpPreference -DisableRealtimeMonitoring $false
Set-MpPreference -DisableBehaviorMonitoring $false
Set-MpPreference -DisableIOAVProtection $false

# Activer les règles Attack Surface Reduction (ASR)
# Mode : 1 = Block, 2 = Audit, 0 = Disabled
$asrRules = @{
  "BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550" = 1  # Block executable content from email/webmail
  "D4F940AB-401B-4EFC-AADC-AD5F3C50688A" = 1  # Block Office apps from creating child processes
  "3B576869-A4EC-4529-8536-B80A7769E899" = 1  # Block Office apps from creating executable content
  "75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84" = 1  # Block Office apps from injecting into processes
  "D3E037E1-3EB8-44C8-A917-57927947596D" = 1  # Block JS/VBS from launching downloaded exe
  "5BEB7EFE-FD9A-4556-801D-275E5FFC04CC" = 1  # Block execution of potentially obfuscated scripts
  "92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B" = 1  # Block Win32 API calls from Office macros
  "01443614-CD74-433A-B99E-2ECDC07BFC25" = 1  # Block executable files unless they meet prevalence criteria
}

foreach ($rule in $asrRules.GetEnumerator()) {
  Add-MpPreference -AttackSurfaceReductionRules_Ids $rule.Key `
    -AttackSurfaceReductionRules_Actions $rule.Value
}

# Activer Tamper Protection (via Defender Security Center UI ou Intune)
# Registre (lecture seule si Tamper Protection actif) :
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection"
# Valeur 5 = Tamper Protection activé

# Vérifier l'état complet
Get-MpComputerStatus | Select-Object AMRunningMode, RealTimeProtectionEnabled, TamperProtectionSource
```

## RDP — Sécurisation

```powershell
# Forcer NLA (Network Level Authentication) obligatoire
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
  -Name "UserAuthentication" -Value 1

# Changer le port RDP par défaut (3389 → port personnalisé)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
  -Name "PortNumber" -Value {{RDP_PORT}}

# Limiter les utilisateurs autorisés
# Ajouter uniquement les comptes nécessaires au groupe "Remote Desktop Users"
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "{{DOMAIN}}\{{RDP_USER}}"

# Désactiver RDP si non utilisé
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" `
  -Name "fDenyTSConnections" -Value 1
```

## PowerShell Constrained Language Mode

```powershell
# Activer Constrained Language Mode via GPO AppLocker ou WDAC
# (Automatique si AppLocker est configuré avec règles de whitelisting)

# Vérifier le language mode actuel
$ExecutionContext.SessionState.LanguageMode
# FullLanguage = pas de restriction
# ConstrainedLanguage = mode restreint actif

# Forcer via variable d'environnement (test uniquement, non persistant)
[Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "4", "Machine")
# Redémarrer PowerShell pour effet
```

## LAPS — Local Administrator Password Solution

```powershell
# Installer le module LAPS (Windows LAPS natif Windows Server 2022 / Win 11 22H2+)
# Vérifier la disponibilité
Get-Module -ListAvailable -Name LAPS

# Configurer via GPO :
# Computer Configuration > Administrative Templates > System > LAPS
# Enable local admin password management = Enabled
# Password Settings : longueur 15+, complexité, rotation 30 jours

# Lire le mot de passe LAPS d'un ordinateur (nécessite droits de lecture)
Get-LapsADPassword -Identity "{{COMPUTER_NAME}}" -AsPlainText

# Pour LAPS legacy (Microsoft LAPS v1) :
# Import-Module AdmPwd.PS
# Get-AdmPwdPassword -ComputerName "{{COMPUTER_NAME}}"
```

## Checklist hardening Windows

<Checklist
  storageKey="hardening-windows"
  items={[
    { id: "accounts-renamed", label: "Compte Administrator renommé, Guest désactivé", critical: true },
    { id: "password-policy", label: "Politique de mots de passe : 14+ chars, complexité, historique 24, lockout 5 tentatives", critical: true },
    { id: "ntlm-v2", label: "NTLMv2 uniquement (LmCompatibilityLevel = 5)", critical: true },
    { id: "smb-v1", label: "SMBv1 désactivé (client et serveur)", critical: true },
    { id: "audit-enabled", label: "Audit activé sur toutes les catégories (auditpol /set /category:*)" },
    { id: "logs-sized", label: "Security log dimensionné à 1 Go minimum (wevtutil sl Security /ms:1073741824)" },
    { id: "cmdline-logging", label: "Process Creation audit (4688) avec command line logging activé" },
    { id: "firewall-active", label: "Pare-feu Windows actif sur tous les profils, LLMNR et NetBIOS bloqués" },
    { id: "applocker", label: "AppLocker configuré : whitelisting Publisher rules, service AppIDSvc démarré" },
    { id: "defender-asr", label: "Windows Defender actif + règles ASR configurées + Tamper Protection" },
    { id: "credential-guard", label: "Credential Guard activé (VBS + Secure Boot requis)" },
    { id: "winrm-restricted", label: "WinRM restreint au sous-réseau de gestion uniquement" },
    { id: "rdp-nla", label: "RDP : NLA obligatoire, port changé, accès restreint au groupe RDP Users" },
    { id: "powershell-clm", label: "PowerShell Constrained Language Mode actif (via AppLocker/WDAC)" },
    { id: "laps-deployed", label: "LAPS déployé sur tous les postes/serveurs (rotation mots de passe locaux)" },
    { id: "services-disabled", label: "Services inutiles désactivés : Print Spooler, Remote Registry, Telnet" }
  ]}
/>

<Tip>
Activer `Process Creation` audit (Event ID 4688) avec command line logging — c'est la source de détection la plus riche pour un SOC Windows. Sans cette option, les logs 4688 n'incluent pas la commande exécutée, rendant l'analyse forensique quasi impossible. Coupler avec PowerShell ScriptBlock Logging (4104) pour une couverture complète.
</Tip>
