Skip to content

Leçon 5.4 · Windows pour analystes· 35 min

Mutex, événements et communication inter-processus

Mutex, événements, sections et pipes nommés comme preuves sur l’hôte : où ils vivent, comment les observer, les extraire et en tirer des détections.

Objectifs

  • Nommer les types d’objets noyau que les malwares créent avec un nom et expliquer les espaces de noms Global\, Local\ et par session
  • Expliquer pourquoi un mutex d’instance unique est un IOC solide, quand la vaccination par mutex fonctionne et quand elle échoue
  • Observer les objets nommés avec System Informer, Process Explorer, WinObj et Sysmon, et savoir quels outils ne les voient pas
  • Retrouver statiquement le nom d’un mutex à partir des chaînes UTF-16 et des arguments de l’appel à CreateMutex
  • Transformer les objets nommés en entrées d’IOC, en chaînes YARA et en requêtes de chasse aux pipes nommés

Dans Processus, threads et DLL, vous avez lu la table des handles d’un processus et croisé au passage un type d’entrée : un Mutant doté d’un nom. De nombreux échantillons créent des objets noyau nommés par leur auteur — un mutex pour qu’une seule copie s’exécute, un événement qui indique à un thread injecté quand démarrer, un pipe par lequel un processus auxiliaire parle à son parent. Ces noms sont choisis par un humain, changent rarement d’un build à l’autre, et aucun programme légitime n’a de raison de choisir le même : ce sont parmi les IOC (indicateurs de compromission) de haute confiance les moins coûteux qu’un échantillon vous livre.

Objets noyau nommés

Tout objet noyau est atteint par un handle. Certains types d’objets peuvent aussi recevoir un nom à leur création, de sorte qu’un second processus puisse ouvrir le même objet par son nom au lieu d’hériter d’un handle ou de le dupliquer.

ObjetNom du type noyauCréé avecUsage par les malwares
MutexMutantCreateMutexA/W, CreateMutexExWMarqueur d’instance unique ; sérialisation de l’accès à un fichier ou à un journal partagé
ÉvénementEventCreateEventA/WSignalisation entre composants : « la charge utile est mappée, démarre », « arrête-toi maintenant »
SémaphoreSemaphoreCreateSemaphoreA/WLimitation du nombre de workers simultanés ; parfois utilisé comme marqueur d’instance à la place d’un mutex
SectionSectionCreateFileMappingA/WMémoire partagée entre processus, y compris pour préparer du code à injecter
Pipe nomméFile (sur le pilote NPFS)CreateNamedPipeA/W, ouvert avec CreateFileWCanal bidirectionnel entre processus, en local ou via SMB
MailslotFile (sur le pilote MSFS)CreateMailslotA/WDatagrammes unidirectionnels, en local ou en diffusion sur le domaine ; rare aujourd’hui

Un mutex a au plus un thread propriétaire, et un thread qui demande un mutex déjà détenu attend qu’il soit libéré. Pour la plupart des malwares (logiciels malveillants), la propriété importe peu : ce qui compte, c’est que le nom existe. CreateMutexW renvoie un handle, qu’il ait créé un nouvel objet ou ouvert un objet existant, et dans le second cas GetLastError renvoie ERROR_ALREADY_EXISTS (183, 0xB7). Ce seul bit d’information constitue toute la vérification d’instance unique.

L’espace de noms des objets

Les objets nommés vivent dans une arborescence gérée par le gestionnaire d’objets, à la manière d’un système de fichiers. Les répertoires qui nous intéressent ici :

