
Preuve de concept pédagogique pour PrintNightmare (CVE-2021-1675/34527) avec charge utile simulée non fonctionnelle, analyse du flux d’attaque, correspondance MITRE, conseils de détection et étapes de remédiation.
Type: Proof of Concept — Simulated (Non-Functional) CVE: CVE-2021-1675 + CVE-2021-34527 CVSS: 7.8 (LPE) / 8.8 (RCE) Alias: PrintNightmare Patched: Juin/Juillet 2021 — KB5004945 Ajouté au CISA KEV: Juillet 2021
⚠️ Le payload de ce PoC est explicitement fictif et non-malveillant — seulement 4 octets d'en-tête DCE/RPC suivis du nom de la fonction en ASCII. Il n'exécute pas de code, n'installe pas de pilote, n'élève pas de privilèges. Usage exclusif à des fins d'apprentissage et de développement de détections.
Developed by: HKK$ cat ./vulnerability_summary.txtCVE-2021-1675, connu sous le nom de PrintNightmare, est une vulnérabilité critique d'exécution de code à distance et d'élévation de privilèges locaux dans le service Windows Print Spooler. La faille permet aux attaquants d'exécuter du code arbitraire avec les privilèges SYSTEM en exploitant la fonction RpcAddPrinterDriverEx(), utilisée pour l'installation à distance de pilotes d'imprimante sans validation adéquate du contrôle d'accès.
Initialement classée comme LPE (CVSS 7.8) et corrigée lors du Patch Tuesday de juin 2021, Microsoft a reclassé la vulnérabilité comme RCE (CVSS 8.8) le 21 juin. Pour éviter toute confusion, le vecteur RCE a reçu un nouvel identifiant, CVE-2021-34527 — les deux sont communément appelés PrintNightmare.
$ cat ./timeline.txtJun 08, 2021 Microsoft publie le correctif initial lors du Patch Tuesday — CVE-2021-1675 (LPE, CVSS 7.8)
Jun 21, 2021 Microsoft reclasse en RCE (CVSS 8.8)
Jun 29, 2021 Des chercheurs publient un PoC accidentellement — croyant qu'il s'agissait de la même vuln déjà corrigée
Jul 01, 2021 CVE-2021-34527 attribué pour le vecteur RCE — distinct du LPE d'origine
Jul 02, 2021 Microsoft publie un correctif d'urgence hors bande : KB5004945
Jul 2021 La CISA l'ajoute au Known Exploited Vulnerabilities Catalog
Exploitation active par des groupes de ransomware (Vice Society, Magniber, etc.)
$ cat ./architecture.txt┌──────────────────────────────────────────────────────────────────────────┐
│ PRINTNIGHTMARE — FLUX D'ATTAQUE │
│ │
│ ATTAQUANT (utilisateur authentifié) WINDOWS PRINT SPOOLER │
│ (spoolsv.exe) │
│ 1. S'authentifier avec n'importe quel ┌────────────────────────────┐ │
│ compte de domaine valide │ S'exécute en tant que │ │
│ │ │ SYSTEM │ │
│ 2. Se connecter via RPC au │ Actif par défaut sur tous │ │
│ named pipe \pipe\spoolss │ les Windows, y compris DCs│ │
│ │ └──────────────┬─────────────┘ │
│ 3. Appeler RpcAddPrinterDriverEx() │ │
│ avec le chemin vers une DLL malveillante ──────────►│ │
│ (UNC : \\attacker\share\evil.dll) │ │
│ ┌────────────▼────────────┐ │
│ │ Print Spooler charge le │ │
│ │ pilote (DLL) en tant que│ │
│ │ SYSTEM sans vérifier │ │
│ │ l'origine ou l'intégrité│ │
│ └────────────┬────────────┘ │
│ │ │
│ ◄── RCE / LPE en tant que SYSTEM ────────────────────┘ │
│ Nouvel utilisateur admin, reverse shell, │
│ déploiement de ransomware, etc. │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ CE POC : uniquement TCP connect + 25 octets → pas de RPC réel → pas │
│ d'exploit │
└──────────────────────────────────────────────────────────────────────────┘
$ cat ./design_decisions.mdfake_rpc_payload = b"\x05\x00\x0b\x03RpcAddPrinterDriverEx"
Ce payload fait 25 octets et démontre deux concepts simultanément :
Octets 0-3 : En-tête DCE/RPC légitime
Octets 4-24 : Nom de la fonction en ASCII
RpcAddPrinterDriverEx — le nom de la fonction vulnérable, en texte clair.
Pourquoi ce payload ne fonctionne pas comme un véritable exploit ?
Un BIND RPC légitime exigerait, après l'en-tête de 4 octets :
- max_xmit_frag (2 octets)
- max_recv_frag (2 octets)
- assoc_group_id (4 octets)
- num_ctx_items (1 octet)
- context_elem[] → UUID d'interface MS-RPRN :
{12345678-1234-ABCD-EF00-0123456789AB}
- transfer_syntaxes → UUID NDR
Sans cette structure, Windows rejette immédiatement le paquet. Le payload n'est, en réalité, qu'une assertion de connectivité — « puis-je atteindre le port 445 avec ce payload fictif ? ».
sock.connect((target_ip, 445))
Pourquoi 445 et non 135 (RPC Endpoint Mapper) ?
PrintNightmare utilise le named pipe SMB \pipe\spoolss comme transport — pas le RPC direct. Le flux réel est :
1. TCP connect → port 445 (SMB)
2. SMB negotiate + session setup (authentification NTLM/Kerberos)
3. Tree connect → IPC$
4. Ouverture du named pipe → \pipe\spoolss
5. DCE/RPC BIND → interface MS-RPRN
6. Appel RpcAddPrinterDriverEx() avec marshaling NDR
Le PoC ne simule que l'étape 1 — la connexion TCP initiale — sans implémenter les étapes 2-6.
sock.settimeout(5) : un délai d'expiration de 5 secondes empêche le PoC de rester bloqué sur des hôtes qui ne répondent pas. Utile pour un scan rapide de la disponibilité du port.
La cause racine des deux est la même : la fonction RpcAddPrinterDriverEx() ne parvient pas à valider l'origine des fichiers de pilote, n'impose pas de restrictions de privilèges adéquates pour l'installation de pilotes, et ne vérifie pas l'authenticité ou l'intégrité des DLL chargées.
$ cat ./mitre_mapping.ymltactic: Initial Access / Lateral Movement
- T1210 # Exploitation of Remote Services
# Appel RPC via \pipe\spoolss vers un hôte distant
# Nécessite des identifiants de domaine valides
tactic: Privilege Escalation
- T1068 # Exploitation for Privilege Escalation
# utilisateur standard → SYSTEM via Print Spooler
# CVE-2021-1675 (LPE) : processus local s'élève vers SYSTEM
tactic: Execution
- T1569.002 # System Services: Service Execution
# DLL malveillante chargée par spoolsv.exe en tant que SYSTEM
# Équivalent à l'exécution de code via un service Windows
- T1055 # Process Injection
# DLL injectée dans le contexte du processus spoolsv.exe (SYSTEM)
tactic: Persistence
- T1547.012 # Boot or Logon Autostart: Print Processors
# Pilote enregistré persiste entre les redémarrages
# Stocké dans : HKLM\SYSTEM\CurrentControlSet\Control\Print\
tactic: Defense Evasion
- T1036.005 # Masquerading: Match Legitimate Name or Location
# DLL malveillante nommée comme un pilote légitime (ex : Canon_XP.dll)
$ cat ./detection_opportunities.md# Event ID 808 — Le Print Spooler n'a pas réussi à charger une DLL suspecte
# Source : Microsoft-Windows-PrintService/Admin
Condition:
EventID: 808
Log: Microsoft-Windows-PrintService/Admin
Level: Error
Description CONTAINS: "failed to load a plug-in module"
Preuve réelle (observée in-the-wild):
"The print spooler failed to load a plug-in module
C:\Windows\system32\spool\DRIVERS\x64\3\svch0st.dll"
# Typosquatting de svchost.dll → DLL malveillante
# Event ID 316 — Pilote installé via RpcAddPrinterDriverEx
# Source : Microsoft-Windows-PrintService/Admin
Condition:
EventID: 316
Log: Microsoft-Windows-PrintService/Admin
Message CONTAINS: "RpcAddPrinterDriverEx"
OR
Message CONTAINS: "\\\\*\\*" # chemin UNC → DLL distante
# Processus enfant suspect du Print Spooler
EventID: 1 (Process Creation)
Condition:
ParentImage: C:\Windows\System32\spoolsv.exe
Image: cmd.exe OU powershell.exe OU wscript.exe OU mshta.exe
Sévérité : CRITIQUE — spoolsv ne doit pas générer de shells
# DLL chargée depuis un chemin non standard
EventID: 7 (Image Loaded)
Condition:
Image: C:\Windows\System32\spoolsv.exe
ImageLoaded NOT STARTS WITH:
- C:\Windows\System32\spool\DRIVERS\
- C:\Windows\System32\
- C:\Windows\SysWOW64\
Sévérité : ÉLEVÉE
Pour la détection au niveau réseau, surveiller les named pipes SMB : la présence de \pipe\spoolss combinée aux opérations RpcEnumPrinterDrivers ou RpcAddPrinterDriverEx sont des indicateurs directs d'exploitation de PrintNightmare.
# Règle conceptuelle Zeek / Suricata :
alert smb any any -> $SERVERS 445 (
msg:"PrintNightmare RpcAddPrinterDriverEx via SMB";
content:"|5c 70 69 70 65 5c 73 70 6f 6f 6c 73 73|"; # \pipe\spoolss
content:"RpcAddPrinterDriverEx";
sid:2021001675;
)
$ cat ./remediation.mdOPTION 1 — Désactiver le Print Spooler (si l'impression n'est pas nécessaire)
PowerShell :
Stop-Service -Name Spooler -Force
Set-Service -Name Spooler -StartupType Disabled
OPTION 2 — Bloquer l'impression à distance via la stratégie de groupe
Computer Configuration → Administrative Templates → Printers
→ "Allow Print Spooler to accept client connections" : DÉSACTIVÉ
Cela désactive uniquement le vecteur distant, en conservant l'impression locale
OPTION 3 — Appliquer les correctifs (obligatoire indépendamment des options ci-dessus)
KB5004945 → correctif hors bande de juillet 2021
+ Correctifs cumulatifs ultérieurs
VÉRIFICATION POST-CORRECTIF — Le registre peut encore laisser le système vulnérable :
REG QUERY "HKLM\Software\Policies\Microsoft\Windows NT\Printers\PointAndPrint"
Si NoWarningNoElevationOnInstall=1 OU UpdatePromptSettings=0 → ENCORE VULNÉRABLE
Correction : définir les deux valeurs sur 0 ou supprimer les clés
PRIORITÉ SPÉCIALE : Contrôleurs de domaine
Les DC exécutent le Print Spooler par défaut
Compromettre un DC via PrintNightmare = compromission complète du domaine AD
$ cat ./usage.sh# Installer la dépendance (socket est dans la bibliothèque standard)
python --version # Python 3.x, pas de pip nécessaire
# Modifier l'IP cible dans le script (uniquement en laboratoire autorisé)
# simulate_printnightmare_attack("192.168.1.xxx")
python poc_cve_2021_1675.py
# Sortie attendue si le port 445 est accessible :
[+] Payload envoyé à 192.168.1.117
# Sortie attendue si le port est fermé/filtré :
[-] Erreur de connexion : [Errno 111] Connection refused
# ou
[-] Erreur de connexion : timed out
Ce que cette sortie indique : uniquement que le port 445 est accessible. Cela ne confirme pas la vulnérabilité — pour cela, un véritable BIND RPC serait nécessaire + une vérification de la version du correctif.
$ cat ./poc_vs_real_exploit.mdComparaison explicite entre le PoC simulé et un véritable exploit.
$ cat ./lessons_learned.txt[+] Le Print Spooler actif sur les DC par défaut est le scénario le plus critique — SYSTEM sur un DC = game over
[+] Les named pipes SMB comme transport RPC sont un vecteur sous-estimé dans les défenses axées sur le périmètre
[+] DCE/RPC est un protocole complexe — comprendre la structure bind/call aide à créer des signatures
[+] CVE publiée ≠ correctif appliqué — des semaines après le correctif, la plupart des environnements étaient encore vulnérables
[+] Les clés de registre PointAndPrint peuvent réintroduire la vulnérabilité même après le correctif — contrôle post-correctif critique
[-] Ce PoC vérifie uniquement la connectivité TCP — il ne valide pas si le système est réellement corrigé
[-] Un socket pur sans SMB ne peut pas négocier l'authentification — limitation fondamentale de l'approche
[-] settimeout(5) peut générer un faux négatif sur des réseaux lents — envisager un délai paramétrable
[→] Améliorations : utiliser impacket pour un RPC réel, vérifier le banner SMB pour l'empreinte de la version de l'OS,
tester le KB appliqué via une requête de registre à distance
CVE Research · PrintNightmare · Patched July 2021 · CISA KEV · Simulated payload only
| Octet | Valeur | Signification |
|---|
\x05 | 5 | Version du protocole DCE/RPC (version 5 — standard Windows) |
\x00 | 0 | Version mineure 0 |
\x0b | 11 | Type de PDU : BIND — première étape de la négociation RPC |
\x03 | 3 | Flags : `PFC_FIRST_FRAG(0x01) |
| Aspect | CVE-2021-1675 | CVE-2021-34527 |
|---|
| Vecteur | Local (LPE) | Remote (RCE) |
| CVSS | 7.8 | 8.8 |
| Authentification | Compte local | Compte de domaine authentifié |
| Correctif | Juin 2021 (Patch Tuesday) | Juillet 2021 (hors bande KB5004945) |
| Fonction vulnérable | RpcAddPrinterDriverEx | RpcAddPrinterDriverEx |
| Scénario | Élévation locale | RCE à distance via named pipe SMB |
| Composant | Ce PoC | Exploit Réel |
|---|
| Connexion | Socket TCP port 445 | Session SMB + Tree connect IPC$ |
| Authentification | Aucune | NTLM ou Kerberos (compte de domaine) |
| Named pipe | Non implémenté | \pipe\spoolss |
| BIND RPC | Faux (4 octets + ASCII) | BIND DCE/RPC avec UUID MS-RPRN |
| Fonction appelée | Non appelée | RpcAddPrinterDriverEx() via NDR |
| Chemin DLL | Non envoyé | UNC : \\attacker\share\evil.dll |
| Résultat | Affichage en console | RCE en tant que SYSTEM / LPE |
| Détection | Quasi nulle | EventID 808, 316, Sysmon 1/7 |