Leçon 7.4 · Comportements malveillants· 45 min
Vol d’identifiants et keylogging
Reconnaître infostealers, accès à LSASS et keyloggers dans les échantillons et la télémétrie, vérifier les contrôles et cerner les comptes exposés.
Objectifs
- Expliquer pourquoi le vol d’identifiants élargit un incident et comment il s’inscrit dans la tactique Credential Access d’ATT&CK
- Reconnaître les stealers de navigateur, l’accès à LSASS et aux ruches du registre, et les keyloggers à partir de leurs imports, de leurs chaînes et de leurs motifs d’accès aux processus
- Détecter l’accès aux identifiants avec les masques d’accès de l’événement Sysmon 10, l’audit Security et l’audit des accès aux fichiers, et ajuster ces règles en toute sécurité
- Vérifier au niveau de la configuration Credential Guard, la protection LSA et les règles de réduction de la surface d’attaque
- Délimiter les comptes exposés, réinitialiser et révoquer dans le bon ordre, et chasser la réutilisation des identifiants volés
Mécanismes de persistance se terminait sur un avertissement : supprimer chaque point d’ancrage ne revient pas à évincer un intrus, car les identifiants qu’il a collectés fonctionnent toujours. Cette leçon porte sur cette seconde moitié. Vous apprendrez à reconnaître le vol d’identifiants dans un échantillon avant de l’exécuter, à le voir dans la télémétrie de l’hôte au moment où il se produit, à vérifier les contrôles qui auraient dû l’arrêter, et à transformer un vol confirmé en une liste de comptes à réinitialiser, de sessions à révoquer et d’ouvertures de session à chasser.
Le vol d’identifiants correspond à la tactique ATT&CK TA0006, Credential Access. Cette leçon reste du côté du défenseur : chaque comportement est décrit par ce qu’un analyste ou un capteur observe, et non par la façon dont il est réalisé.
Pourquoi le vol d’identifiants change l’incident
Un échantillon qui ne s’exécute que sur un hôte est un problème d’hôte. Un échantillon qui vole des identifiants est un problème d’identité, et les problèmes d’identité ne restent pas sur une seule machine. Trois conséquences découlent de la confirmation d’un vol :
- Le périmètre s’étend à tous les systèmes que les comptes volés peuvent atteindre. Le hash du mot de passe d’un administrateur de domaine récupéré sur un poste de travail ouvre des serveurs que le malware (logiciel malveillant) n’a jamais touchés. Le mouvement latéral avec du matériel volé (pass-the-hash et pass-the-ticket, ATT&CK T1550) ressemble à des ouvertures de session normales : le seul moyen fiable de le borner est donc de savoir quels comptes ont été exposés.
- Le nettoyage doit inclure des réinitialisations, et celles-ci ont un ordre. Réinitialiser un mot de passe alors que l’intrus a encore un point d’ancrage lui livre le nouveau. Réinitialiser le mot de passe d’un utilisateur ne met pas fin à une session de navigateur dont le cookie a été volé.
- Les jetons survivent aux mots de passe. Les cookies de session, les jetons de rafraîchissement et les tickets Kerberos restent valides jusqu’à leur expiration ou leur révocation. Un stealer qui a exporté des cookies peut permettre à l’attaquant de contourner entièrement la MFA (T1539, puis T1550.004) : révoquer les sessions est donc aussi important que changer les mots de passe.
De nombreux stealers sont des programmes one-shot sans aucune persistance : ils collectent, envoient et se terminent. C’est pourquoi la question de la persistance vue dans la leçon précédente a une question jumelle dans chaque rapport : qu’est-ce que cet échantillon a pu prendre, et à qui ?
Les familles que vous rencontrerez
Les familles ci-dessous se chevauchent en pratique (de nombreux infostealers prennent aussi des captures d’écran et lisent le presse-papiers), mais chacune laisse une trace distincte.
| Famille | Ce qu’elle cible | Indices statiques | Télémétrie | ATT&CK |
|---|---|---|---|---|
| Stealers de navigateurs et d’applications | Identifiants enregistrés, cookies, saisie automatique, fichiers de portefeuilles et de clients de messagerie | Chemins de profils et noms de fichiers sous forme de chaînes ; code ou chaînes SQLite ; CryptUnprotectData ; BCryptDecrypt | Lectures de fichiers sous les profils de navigateur (Security 4663 avec une SACL, événements fichier de l’EDR) ; copie d’une base verrouillée écrite dans un dossier temporaire | T1555.003, T1539, T1552.001 |
| Magasins d’identifiants Windows | Gestionnaire d’identifiants, entrées du coffre | CredEnumerateW, fonctions de vaultcli.dll comme VaultEnumerateItems | Télémétrie d’API de l’EDR ; lectures sous AppData\...\Microsoft\Credentials | T1555.004 |
| Accès à LSASS | Hashs, tickets et secrets détenus par la Local Security Authority | OpenProcess avec MiniDumpWriteDump, ReadProcessMemory, ou manipulation de SeDebugPrivilege ; lsass sous forme de chaîne, souvent encodée | Sysmon 10 avec TargetImage lsass.exe ; Security 4656 ; nouveau fichier de dump (Sysmon 11) ; événements de blocage ASR | T1003.001 |
| Ruches du registre | Hashs des comptes locaux (SAM), secrets LSA, ouvertures de session de domaine en cache | RegSaveKeyExW ; chaînes SAM, SECURITY, SYSTEM ; ligne de commande reg.exe avec un verbe save | Sysmon 1 pour reg.exe ; Sysmon 11 pour des fichiers de la taille d’une ruche dans des chemins temporaires ; accès aux clichés instantanés | T1003.002, T1003.004, T1003.005 |
| Keyloggers | Tout ce qui est tapé, avec la fenêtre dans laquelle c’est tapé | GetAsyncKeyState ou GetKeyboardState dans une boucle ; SetWindowsHookExW avec un hook clavier de bas niveau ; RegisterRawInputDevices ; GetForegroundWindow avec GetWindowTextW | Télémétrie de hooks et d’API de l’EDR ; fichier journal qui grossit ; envois périodiques | T1056.001 |
| Capture du presse-papiers et de l’écran | Mots de passe copiés, adresses de portefeuilles, documents visibles | OpenClipboard, GetClipboardData, AddClipboardFormatListener ; GetDC, BitBlt, encodeurs d’images GDI+ | Fichiers image apparaissant dans des dossiers temporaires ; événements de capture d’écran de l’EDR | T1115, T1113 |
Les stealers se lisent comme une liste de courses
Les infostealers courants sont généralement les plus faciles à reconnaître dans
les chaînes, une fois celles-ci décodées. Ils
transportent de longues listes d’emplacements cibles : dossiers de profils de
navigateur se terminant par User Data, noms de fichiers comme Login Data,
Cookies, Web Data et Local State, les logins.json et key4.db de Firefox,
fichiers de configuration de clients FTP, dossiers de session de clients de
messagerie et répertoires de portefeuilles de cryptomonnaie. À côté des chemins,
on trouve souvent des fragments SQL nommant les colonnes d’une table de logins,
des clés JSON comme encrypted_key, et une bibliothèque SQLite liée
statiquement. L’import qui relie le tout est CryptUnprotectData de
crypt32.dll, l’appel DPAPI qui déballe les secrets protégés pour l’utilisateur
courant, souvent suivi de BCryptDecrypt ou d’une routine AES embarquée. Comme un
navigateur en cours d’exécution garde ses bases verrouillées, les stealers les
copient souvent d’abord dans un dossier temporaire, et certains délèguent la copie
à un utilitaire signé comme esentutl.exe ;
la copie est elle-même un événement de création de fichier sur lequel vous pouvez
alerter.
Deux mises en garde gardent cette lecture honnête. D’abord, ces chaînes sont précisément ce que les auteurs chiffrent : un stealer dont la liste de chaînes paraît propre n’a rien d’inhabituel ; décodez d’abord, comme dans Scripter le déchiffrement des chaînes. Ensuite, de nombreux stealers sont écrits en .NET ou sous forme de scripts, et les preuves se trouvent alors dans les métadonnées et le source plutôt que dans l’IAT ; les leçons Analyser un malware .NET et Malwares PowerShell, JavaScript et VBScript expliquent où chercher.
L’accès à LSASS est une question d’accès aux processus
lsass.exe détient le matériel d’authentification de toutes les personnes
connectées à la machine. Le lire exige d’ouvrir le processus avec des droits qui
autorisent la lecture de sa mémoire, ce qui en fait un problème d’accès aux
processus plus qu’un problème d’API. Processus, threads et DLL
a montré qu’un handle vers un autre processus est un prérequis pour toucher à sa
mémoire ; pour LSASS, ce sont les droits de ce handle qui constituent le signal.
Dans un échantillon, cherchez lsass sous forme de chaîne (ou d’une forme
encodée) à proximité d’une énumération de processus, d’OpenProcess, d’un
ajustement de privilège pour SeDebugPrivilege, et soit de MiniDumpWriteDump
issu de dbghelp.dll ou de dbgcore.dll, soit de lectures directes de mémoire.
De nombreux outils évitent entièrement la table des imports grâce à la
résolution dynamique ou au
hachage d’API, et certains détournent des
utilitaires légitimes signés pour produire le dump, ce qui déplace les preuves des
imports de l’échantillon vers une ligne de commande.
Les keyloggers utilisent des API de saisie ordinaires
Les API de saisie sont les plus difficiles à juger isolément, car les jeux, les outils d’accessibilité, les utilitaires de raccourcis clavier et les logiciels d’assistance à distance les appellent aussi. Regardez la combinaison et la boucle, pas l’import isolé :
- Polling :
GetAsyncKeyStateouGetKeyStateappelée sur toute la plage des codes de touches virtuelles dans une boucle à court sommeil, généralement avecMapVirtualKeyWouToUnicodepour convertir les codes en caractères. - Hooking :
SetWindowsHookExWavec le type de hook clavier de bas niveau (WH_KEYBOARD_LL, valeur 13) et une boucle de messages autour deGetMessageW. La page SetWindowsHookEx traite de l’usage de la même API pour l’injection. - Raw input :
RegisterRawInputDevicesetGetRawInputDatasur une fenêtre cachée. - Contexte et exfiltration :
GetForegroundWindowetGetWindowTextWpour étiqueter les frappes par fenêtre, un motif d’ajout à un fichier, et une capacité réseau ou de messagerie pour envoyer le journal.
Un binaire qui interroge une seule touche pour gérer un raccourci est normal. Un binaire qui interroge toutes les touches, enregistre le titre de la fenêtre, écrit dans un fichier caché et dispose d’une capacité d’envoi est un keylogger. Le lab ci-dessous montre le peu qu’un import de saisie isolé vous apprend à lui seul.
Sources de détection
Événement Sysmon 10 : accès aux processus
Sysmon 10 enregistre l’ouverture d’un processus par un autre, avec
SourceImage, TargetImage, GrantedAccess (le masque d’accès effectivement
accordé) et CallTrace (la pile d’appels des modules à l’origine de la demande).
Le collecter pour chaque processus coûte cher : la plupart des configurations
n’incluent donc que des cibles sensibles comme lsass.exe.
Le masque est le cœur de la règle. Les droits qui comptent pour LSASS sont :
| Droit | Valeur | Pourquoi il compte |
|---|---|---|
PROCESS_VM_READ | 0x0010 | Lire la mémoire du processus |
PROCESS_VM_WRITE, PROCESS_VM_OPERATION | 0x0020, 0x0008 | Modifier la mémoire ou changer les protections |
PROCESS_CREATE_THREAD | 0x0002 | Exécuter du code dans le processus |
PROCESS_DUP_HANDLE | 0x0040 | En copier des handles |
PROCESS_QUERY_LIMITED_INFORMATION | 0x1000 | Nom, chemin, code de sortie : inoffensif |
Des masques comme 0x1000 et 0x1400 (droits de requête uniquement) sont du
bruit de fond provenant des services système et des outils de supervision. Les
masques qui combinent un droit de requête avec 0x0010, comme 0x1010 ou
0x1410, sont le motif classique de lecture mémoire, et 0x1fffff correspond à
l’accès complet. Testez des bits, pas des valeurs littérales : les attaquants
font varier le masque pour esquiver les règles qui correspondent à une seule
chaîne. Un CallTrace contenant UNKNOWN signifie qu’une partie de la pile
d’appels s’est exécutée depuis une mémoire non adossée à un fichier sur disque, ce
qui est un signe fort de code injecté ou chargé de manière réflective.
Audit Security et EDR
Avec la sous-catégorie d’audit Kernel Object activée, Windows peut journaliser
les demandes de handle Security 4656 visant lsass.exe ; les builds récents
fournissent à cet effet une liste de contrôle d’accès système (SACL) sur le
processus : vérifiez donc le volume sur un pilote avant de l’activer sur tout le
parc. Security 4663 enregistre les accès aux fichiers dotés d’une SACL, ce qui
permet d’auditer les lectures des fichiers de profil de navigateur : ajoutez une
entrée d’audit pour Read data sur les dossiers User Data d’un groupe pilote et
excluez le navigateur lui-même. Security 4624 et 4648 comptent plus tard, pour
chasser la réutilisation. Les produits EDR ajoutent ce que Windows ne journalise
pas nativement : des événements au niveau des API pour les lectures mémoire,
l’installation de hooks et la capture d’écran, et ils bloquent généralement
d’emblée le motif LSASS ; leurs alertes sont aussi des sources de détection.
Ajustement
Chaque source rencontre ici des logiciels légitimes. Les antivirus et les agents
EDR ouvrent LSASS avec tous les droits ; les outils de sauvegarde et de
synchronisation lisent les profils de navigateur ; les gestionnaires de mots de
passe lisent le presse-papiers. Ajustez avec des exceptions qui fixent le chemin
complet de l’image et, si vos données le permettent, le signataire ou le hash
issu de l’événement Sysmon 1 du processus. N’ajustez jamais sur le seul nom de
fichier : un stealer nommé MsMpEng.exe qui s’exécute depuis un dossier
temporaire doit toujours déclencher l’alerte. Les exceptions doivent expirer et
être revues, car les produits qu’elles couvrent se mettent à jour et changent
d’emplacement.
Astuce : Une règle LSASS qui ne se déclenche jamais est plus probablement cassée que simplement silencieuse. Vérifiez que votre configuration Sysmon inclut bien l’événement 10 pour
lsass.exe, et rejouez un accès bénin connu (la requête d’un agent de supervision) pour valider la chaîne de bout en bout.
Contrôles préventifs à vérifier
Lorsqu’une alerte de vol d’identifiants arrive, l’une des premières questions est de savoir si les contrôles qui auraient dû le limiter étaient en place. Vérifiez-les hôte par hôte, comme des faits de configuration à consigner dans le rapport :
| Contrôle | À vérifier | Ce qu’il change |
|---|---|---|
| Credential Guard | (Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesRunning inclut 1 | Isole les hashs NTLM et les secrets Kerberos de la mémoire de LSASS dans un processus protégé par la virtualisation ; les lectures de LSASS rapportent bien moins |
| Protection LSA | RunAsPPL sous HKLM\SYSTEM\CurrentControlSet\Control\Lsa vaut 1 (verrouillé par UEFI) ou 2 ; l’événement WinInit 12 indique que LSASS a démarré en tant que processus protégé | Les processus non protégés ne peuvent pas ouvrir LSASS pour lire sa mémoire, même en administrateur |
| Règle ASR | Le GUID de règle 9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2 (« Block credential stealing from the Windows local security authority subsystem ») apparaît dans Get-MpPreference avec l’action block, et non audit | Bloque les tentatives de lecture de LSASS et les journalise sous forme d’événements Defender |
| WDigest | UseLogonCredential sous ...\SecurityProviders\WDigest absent ou à 0 | Empêche la présence de mots de passe en clair en mémoire ; un passage à 1 est en soi une alerte |
| MFA | Imposée pour l’accès distant, la messagerie et les portails d’administration, de préférence résistante au phishing | Limite ce qu’un mot de passe volé permet à lui seul |
| Moindre privilège | Aucune ouverture de session d’administrateur de domaine sur les postes de travail ; mots de passe d’administrateur local uniques (LAPS) ; utilisateurs privilégiés dans Protected Users | Limite les secrets que détient un poste de travail compromis |
Aucun de ces contrôles n’arrête un stealer de navigateur qui s’exécute sous l’identité de l’utilisateur, puisque celui-ci peut lire ses propres données. Les contrôles sont alors la MFA, des politiques qui tiennent les identifiants hors des magasins des navigateurs lorsque votre organisation le permet, et des durées de session courtes.
Réponse à incident
Lorsque l’analyse ou une alerte confirme un accès aux identifiants, procédez dans cet ordre :
- Contenir d’abord. Isolez l’hôte pour que la collecte s’arrête et que rien d’autre ne sorte ; laissez-le allumé pour les preuves en mémoire, comme dans la leçon sur la persistance.
- Délimiter les comptes exposés. Listez toutes les personnes dont le matériel se trouvait sur l’hôte : ouvertures de session interactives et bureau à distance (Security 4624, types de logon 2, 10 et 11) pendant la fenêtre d’exposition, ouvertures de session de domaine en cache, comptes de service exécutés sur l’hôte, tâches planifiées avec identifiants enregistrés, et pour les stealers, chaque site enregistré dans les navigateurs de l’utilisateur touché et chaque application dont les fichiers de session ont été lus. La fenêtre d’exposition commence à la première exécution, pas à la détection.
- Supprimer le point d’ancrage avant de réinitialiser. Sinon, les nouveaux mots de passe sont volés eux aussi.
- Réinitialiser et révoquer. Réinitialisez les mots de passe des comptes exposés, en commençant par les plus privilégiés ; révoquez les sessions et les jetons de rafraîchissement auprès du fournisseur d’identité ; invalidez les clés d’API et les mots de passe d’application trouvés dans le périmètre ; examinez les méthodes MFA enregistrées pendant la fenêtre. Si des contrôleurs de domaine ou des secrets Kerberos ont été exposés, le matériel de clés du domaine lui-même doit faire l’objet d’une rotation planifiée, ce qui est un projet, pas une case à cocher.
- Chasser la réutilisation. Recherchez dans les journaux d’authentification les comptes exposés depuis de nouveaux hôtes, adresses sources ou pays ; les ouvertures de session avec identifiants explicites (4648) ; les demandes inhabituelles de tickets de service Kerberos ; les nouvelles règles de boîte aux lettres ; et les connexions réussies avec un jeton de session mais sans MFA interactive.
Rapporter le vol d’identifiants
Le rapport de triage liste le vol d’identifiants parmi les capacités. Pour les intervenants, ajoutez une section qui répond à ce qui était atteignable, et pas seulement à ce que le code peut faire :
| Champ | Exemple |
|---|---|
| Comportement et identifiant ATT&CK | Lit les identifiants et cookies du navigateur, T1555.003, T1539 |
| Niveau de preuve | Observé lors d’une exécution en laboratoire / vu dans la télémétrie de l’hôte / code uniquement |
| Cibles | Deux navigateurs basés sur Chromium, un client FTP, des dossiers de portefeuilles |
| Exfiltration | HTTP POST d’un ZIP vers le C2 décrit dans la section réseau |
| Comptes exposés | Utilisateur demo (tous les sites enregistrés) ; aucune ouverture de session privilégiée dans la fenêtre |
| Contrôles présents | Credential Guard actif ; protection LSA activée ; règle ASR en mode audit uniquement |
| Actions | Réinitialisations, révocation des sessions, requêtes de chasse et leur état |
Distinguez bien « peut voler » et « a volé ». Un échantillon doté d’une routine LSASS qui s’est exécutée sur un hôte où la protection LSA était activée, et qui a échoué, constitue un constat différent, avec un périmètre différent, de celui d’un échantillon qui a écrit un fichier de dump.
Lab : détecter l’accès aux identifiants dans des événements de type Sysmon
Dans ce lab, vous générez des événements synthétiques pour un poste de travail, écrivez un script Python de détection doté d’une liste d’autorisation et d’une logique de masques d’accès, et triez les imports d’un programme bénin qui interroge le clavier. Rien ici n’ouvre LSASS, ne lit un magasin de navigateur ni n’enregistre de frappes ; les événements sont des lignes JSON et tous les noms sont inventés.
-
Préparez un dossier de travail et un environnement virtuel. Seul
pefileest nécessaire, pour l’étape 5 :bash mkdir m7t && cd m7t python3 -m venv .venv .venv/bin/pip install -q pefile .venv/bin/python --versiontext Python 3.14.7 -
Générez les événements. Les entrées Sysmon 10 montrent des processus attendus qui ouvrent LSASS ; les entrées Security 4663 montrent des lectures d’un profil de navigateur. L’intrusion simulée est un programme situé dans un dossier temporaire qui fait les deux :
python # make_events.py - writes events.jsonl: synthetic Sysmon/Security-style events for one workstation. # No process is opened and no file is read; this script only writes JSON. import json HOST = "WS-DEMO-11" SYS32 = r"C:\Windows\System32" LSASS = SYS32 + r"\lsass.exe" PROFILE = r"C:\Users\demo\AppData\Local\DemoBrowser\User Data" BROWSER = r"C:\Program Files\DemoBrowser\browser.exe" DEFENDER = r"C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.24090.11-0\MsMpEng.exe" EDR = r"C:\Program Files\DemoEDR\agent.exe" BACKUP = r"C:\Program Files\DemoBackup\backupsvc.exe" ODD = r"C:\Users\demo\AppData\Local\Temp\DemoTool.exe" def ev(t, eid, **f): return {"UtcTime": f"2026-09-29 {t}", "EventID": eid, "Computer": HOST, **f} def access(t, src, mask, trace="ntdll.dll+9d4c4|KERNELBASE.dll+2c0fe"): return ev(t, 10, SourceImage=src, TargetImage=LSASS, GrantedAccess=mask, CallTrace=trace) def read(t, proc, name): # Security 4663 (object access, requires a SACL on the folder); 0x1 = ReadData return ev(t, 4663, ProcessName=proc, ObjectName=PROFILE + "\\" + name, AccessMask="0x1") events = [ # --- expected access to lsass.exe --- access("09:00:01.102", SYS32 + r"\svchost.exe", "0x1000"), access("09:00:04.417", SYS32 + r"\wininit.exe", "0x1fffff"), access("09:02:10.880", DEFENDER, "0x1410"), access("09:02:11.035", EDR, "0x1fffff"), access("09:30:45.219", SYS32 + r"\Taskmgr.exe", "0x1400"), # --- expected reads of browser profile files --- read("09:15:02.334", BROWSER, r"Default\Login Data"), read("09:15:02.341", BROWSER, r"Default\Cookies"), read("11:00:00.512", BACKUP, r"Default\Login Data"), # --- simulated intrusion (names fictional) --- ev("10:22:31.006", 1, Image=ODD, ParentImage=r"C:\Windows\explorer.exe", CommandLine=f'"{ODD}"'), access("10:22:33.781", ODD, "0x1010", trace="ntdll.dll+9d4c4|KERNELBASE.dll+2c0fe|UNKNOWN(00000000004016a2)"), read("10:22:34.120", ODD, r"Default\Login Data"), read("10:22:34.126", ODD, r"Default\Cookies"), read("10:22:34.131", ODD, "Local State"), ev("10:22:34.402", 11, Image=ODD, TargetFilename=r"C:\Users\demo\AppData\Local\Temp\ld.tmp"), ] with open("events.jsonl", "w") as f: for e in events: f.write(json.dumps(e) + "\n") print(f"wrote {len(events)} events")bash .venv/bin/python make_events.py sed -n 10p events.jsonltext wrote 14 events {"UtcTime": "2026-09-29 10:22:33.781", "EventID": 10, "Computer": "WS-DEMO-11", "SourceImage": "C:\\Users\\demo\\AppData\\Local\\Temp\\DemoTool.exe", "TargetImage": "C:\\Windows\\System32\\lsass.exe", "GrantedAccess": "0x1010", "CallTrace": "ntdll.dll+9d4c4|KERNELBASE.dll+2c0fe|UNKNOWN(00000000004016a2)"} -
Écrivez le script de détection. La règle LSASS décode le masque en droits nommés et ignore les accès de requête pure avant de consulter la liste d’autorisation. La règle navigateur ignore le navigateur propriétaire, applique la liste d’autorisation et regroupe les lectures restantes par processus en une seule alerte. Les deux ajoutent l’événement de création de processus lorsqu’il existe :
python # detect_creds.py - flag credential-access patterns in Sysmon/Security-style JSON lines. # Usage: detect_creds.py events.jsonl [--explain] [--no-tuning] import json, re, sys from collections import defaultdict USER_WRITABLE = re.compile(r"\\users\\[^\\]+\\|\\programdata\\|\\windows\\temp\\", re.I) # Process access rights that let the caller read, write or run code in lsass.exe RIGHTS = {0x0002: "CREATE_THREAD", 0x0008: "VM_OPERATION", 0x0010: "VM_READ", 0x0020: "VM_WRITE", 0x0040: "DUP_HANDLE"} # Tuning: reviewed processes that legitimately open lsass.exe with memory rights. ALLOW_LSASS = [re.compile(p, re.I) for p in ( r"^C:\\Windows\\System32\\(wininit|csrss|services|svchost|wmiprvse)\.exe$", r"^C:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\[\d.\-]+\\MsMpEng\.exe$", r"^C:\\Program Files\\DemoEDR\\agent\.exe$", )] # Browser credential stores (generic Chromium-style layout) and their ATT&CK IDs STORES = {r"\\User Data\\[^\\]+\\Login Data$": ("saved logins", "T1555.003"), r"\\User Data\\[^\\]+\\(Network\\)?Cookies$": ("cookies", "T1539"), r"\\User Data\\Local State$": ("key file", "T1555.003")} BROWSER_OWNERS = [re.compile(r"^C:\\Program Files\\DemoBrowser\\browser\.exe$", re.I)] ALLOW_READERS = [re.compile(r"^C:\\Program Files\\DemoBackup\\backupsvc\.exe$", re.I)] def rights(mask): return [n for bit, n in RIGHTS.items() if mask & bit] def matches(rxs, path): return any(r.search(path) for r in rxs) def main(path, explain, tuned): events = [json.loads(l) for l in open(path, encoding="utf-8-sig") if l.strip()] starts = {e["Image"].lower(): e for e in events if e["EventID"] == 1} alerts, ignored = [], [] reads = defaultdict(list) for e in events: if e["EventID"] == 10 and e.get("TargetImage", "").lower().endswith("\\lsass.exe"): src, mask = e["SourceImage"], int(e["GrantedAccess"], 16) got = rights(mask) if not got: ignored.append((e, f"lsass access by {src}: mask {e['GrantedAccess']} is query-only")) elif tuned and matches(ALLOW_LSASS, src): ignored.append((e, f"lsass access by {src}: allow-listed")) else: why = ["+".join(got)] if USER_WRITABLE.search(src): why.append("user-writable path") if "UNKNOWN" in e.get("CallTrace", ""): why.append("CallTrace has UNKNOWN frame") sev = "HIGH" if len(why) > 1 else "MEDIUM" alerts.append((sev, e, "T1003.001", f"{src} opened lsass.exe with {e['GrantedAccess']} ({'; '.join(why)})")) elif e["EventID"] == 4663: obj, proc = e["ObjectName"], e["ProcessName"] hit = next((v for k, v in STORES.items() if re.search(k, obj, re.I)), None) if not hit: continue if matches(BROWSER_OWNERS, proc): ignored.append((e, f"{hit[0]} read by owning browser")) elif tuned and matches(ALLOW_READERS, proc): ignored.append((e, f"{hit[0]} read by allow-listed {proc}")) else: reads[(e["Computer"], proc)].append((e, hit)) for (host, proc), hits in reads.items(): first = hits[0][0] ids = sorted({h[1] for _, h in hits}) kinds = ", ".join(h[0] for _, h in hits) sev = "HIGH" if USER_WRITABLE.search(proc) else "MEDIUM" alerts.append((sev, first, ",".join(ids), f"{proc} read browser {kinds} ({len(hits)} files)")) if explain: for e, why in ignored: print(f"[ignored] {e['UtcTime']} EID {e['EventID']:<4} {why}") for sev, e, tid, msg in sorted(alerts, key=lambda a: a[1]["UtcTime"]): print(f"[{sev:<6}] {e['UtcTime']} {e['Computer']} EID {e['EventID']:<4} {tid} {msg}") img = e.get("SourceImage") or e.get("ProcessName") if img and img.lower() in starts: p = starts[img.lower()] print(f" started {p['UtcTime']} by {p['ParentImage']}") print(f"-- {len(events)} events, {len(alerts)} alerts, {len(ignored)} ignored (tuning {'on' if tuned else 'off'})") if __name__ == "__main__": a = sys.argv[1:] main(a[0], "--explain" in a, "--no-tuning" not in a) -
Exécutez-le avec
--explainpour voir ce qui a été écarté et pourquoi :bash .venv/bin/python detect_creds.py events.jsonl --explaintext [ignored] 2026-09-29 09:00:01.102 EID 10 lsass access by C:\Windows\System32\svchost.exe: mask 0x1000 is query-only [ignored] 2026-09-29 09:00:04.417 EID 10 lsass access by C:\Windows\System32\wininit.exe: allow-listed [ignored] 2026-09-29 09:02:10.880 EID 10 lsass access by C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.24090.11-0\MsMpEng.exe: allow-listed [ignored] 2026-09-29 09:02:11.035 EID 10 lsass access by C:\Program Files\DemoEDR\agent.exe: allow-listed [ignored] 2026-09-29 09:30:45.219 EID 10 lsass access by C:\Windows\System32\Taskmgr.exe: mask 0x1400 is query-only [ignored] 2026-09-29 09:15:02.334 EID 4663 saved logins read by owning browser [ignored] 2026-09-29 09:15:02.341 EID 4663 cookies read by owning browser [ignored] 2026-09-29 11:00:00.512 EID 4663 saved logins read by allow-listed C:\Program Files\DemoBackup\backupsvc.exe [HIGH ] 2026-09-29 10:22:33.781 WS-DEMO-11 EID 10 T1003.001 C:\Users\demo\AppData\Local\Temp\DemoTool.exe opened lsass.exe with 0x1010 (VM_READ; user-writable path; CallTrace has UNKNOWN frame) started 2026-09-29 10:22:31.006 by C:\Windows\explorer.exe [HIGH ] 2026-09-29 10:22:34.120 WS-DEMO-11 EID 4663 T1539,T1555.003 C:\Users\demo\AppData\Local\Temp\DemoTool.exe read browser saved logins, cookies, key file (3 files) started 2026-09-29 10:22:31.006 by C:\Windows\explorer.exe -- 14 events, 2 alerts, 8 ignored (tuning on)Les deux alertes correspondent à l’intrusion, et elles partagent un processus lancé trois secondes plus tôt. Le Gestionnaire des tâches est un cas bénin utile : il ne figure pas dans la liste d’autorisation, mais il est correctement ignoré car
0x1400ne comporte aucun droit mémoire. C’est la vérification du masque, et non la liste d’autorisation, qui fait l’essentiel du travail. -
Voyez ce que fait l’ajustement. Relancez avec les listes d’autorisation désactivées :
bash .venv/bin/python detect_creds.py events.jsonl --no-tuningtext [MEDIUM] 2026-09-29 09:00:04.417 WS-DEMO-11 EID 10 T1003.001 C:\Windows\System32\wininit.exe opened lsass.exe with 0x1fffff (CREATE_THREAD+VM_OPERATION+VM_READ+VM_WRITE+DUP_HANDLE) [HIGH ] 2026-09-29 09:02:10.880 WS-DEMO-11 EID 10 T1003.001 C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.24090.11-0\MsMpEng.exe opened lsass.exe with 0x1410 (VM_READ; user-writable path) [MEDIUM] 2026-09-29 09:02:11.035 WS-DEMO-11 EID 10 T1003.001 C:\Program Files\DemoEDR\agent.exe opened lsass.exe with 0x1fffff (CREATE_THREAD+VM_OPERATION+VM_READ+VM_WRITE+DUP_HANDLE) [HIGH ] 2026-09-29 10:22:33.781 WS-DEMO-11 EID 10 T1003.001 C:\Users\demo\AppData\Local\Temp\DemoTool.exe opened lsass.exe with 0x1010 (VM_READ; user-writable path; CallTrace has UNKNOWN frame) started 2026-09-29 10:22:31.006 by C:\Windows\explorer.exe [HIGH ] 2026-09-29 10:22:34.120 WS-DEMO-11 EID 4663 T1539,T1555.003 C:\Users\demo\AppData\Local\Temp\DemoTool.exe read browser saved logins, cookies, key file (3 files) started 2026-09-29 10:22:31.006 by C:\Windows\explorer.exe [MEDIUM] 2026-09-29 11:00:00.512 WS-DEMO-11 EID 4663 T1555.003 C:\Program Files\DemoBackup\backupsvc.exe read browser saved logins (1 files) -- 14 events, 6 alerts, 4 ignored (tuning off)Remarque sur l’ajustement : Defender se déclenche en HIGH parce que son dossier de plateforme se trouve sous
ProgramData, que la règle considère comme accessible en écriture par l’utilisateur. L’entrée de la liste d’autorisation corrige cela avec un motif de chemin complet qui inclut le dossier de plateforme versionné, mais ce n’est toujours qu’un chemin. En production, joignez chaque événement Sysmon 10 à l’événement Sysmon 1 de la source viaSourceProcessGUIDet vérifiez le signataire, et gardez la vérification de call traceUNKNOWNactive même pour les images autorisées, car du code injecté dans un processus de confiance apparaît exactement de cette façon. -
Triez un programme bénin qui interroge le clavier. Compilez un programme qui se contente de vérifier dix fois si la barre d’espace est enfoncée et affiche la réponse. Il ne stocke et n’envoie rien :
c /* keypoll.c - benign input-polling demo: reports whether the space bar is held, ten times, then exits. It records nothing, stores nothing and sends nothing. */ #include <windows.h> #include <stdio.h> int main(void) { for (int i = 0; i < 10; i++) { SHORT s = GetAsyncKeyState(VK_SPACE); printf("poll %d: space %s\n", i, (s & 0x8000) ? "down" : "up"); Sleep(200); } return 0; }bash x86_64-w64-mingw32-gcc -O2 -o keypoll.exe keypoll.cÉtendez
capimports.pyde Lire les capacités à partir des imports avec une catégorie saisie et écran, faites correspondre le motifregistryàRegSaveKeyEx, et affichez le nombre de DLL. Seules les lignes modifiées sont montrées ; la nouvelle catégorie se place aprèsresources:python "registry": r"^Reg(Open|Create|Set|Query|Delete|Enum|Save)", "input/screen": r"^(GetAsyncKeyState|GetKeyState|GetKeyboardState|SetWindowsHookEx|CallNextHookEx|GetRawInputData|RegisterRawInputDevices|GetClipboardData|OpenClipboard|AddClipboardFormatListener|BitBlt|GetDC|GetForegroundWindow|GetWindowText)", print(f"{sys.argv[1]}: {total} imports from {len(pe.DIRECTORY_ENTRY_IMPORT)} DLLs")bash .venv/bin/python capimports.py keypoll.exetext keypoll.exe: 42 imports from 10 DLLs memory VirtualProtect (kernel32.dll) input/screen GetAsyncKeyState (user32.dll)La catégorie de saisie s’allume sur un import de
user32.dll, et le seul autre résultat est leVirtualProtectdu runtime, déjà rencontré dans Lire les capacités à partir des imports. Il n’y a ni hook, ni API de titre de fenêtre, ni catégorie écriture de fichier ou réseau. Formulé comme une hypothèse : « interroge l’état du clavier ; aucune preuve de journalisation, de contexte de fenêtre ni d’exfiltration ». C’est ainsi qu’il faut rapporter un import de saisie isolé. La même ligne dans la table des imports d’un stealer, à côté deGetForegroundWindow, d’ajouts à un fichier et de WinINet, se lirait très différemment.
Questions : Quelles valeurs de masque d’accès de vos propres données Sysmon
tombent dans le groupe « requête uniquement », et quels processus auraient besoin
d’entrées dans la liste d’autorisation ? Le script ignore l’événement Sysmon 11
qui a écrit ld.tmp ; quelle règle l’utiliserait, et pourquoi une copie d’une
base de navigateur dans un dossier temporaire compte-t-elle ? Si WS-DEMO-11
avait eu Credential Guard et la protection LSA activés, comment cela
changerait-il la ligne « comptes exposés » de votre rapport, et comment le
confirmeriez-vous depuis l’hôte ? Quels imports devraient rejoindre
GetAsyncKeyState avant que vous ne qualifiiez keypoll.exe de keylogger ?
À retenir
- Le vol d’identifiants transforme un incident d’hôte en incident d’identité : le périmètre suit les comptes exposés, et les jetons et cookies doivent être révoqués en plus de la réinitialisation des mots de passe.
- Les stealers révèlent, une fois les chaînes décodées, des chemins cibles, des noms de fichiers de magasins et des appels DPAPI ; l’accès à LSASS apparaît sous forme de handles de processus dotés de droits mémoire ; les keyloggers apparaissent sous forme d’API de saisie combinées au contexte de fenêtre, à la journalisation et à l’exfiltration.
- Détectez l’accès à LSASS avec Sysmon 10 en testant les bits du masque d’accès,
pas des valeurs littérales, et surveillez les frames
UNKNOWNdansCallTrace; auditez les lectures des magasins de navigateur avec des SACL et les événements fichier de l’EDR. - Ajustez avec des exceptions fondées sur le chemin complet et le signataire, jamais sur les noms de fichiers, et laissez la logique de masque écarter les accès de requête pure avant que la liste d’autorisation ne le fasse.
- Vérifiez Credential Guard, la protection LSA, la règle ASR pour LSASS, WDigest, la MFA et le moindre privilège comme des faits de configuration consignés pour chaque hôte touché.
- Contenez, délimitez les comptes exposés, supprimez le point d’ancrage, puis réinitialisez, révoquez et chassez la réutilisation. Les leçons suivantes de ce module, la future Comprendre l’injection de processus et Hooking et rootkits en mode utilisateur, examinent comment le code à l’origine de ces comportements se cache dans d’autres processus.