text
\BaseNamedObjects                         global namespace ("Global\" prefix; session 0's local names)
\Sessions\1\BaseNamedObjects              local namespace for interactive session 1 ("Local\" prefix)
\Sessions\1\AppContainerNamedObjects\<SID> objects created inside an AppContainer sandbox
\Device\NamedPipe\                        named pipes, opened as \\.\pipe\<name>
\Device\Mailslot\                         mailslots, opened as \\.\mailslot\<name>

Le préfixe Win32 détermine où un nom aboutit :

  • Global\name est créé dans \BaseNamedObjects et reste visible depuis toutes les sessions. Un échantillon qui veut une seule instance par machine, et non par utilisateur connecté, l’utilise. Créer un file mapping à cet endroit depuis une autre session que la 0 exige SeCreateGlobalPrivilege ; ce n’est pas le cas des mutex et des événements.
  • Local\name, ou un nom sans préfixe, va dans le répertoire de la session de l’appelant. Deux utilisateurs connectés en même temps obtiennent chacun leur propre copie — un mutex par session n’empêche donc pas une seconde infection dans une autre session.
  • Les services s’exécutent dans la session 0, où l’espace de noms local est \BaseNamedObjects. Le même nom Local\ se résout donc vers un chemin complet différent selon que l’échantillon tourne en tant que service ou qu’un utilisateur double-clique dessus. Consignez le nom que le code transmet, et notez le chemin complet que vous avez observé.

Les pipes nommés et les mailslots échappent à ce schéma : ce sont des fichiers sur des pilotes de système de fichiers dédiés, avec un espace de noms unique pour toute la machine. Un nom de pipe est joignable depuis d’autres hôtes sous la forme \\host\pipe\name via SMB, à travers le partage IPC$, ce qui rend les pipes intéressants pour le mouvement latéral.

Pourquoi le nom du mutex est un si bon IOC

Un auteur choisit un nom de mutex une fois pour toutes et le réutilise, car le changer laisserait deux versions tourner côte à côte et se disputer les mêmes fichiers et les mêmes ports. Des noms comme Local\LabDemoMutex n’apparaissent pas par hasard : l’indicateur combine donc forte spécificité et faible renouvellement. Un hash de fichier meurt à la recompilation suivante ; un nom de mutex survit souvent au repacking, au rechiffrement et à une nouvelle infrastructure de C2, parce qu’il vit dans le code qui s’exécute après toutes ces couches.

La même propriété permet la vaccination : créez vous-même le mutex sur les postes, avant l’arrivée de l’échantillon, et un échantillon qui le vérifie en conclura qu’il tourne déjà et se terminera. Cette méthode a servi lors d’incidents réels comme solution d’attente pendant le déploiement d’une vraie détection. Ses limites sont strictes :

  • Elle ne fonctionne que pour le nom exact et l’espace de noms exact. Un vaccin Local\ ne protège pas les autres sessions ; un échantillon Global\ ignore un vaccin placé dans \Sessions\1\BaseNamedObjects.
  • Elle ne fonctionne que contre les échantillons qui se terminent (ou se bloquent) sur ERROR_ALREADY_EXISTS ; certains ignorent le résultat.
  • Le vaccin doit être maintenu ouvert par un processus en cours d’exécution ; il disparaît au redémarrage jusqu’à ce que ce processus soit relancé.
  • Elle ne peut rien contre les noms aléatoires ou dérivés de l’hôte.

Ce dernier point est la principale faiblesse de toute cette catégorie d’indicateurs. Les auteurs qui savent que les défenseurs collectent les mutex dérivent le nom à l’exécution : un hash du nom de l’ordinateur, du numéro de série du volume ou du SID de l’utilisateur, mis en forme comme un GUID ou une chaîne hexadécimale. Le résultat est stable sur une machine (la vérification d’instance unique fonctionne donc toujours) et différent sur toutes les autres. Pour ces familles, le nom littéral n’est un IOC d’hôte que pour cet incident ; l’indicateur durable, c’est l’algorithme — la chaîne de format, le hash, les entrées —, que vous retrouvez par rétro-ingénierie et pouvez reproduire dans un script de détection.

Astuce : Avant de signaler un nom de mutex comme IOC de famille, vérifiez-le sur deux exécutions dans des VM portant des noms différents. S’il change, signalez le motif (par exemple « 32 chiffres hexadécimaux dérivés du numéro de série du volume ») plutôt que la valeur.

Observer les objets nommés à l’exécution

Les mutex et les événements ne laissent ni fichier ni valeur de registre, si bien que les outils de Surveillance comportementale les voient de manière inégale.

OutilMutex / événements / sectionsPipes nommés
System Informer — onglet HandlesOui, tant que le handle est ouvert, avec le chemin complet de l’objetOui, sous forme de handles File sur \Device\NamedPipe\…
Process Explorer — vue des handles (Ctrl+H), Find (Ctrl+F)Oui ; Find Handle or DLL recherche un nom dans tous les processusOui
WinObjOui — parcourt l’espace de noms lui-même, indépendamment de tout processusPas la liste des pipes elle-même
Handle (CLI Sysinternals)handle -a -p <pid>, ou handle -a <name> pour rechercherOui
SysmonAucun événement pour la création de mutex ou d’événementsEvent 17 (pipe créé), Event 18 (pipe connecté)
Process MonitorNon enregistréCreateFile, ReadFile, WriteFile sur le chemin du pipe
Sandboxes (bacs à sable ; CAPE et similaires)Généralement listés dans le résumé comportemental, grâce aux hooks d’APIGénéralement listés

Deux conséquences en découlent. D’abord, les vues en direct ne montrent que ce qui est encore ouvert : un échantillon qui constate que son mutex existe déjà et se termine ne laisse rien à Process Explorer. Le traçage à base de hooks — un rapport de sandbox, un moniteur d’API ou un point d’arrêt sur CreateMutexW dans un débogueur — capte chaque tentative, y compris celles qui échouent. Ensuite, la télémétrie de production contient rarement des mutex. Sysmon n’a pas d’événement pour les mutex, et la plupart des EDR ne remontent pas la création d’objets. Sur un hôte en production, on les chasse par énumération : une session de réponse à incident exécutant handle.exe, un artefact Velociraptor qui parcourt les répertoires d’objets, ou une image mémoire analysée après coup. Les pipes nommés font exception, car Sysmon et la plupart des EDR les journalisent.

Trouver le nom statiquement

Un échantillon qui utilise un nom fixe doit le transporter quelque part dans le binaire. Où chercher :

  1. Les imports. CreateMutexA/W, OpenMutexW, CreateEventW, CreateNamedPipeW ou CreateFileMappingW dans la table des imports (voir Lire les capacités à partir des imports) indiquent qu’un objet nommé est probable. Associé à un GetLastError peu après, il s’agit presque certainement d’une vérification d’instance.
  2. Les chaînes larges. Les API W prennent des chaînes UTF-16LE, qu’une exécution par défaut de strings manque complètement. Cherchez avec strings -el (ou FLOSS, présenté dans Chaînes et chaînes obfusquées).
  3. Le site d’appel. En x64, le nom est le troisième argument de CreateMutexW (lpName) et arrive donc dans r8 ; pour OpenMutexW, c’est aussi le troisième ; pour CreateEventW, c’est le quatrième, dans r9. Trouvez l’appel via l’entrée de l’IAT, puis lisez le lea qui charge le registre d’argument (voir les conventions d’appel).
  4. Les noms cachés. Quand aucune chaîne n’apparaît, le nom est construit à l’exécution — stack strings, blob chiffré par XOR, ou chaîne de format alimentée par des données de l’hôte. Le site d’appel vous donne quand même la réponse : posez un point d’arrêt sur l’API et lisez l’argument, une méthode que la leçon Déboguer un malware avec x64dbg couvre en détail.

Les instructions qui suivent immédiatement l’appel méritent aussi d’être lues. Une comparaison du résultat de GetLastError avec 0xB7, suivie d’un saut vers un chemin de sortie, est la vérification d’instance unique dans sa forme la plus pure — et l’endroit exact qu’un analyste patche pour laisser tourner une seconde copie sous un débogueur.

Les pipes nommés et les autres IPC comme indicateurs

Un échantillon qui crée un pipe vous dit généralement qu’il est plus qu’un seul processus : un loader et sa charge utile, un implant et une tâche de post-exploitation qu’il a lancée, ou un service et l’opérateur distant qui le pilote via SMB. Les pipes sont donc un indice structurel autant qu’un IOC.

Ce que les défenseurs journalisent et chassent couramment :

  • Les Events Sysmon 17 et 18. L’Event 17 enregistre le processus qui a créé un pipe ainsi que son nom ; l’Event 18 enregistre qui s’y est connecté. La chasse commence par les noms de pipes qui correspondent aux valeurs par défaut d’outils connus — outils d’administration à distance comme PsExec (\PSEXESVC) et motifs par défaut publiés des frameworks de red team courants —, puis passe aux pipes créés par des processus qui n’ont rien à faire à servir de l’IPC, comme rundll32.exe ou un binaire dans %TEMP%.
  • L’accès distant à IPC$. L’événement de sécurité Windows 5145 (audit détaillé des partages de fichiers) montre un client distant ouvrant un nom de pipe via IPC$, ce qui est la manière dont le mouvement latéral via SMB et les implants pair-à-pair apparaissent sur l’hôte qui les reçoit.
  • La forme plutôt que la valeur. Les valeurs par défaut des frameworks sont configurables, si bien que les chasses matures recherchent des motifs : noms d’apparence aléatoire sous un préfixe fixe, pipes qui n’existent que quelques secondes autour d’une création de processus, processus unique créant de nombreux pipes aux noms similaires.

Les sections et les événements sont moins visibles dans les journaux. Une section nommée est de la mémoire partagée, et une vue exécutable de celle-ci dans un autre processus est une primitive d’injection classique (voir section mapping injection et la future leçon Comprendre l’injection de processus). Un événement nommé qu’un processus attend explique pourquoi un composant injecté reste inactif.

Rapport et contenu de détection

Consignez chaque objet nommé comme une entrée d’IOC, avec assez de contexte pour que quelqu’un d’autre puisse l’utiliser correctement :

ChampExemple
TypeMutex
Valeur telle que transmise à l’APILocal\LabDemoMutex
Chemin complet observé\Sessions\1\BaseNamedObjects\LabDemoMutex
StabilitéChaîne statique dans .rdata ; identique d’une exécution et d’un hôte à l’autre
Comportement en cas de collisionSe termine si ERROR_ALREADY_EXISTS
Encodage dans le fichierUTF-16LE

Les mêmes faits alimentent trois types de contenu de détection. Pour YARA (voir Écrire vos premières règles YARA), faites correspondre le nom en wide — et aussi en ascii si vous n’êtes pas sûr de l’API qu’utilise la famille :

text
rule LabDemo_Mutex_Name
{
    meta:
        description = "Lab sample: LabDemoMutex single-instance marker"
    strings:
        $mutex = "LabDemoMutex" wide ascii
    condition:
        uint16(0) == 0x5A4D and $mutex
}

Laissez le préfixe Local\ ou Global\ hors de la chaîne : il ne fait pas partie du nom de l’objet et certaines variantes le changent. Pour le contenu côté endpoint, un pipe nommé devient une requête sur l’Event Sysmon 17 ; en voici une ébauche en syntaxe de type KQL :

text
Sysmon
| where EventID in (17, 18)
| where PipeName has_any ("\\PSEXESVC", "\\LabDemoPipe")
    or (PipeName matches regex @"^\\[0-9a-f]{8}$" and Image endswith @"\rundll32.exe")
| project TimeGenerated, Computer, EventID, PipeName, Image, ProcessGuid

Pour les mutex, fournissez au SOC une vérification en réponse à incident plutôt qu’une règle de flux : un script qui tente OpenMutexW sur le nom, ou un balayage avec handle.exe -a LabDemoMutex, et précisez-le dans le rapport de triage.

Lab : un mutex d’instance unique, observé et extrait

Ce lab utilise un programme inoffensif que vous compilez vous-même. Il crée Local\LabDemoMutex, indique si celui-ci existait déjà, le conserve dix secondes puis se termine. Les étapes statiques fonctionnent sur toute machine disposant de mingw-w64 et de Python ; les étapes d’exécution utilisent Wine si vous l’avez, ainsi que votre VM d’analyse Windows issue de Construire un laboratoire d’analyse sûr.

  1. Compilez le programme.

    c
    // mutexdemo.c — single-instance marker with a named mutex (harmless)
    #include <windows.h>
    #include <stdio.h>
    
    int main(void) {
        HANDLE m = CreateMutexW(NULL, FALSE, L"Local\\LabDemoMutex");
        if (m == NULL) {
            printf("CreateMutexW failed: %lu\n", GetLastError());
            return 1;
        }
        if (GetLastError() == ERROR_ALREADY_EXISTS) {
            printf("pid %lu: mutex already exists, another copy is running - exiting\n",
                   GetCurrentProcessId());
            CloseHandle(m);
            return 0;
        }
        printf("pid %lu: created Local\\LabDemoMutex, holding it for 10 seconds\n",
               GetCurrentProcessId());
        Sleep(10000);
        CloseHandle(m);
        printf("pid %lu: released mutex, exiting\n", GetCurrentProcessId());
        return 0;
    }
    bash
    x86_64-w64-mingw32-gcc -O1 -s -o mutexdemo.exe mutexdemo.c
    file mutexdemo.exe
    shasum -a 256 mutexdemo.exe

    Sur la machine de build utilisée pour cette leçon (GCC 15.2 ; votre hash sera différent) :

    text
    mutexdemo.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
    ea6cec36447c9069c1c207f3dd1a204f1bc8bdac9acc871e011d3fd34a2510e0  mutexdemo.exe
  2. Montrez le comportement d’instance unique. Sous Wine, lancez une copie en arrière-plan et une seconde quelques secondes plus tard :

    bash
    export WINEDEBUG=-all
    wine mutexdemo.exe > run1.txt 2>&1 &
    sleep 4; wine mutexdemo.exe > run2.txt 2>&1
    sleep 9; cat run1.txt run2.txt
    text
    pid 32: created Local\LabDemoMutex, holding it for 10 seconds
    pid 32: released mutex, exiting
    pid 200: mutex already exists, another copy is running - exiting

    Wine n’est pas Windows ; il montre la logique, et les étapes dans la VM montrent le véritable espace de noms.

  3. Lisez les imports et les chaînes.

    bash
    x86_64-w64-mingw32-objdump -p mutexdemo.exe | grep -iE 'Mutex|GetLastError|CloseHandle'
    strings -n 8 mutexdemo.exe | grep -i mutex
    x86_64-w64-mingw32-strings -el -n 6 mutexdemo.exe
    text
    	00008270  <none>  0097  CloseHandle
    	00008278  <none>  00ef  CreateMutexW
    	00008298  <none>  0288  GetLastError
    pid %lu: created Local\LabDemoMutex, holding it for 10 seconds
    CreateMutexW
    Local\LabDemoMutex

    La recherche ASCII ne trouve le nom qu’à l’intérieur d’une chaîne de format de printf — un accident propre à ce programme. Le nom réellement transmis à l’API n’est trouvé que par la recherche UTF-16LE (-el). Dans un dump hexadécimal, il ressemble à ceci :

    text
    00002200: 4c00 6f00 6300 6100 6c00 5c00 4c00 6100  L.o.c.a.l.\.L.a.
    00002210: 6200 4400 6500 6d00 6f00 4d00 7500 7400  b.D.e.m.o.M.u.t.
    00002220: 6500 7800 0000 4372 6561 7465 4d75 7465  e.x...CreateMute
  4. Localisez l’appel avec Capstone. Dans un environnement virtuel avec pip install pefile capstone, enregistrez ce script sous find_mutex.py :

    python
    # find_mutex.py — locate a UTF-16LE mutex name and the CreateMutexW call site
    import sys, pefile
    from capstone import Cs, CS_ARCH_X86, CS_MODE_64
    
    pe = pefile.PE(sys.argv[1])
    base = pe.OPTIONAL_HEADER.ImageBase
    data = pe.get_memory_mapped_image()
    
    name = "Local\\LabDemoMutex".encode("utf-16le")
    str_rva = data.find(name)
    print(f"UTF-16LE name at VA {base + str_rva:#x}")
    
    iat = {imp.name.decode(): imp.address
           for entry in pe.DIRECTORY_ENTRY_IMPORT for imp in entry.imports if imp.name}
    target = iat["CreateMutexW"]
    print(f"IAT slot for CreateMutexW at {target:#x}")
    
    text = next(s for s in pe.sections if s.Name.startswith(b".text"))
    md = Cs(CS_ARCH_X86, CS_MODE_64)
    insns = list(md.disasm(text.get_data(), base + text.VirtualAddress))
    
    def rip_target(i):
        # resolve [rip + disp] operands to an absolute address
        if "rip" not in i.op_str:
            return None
        disp = int(i.op_str.split("rip")[1].strip(" +]-").split("]")[0], 16)
        sign = -1 if "rip -" in i.op_str else 1
        return i.address + i.size + sign * disp
    
    # the call may go through a small "jmp [rip+x]" thunk; collect thunks first
    thunks = {i.address for i in insns if i.mnemonic == "jmp" and rip_target(i) == target}
    for idx, i in enumerate(insns):
        t = rip_target(i)
        hit_call = i.mnemonic == "call" and (t == target or
                   (i.op_str.startswith("0x") and int(i.op_str, 16) in thunks))
        if hit_call:
            for j in insns[max(0, idx - 6): idx + 7]:
                note = ""
                if rip_target(j) == base + str_rva:
                    note = "   ; -> L\"Local\\LabDemoMutex\""
                if j is i:
                    note = "   ; CreateMutexW"
                print(f"  {j.address:#x}: {j.mnemonic:6} {j.op_str}{note}")
    bash
    python find_mutex.py mutexdemo.exe
    text
    UTF-16LE name at VA 0x140004000
    IAT slot for CreateMutexW at 0x140008278
      0x140001491: push   rbx
      0x140001492: sub    rsp, 0x28
      0x140001496: call   0x140001620
      0x14000149b: lea    r8, [rip + 0x2b5e]   ; -> L"Local\LabDemoMutex"
      0x1400014a2: mov    edx, 0
      0x1400014a7: mov    ecx, 0
      0x1400014ac: call   qword ptr [rip + 0x6dc6]   ; CreateMutexW
      0x1400014b2: mov    rbx, rax
      0x1400014b5: test   rax, rax
      0x1400014b8: je     0x14000150e
      0x1400014ba: call   qword ptr [rip + 0x6dd8]
      0x1400014c0: cmp    eax, 0xb7
      0x1400014c5: je     0x140001529

    Lisez-le comme le dicte la convention d’appel : ecx = 0 correspond à lpMutexAttributes = NULL, edx = 0 à bInitialOwner = FALSE, et r8 pointe vers le nom. Le second appel indirect se résout en 0x140008298, l’entrée de GetLastError vue à l’étape 3, et cmp eax, 0xb7 est le test de ERROR_ALREADY_EXISTS. Le je à 0x1400014c5 est le branchement qu’un utilisateur de débogueur inverserait pour laisser tourner une seconde copie.

  5. Vérifiez votre chaîne YARA. Compilez la règle de la section précédente et exécutez-la sur mutexdemo.exe (avec yara ou yara-python). Elle correspond deux fois : le nom UTF-16LE à l’offset de fichier 0x220c et le texte ASCII à l’intérieur de la chaîne de format à 0x229f. Décidez à laquelle des deux vous feriez confiance dans une règle visant une famille qui n’affiche pas le nom de son mutex.

  6. Observez-le sous Windows. Dans la VM d’analyse, lancez mutexdemo.exe depuis une invite de commandes et, dans les dix secondes, ouvrez System Informer → mutexdemo.exe → Handles. Trouvez le handle de type Mutant nommé \Sessions\<n>\BaseNamedObjects\LabDemoMutex, où <n> est votre numéro de session. Modifiez le source pour conserver le mutex plus longtemps s’il vous faut plus de temps.

  7. Cherchez depuis l’autre côté. Dans Process Explorer, appuyez sur Ctrl+F et recherchez LabDemoMutex ; le résultat nomme le processus propriétaire. Depuis une invite élevée, handle.exe -a LabDemoMutex fait la même chose en ligne de commande.

  8. Parcourez l’espace de noms. Ouvrez WinObj en tant qu’administrateur et allez dans \Sessions\<n>\BaseNamedObjects. Trouvez LabDemoMutex de type Mutant. Une fois le programme terminé, actualisez : l’objet a disparu, car son dernier handle a été fermé.

  9. Testez la frontière de l’espace de noms. Connectez un second utilisateur avec le changement rapide d’utilisateur pour que deux sessions existent. Avec le build Local\, lancez une copie dans chaque session dans l’intervalle de dix secondes et constatez que les deux créent le mutex. Remplacez le nom par Global\LabDemoMutex, recompilez et recommencez. Si la seconde copie affiche CreateMutexW failed: 5, l’objet existe mais sa sécurité par défaut, définie par le jeton de l’autre utilisateur, refuse l’accès. Réfléchissez à ce que chaque résultat signifie pour un vaccin déployé par un seul compte.

Questions : Quelle recherche de chaînes a trouvé le nom qui compte, et pourquoi la simple recherche ASCII vous a-t-elle induit en erreur ? Où l’objet apparaîtrait-il dans WinObj si le programme s’exécutait en tant que service ? Si le nom était construit à partir du nom de l’ordinateur avec wsprintfW, que signaleriez-vous comme IOC et comment un SOC le vérifierait-il sur un hôte en production ? Lesquels des outils de cette leçon n’auraient rien vu si la seconde copie avait été la seule que vous observiez ?

À retenir

  • Les mutex, événements, sémaphores et sections peuvent être nommés pour que des processus sans lien retrouvent le même objet ; le préfixe (Global\ ou Local\) et la session déterminent où le nom aboutit dans l’espace de noms des objets.
  • Un nom de mutex fixe est spécifique et durable, ce qui en fait un IOC solide et parfois un vaccin — mais seulement pour le nom, l’espace de noms et le comportement de sortie exacts, et jamais pour des noms aléatoires ou dérivés de l’hôte.
  • Les vues des handles en direct et WinObj ne voient les objets que tant qu’un handle est ouvert ; Sysmon enregistre les pipes nommés (Events 17 et 18) mais pas les mutex, si bien que la chasse aux mutex repose sur les rapports de sandbox et l’énumération en direct.
  • Statiquement, cherchez les chaînes larges et lisez le site d’appel de CreateMutexW : le nom est dans r8, et un cmp eax, 0xb7 après GetLastError est la vérification d’instance unique.
  • Les pipes nommés signalent un outillage multi-processus et du mouvement latéral ; chassez sur les valeurs par défaut connues, les créateurs suspects et les motifs de noms plutôt que sur des valeurs isolées.