Leçon 10.3 · Au-delà de l'EXE· 45 min
Malwares PowerShell, JavaScript et VBScript
Désobfusquer PowerShell, JavaScript et VBScript malveillants sans les lancer : neutraliser eval, décoder les couches, lire les journaux, extraire les IOC.
Objectifs
- Nommer les hôtes Windows qui exécutent les malwares de script (powershell.exe, wscript.exe, cscript.exe, mshta.exe) et les voies par lesquelles ces scripts arrivent
- Reconnaître les familles d’obfuscation courantes : concaténation, codes de caractères, Base64 et -EncodedCommand, chaînes de format, bruit d’échappement et eval en couches
- Éplucher un script obfusqué couche par couche en remplaçant son point d’exécution par un affichage, sans exécuter la charge utile
- Expliquer ce que la journalisation des blocs de script (événement 4104), la journalisation des modules, AMSI et les lignes de commande Sysmon apportent à un défenseur
- Extraire d’un script décodé les URL, les chemins et les hashs des étapes suivantes, et les consigner dans un rapport
Tous les échantillons ne sont pas des fichiers PE. Une large part des malwares
(logiciels malveillants) de première étape tient en quelques kilo-octets de
texte : un fichier .js dans une pièce jointe ZIP, un .vbs déposé par un
raccourci, un .hta récupéré via un lien, ou une unique ligne de commande
powershell.exe capturée par un EDR quelques secondes avant l’arrivée de la
vraie charge utile. Il n’y a aucun en-tête à analyser et rien à désassembler. Ce
qui vous sépare de la réponse, c’est l’obfuscation, souvent en plusieurs
couches.
Les scripts sont aussi les échantillons les plus faciles à exécuter par
accident. Double-cliquer sur un fichier .js sous Windows n’ouvre pas un
éditeur : cela l’exécute. Cette leçon adopte le point de vue d’un analyste à qui
l’on remet un tel script et qui doit déterminer ce qu’il fait, quelle
infrastructure il contacte et ce qu’il dépose, sans le laisser s’exécuter sur une
vraie machine.
Où l’on rencontre les malwares de script
Chaque machine Windows est livrée avec des interpréteurs de script, les scripts se modifient trivialement d’une campagne à l’autre, et ils peuvent récupérer et lancer l’étape suivante sans binaire compilé. Points d’entrée typiques :
- Pièces jointes d’e-mails : des fichiers
.js,.vbs,.wsfou.hta, généralement dans une archive ZIP, ISO ou protégée par mot de passe pour passer les filtres de messagerie. - Fichiers de raccourci (
.lnk) dont la cible estpowershell.exeoumshta.exeavec une longue chaîne d’arguments. - Macros Office qui ne font guère plus que lancer PowerShell avec une commande encodée. La prochaine leçon, Documents et archives malveillants, couvre le versant document.
- Fausses pages de mise à jour et de CAPTCHA qui demandent à la victime de coller une commande dans la boîte de dialogue Exécuter ou dans un terminal.
- Post-exploitation : les attaquants déjà présents sur un réseau utilisent PowerShell pour la reconnaissance, le mouvement latéral et le chargement d’outils directement en mémoire.
Dans la plupart de ces cas, le script est un downloader : il récupère une deuxième étape (un EXE, une DLL, du shellcode ou un autre script) et l’exécute. Votre travail consiste à trouver ce qu’il récupère, d’où, et comment il l’exécute.
Les hôtes qui les exécutent
| Hôte | Exécute | Remarques |
|---|---|---|
powershell.exe / pwsh.exe | .ps1, -Command en ligne, -EncodedCommand | Accès complet à .NET ; Windows PowerShell 5.1 est intégré, PowerShell 7 est pwsh.exe |
wscript.exe | .js, .jse, .vbs, .vbe, .wsf | Windows Script Host, version graphique ; le gestionnaire par défaut quand un utilisateur double-clique |
cscript.exe | Comme wscript.exe | Version console de Windows Script Host ; la sortie va dans le terminal |
mshta.exe | .hta, URL vbscript: ou javascript: en ligne | Exécute une application HTML avec tous les privilèges locaux, hors de la sandbox (bac à sable) du navigateur |
cmd.exe | .bat, .cmd | Surtout utilisé comme liant pour lancer l’un des hôtes ci-dessus |
Deux choses surprennent les débutants. D’abord, un fichier .js sous Windows
Script Host est du JScript, l’ancien dialecte JavaScript de Microsoft, et
non du JavaScript de navigateur ou de Node.js : il n’y a ni document ni
console, mais il y a WScript.Shell pour exécuter des commandes et
ActiveXObject pour les téléchargements HTTP et l’accès aux fichiers. Ensuite,
.jse et .vbe sont des scripts « encodés », selon un schéma Microsoft trivial
que des décodeurs publics inversent instantanément ; ils ne sont pas chiffrés.
Microsoft a annoncé l’obsolescence de VBScript, mais il reste présent sur la
plupart des systèmes que vous examinerez.
Le processus parent compte autant que le fichier : un winword.exe qui lance
powershell.exe constitue une détection à lui seul. Processus, threads et
DLL explique l’arbre des processus.
Familles d’obfuscation
L’obfuscation de script poursuit un seul but : faire en sorte qu’aucun scanner
et aucun humain ne voie les chaînes intéressantes (http://, DownloadString,
Invoke-Expression, WScript.Shell) dans le fichier tel qu’il est livré. Les
techniques sont peu nombreuses et elles se cumulent.
| Famille | Exemple PowerShell | Exemple JavaScript / VBScript |
|---|---|---|
| Concaténation et découpage | 'Down' + 'loadStr' + 'ing' | "WScr" + "ipt.Sh" + "ell" |
| Codes de caractères | [char]73 + [char]69 + [char]88 | String.fromCharCode(101,118,97,108), Chr(69) & Chr(120) |
| Base64 | [Convert]::FromBase64String(...), -EncodedCommand | atob(...) dans les navigateurs ; décodeurs maison en JScript |
| Réordonnancement par chaîne de format | "{2}{0}{1}" -f 'ad','String','Downlo' | accès à un tableau par indices mélangés : _0x3a[4] + _0x3a[1] |
| Bruit d’échappement et de casse | I`nv`oKe-ExPrEsSiOn (les accents graves sont ignorés) | c^m^d dans cmd.exe (les carets sont ignorés) ; casse aléatoire en VBScript |
| Renommage | $qZx, $____, ${ } | _0x4f2a, lettres isolées, sosies Unicode |
| Inversion et découpage en tranches | -join ('gnirts'[-1..-6]) | .split("").reverse().join("") |
| Chiffrement ou compression | IO.Compression.DeflateStream, boucles XOR | Boucles XOR ou RC4 avec une clé stockée à côté du blob |
| Exécution en couches | Invoke-Expression, iex, [scriptblock]::Create() | eval, new Function, Execute et ExecuteGlobal en VBScript |
La plupart de ces techniques ne changent que la façon dont une chaîne est écrite. La dernière ligne est ce qui retransforme la chaîne en code, et c’est la clé de tout le processus.
-EncodedCommand est en UTF-16LE
Le paramètre -EncodedCommand de PowerShell (accepté sous la forme -e, -en,
-enc, -ec et d’autres préfixes, quelle que soit la casse) prend le Base64 du
texte de la commande encodé en UTF-16LE, le même encodage large que celui
décrit dans Chaînes et chaînes obfusquées.
Décodez-le en UTF-8 et chaque caractère sera suivi d’un octet nul. Ces blobs se
reconnaissent à l’œil : le Base64 d’un texte ASCII en UTF-16LE est rempli de
caractères A, car chaque octet nul contribue à des suites comme AA et à des
motifs tels que JABhACAA, l’encodage de $a . Décodez en UTF-16LE
(From Base64 puis Decode text en UTF-16LE dans CyberChef) et vous
obtenez du code lisible.
Pourquoi des couches ?
Chaque couche est une chaîne que la couche précédente décode et transmet à un
point d’exécution (sink). Une chaîne typique : un .lnk exécute
powershell -enc <Base64> ; la commande décodée concatène des fragments en un
deuxième blob Base64, le décompresse avec DeflateStream et passe le résultat à
Invoke-Expression ; cette troisième couche télécharge une DLL et la charge en
mémoire avec [Reflection.Assembly]::Load, le cousin scripté de l’injection
réflexive de DLL. Trois couches, et
seule la dernière fait quelque chose d’intéressant.
Éplucher un script sans risque
La méthode qui fonctionne sur presque tout :
- Travaillez sur une copie, dans le laboratoire. Renommez le fichier avec
une extension inoffensive (
.js.txt,.ps1.txt) pour qu’un double-clic malencontreux ouvre un éditeur, et faites le travail dans votre VM d’analyse, réseau coupé ou simulé. Hachez d’abord l’original. - Mettez en forme. Les scripts d’une seule ligne deviennent lisibles après
js-beautify, un formateur PowerShell ou un simple découpage sur;. Renommez les variables au fur et à mesure que vous les comprenez. - Trouvez les points d’exécution. Recherchez
eval,Function(,Execute,ExecuteGlobal,Invoke-Expression,iex,ScriptBlock,.Invoke(,Start-Process,WScript.Shell,.Run(,.Exec(etActiveXObject. Tout ce qui précède un point d’exécution est généralement du décodage ; le point d’exécution est l’endroit où la couche suivante prend vie. - Remplacez le point d’exécution par un affichage.
eval(x)devientconsole.log(x),Invoke-Expression $xdevientWrite-Output $x, etExecute xen VBScript devientWScript.Echo x. Le code de décodage s’exécute, et le résultat décodé est affiché au lieu d’être exécuté. - Recommencez sur la sortie affichée jusqu’à atteindre du code qui fait autre chose que décoder du code.
- Décodez statiquement quand c’est possible. Si une couche n’est que du Base64 ou un tableau de codes de caractères, décodez-la dans CyberChef ou en quelques lignes de Python plutôt que d’exécuter quoi que ce soit.
Attention : Remplacer le point d’exécution final n’est sûr que si rien avant lui n’agit sur le système. Lisez chaque ligne qui va s’exécuter. Les malwares téléchargent souvent, écrivent un fichier ou vérifient l’environnement tôt, et décodent plus tard. Si une ligne appelle
DownloadString,Run,Start-Process,WScript.Shellou unActiveXObject, neutralisez-la aussi, ou décodez cette partie à la main. Et exécutez le script neutralisé dans la VM du laboratoire, jamais sur votre poste de travail.
Pour le JScript, il existe une couche de sécurité supplémentaire : Node.js ne
possède ni WScript ni ActiveXObject, si bien que les appels propres à
Windows échouent au lieu de s’exécuter. Des émulateurs comme box-js vont plus
loin et fournissent de fausses versions de ces objets qui journalisent chaque
appel (URL demandées, fichiers écrits, commandes lancées) sans les effectuer.
Astuce : Conservez chaque couche. Enregistrez chaque étape décodée dans son propre fichier (
stage1.ps1,stage2.ps1...) et notez comment vous êtes passé de l’une à la suivante. Votre rapport a besoin de la chaîne, et une couche ultérieure réutilise parfois une clé ou une fonction utilitaire d’une couche antérieure.
D’abord le statique, ensuite l’exécution contrôlée
L’épluchage statique est la voie par défaut : vous voyez chaque étape, rien ne communique avec le réseau, et le décodeur fonctionne sur l’échantillon suivant de la même famille. Il échoue lorsque la clé d’une couche est dérivée à l’exécution (nom d’hôte, valeur de registre, réponse du serveur), ou lorsque l’émulation manuelle de l’obfuscation prend trop de temps.
L’exécution devient alors le meilleur outil, sous contrôle : le véritable hôte
(powershell.exe, wscript.exe) dans la VM isolée, avec une
sandbox ou des outils de surveillance qui enregistrent ce
qui se passe, de faux services réseau qui répondent, et un instantané auquel
revenir. C’est de l’analyse dynamique à
proprement parler ; la leçon Surveillance comportementale du module 6 couvre
l’outillage. Pour PowerShell, le laboratoire offre un avantage particulier,
décrit ci-après : Windows lui-même journalisera les couches décodées pour vous.
Ce qu’enregistre la télémétrie Windows
Les malwares de script ont une faiblesse que les binaires n’ont pas : l’interpréteur appartient à Microsoft, et Microsoft l’a instrumenté. Quatre sources comptent.
| Source | Où | Ce qu’elle capture |
|---|---|---|
| Journalisation des blocs de script | Microsoft-Windows-PowerShell/Operational, événement 4104 | Le texte de chaque bloc de script que PowerShell compile, y compris les blocs créés à l’exécution |
| Journalisation des modules | Même journal, événement 4103 | Détails de l’exécution du pipeline : commandes invoquées et leurs paramètres |
| AMSI | Transmis au produit antimalware installé | Le contenu du script remis au scanner juste avant son exécution, depuis PowerShell, Windows Script Host, VBA d’Office et d’autres |
| Création de processus | Événement Sysmon 1, ou événement de sécurité 4688 avec l’audit de la ligne de commande | Ligne de commande complète, processus parent, hashs (Sysmon), utilisateur |
La journalisation des blocs de script est celle que les analystes adorent.
PowerShell journalise un bloc de script lorsqu’il le compile, et chaque
Invoke-Expression ou [scriptblock]::Create() compile un nouveau bloc. Ainsi,
lorsque la couche 1 décode la couche 2 et la passe à Invoke-Expression, la
couche 2 est journalisée dans son propre événement 4104, sous la forme reçue par
le moteur : après le décodage Base64, la décompression et la concaténation qui
l’ont produite. L’obfuscation qui se produit à l’intérieur d’un bloc reste
visible dans le texte de ce bloc, mais chaque couche que l’attaquant exécute
arrive en clair dans le journal. Les grands blocs sont répartis sur plusieurs
événements qui partagent un identifiant de bloc de script. La journalisation des
blocs de script s’active par stratégie de groupe (« Turn on PowerShell Script
Block Logging ») ; même sans cela, PowerShell 5 et versions ultérieures
journalisent au niveau Warning les blocs qui contiennent des mots-clés suspects
connus.
AMSI, l’Antimalware Scan Interface, repose sur le même principe : l’hôte
transmet à l’antivirus, via amsi.dll, le contenu qu’il s’apprête à exécuter,
si bien que le scanner voit l’argument d’eval plutôt que le fichier obfusqué.
C’est pourquoi tant de scripts PowerShell malveillants commencent par un
contournement d’AMSI, par exemple du code qui patche AmsiScanBuffer en
mémoire. Des chaînes comme AmsiScanBuffer, amsiInitFailed ou
System.Management.Automation.AmsiUtils dans un script sont un signal fort à
elles seules et un bon matériau pour YARA.
Les événements de création de processus vous donnent le lanceur : la ligne
exacte powershell.exe -NoP -W Hidden -Enc ..., son parent et l’utilisateur.
C’est souvent tout ce que vous envoie un intervenant en réponse à incident, et le
lab ci-dessous part d’une telle ligne.
Astuce : Dans la VM du laboratoire, activez la journalisation des blocs de script avant d’exécuter un échantillon PowerShell, puis lisez ensuite les événements 4104 avec l’Observateur d’événements ou
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational'. Vous obtenez chaque couche décodée, dans l’ordre, sans écrire un seul décodeur.
Extraire les IOC et rédiger le rapport
Une fois la dernière couche lisible, collectez :
- Les indicateurs réseau : URL, domaines, adresses IP, user-agents et chemins d’URL. Les scripts transportent souvent plusieurs URL de repli ; prenez-les toutes.
- Les indicateurs sur l’hôte : fichiers écrits (
$env:TEMP\x.dll,%APPDATA%\...\update.vbs), clés de registre, noms de tâches planifiées, et la ligne de commande utilisée pour lancer l’étape suivante (rundll32.exe x.dll,Start,regsvr32 /s). - Les hashs des étapes suivantes : si le laboratoire a servi ou capturé le fichier téléchargé, hachez-le et triez-le comme un nouvel échantillon en suivant les étapes d’Identifier et hacher les fichiers.
- Les clés et constantes utilisées par les décodeurs, qui identifient la famille ou la boîte à outils et constituent un bon matériau de signature.
Dans le rapport, en suivant la structure du rapport de
triage, décrivez la livraison (pièce jointe,
raccourci, macro), l’hôte (quel interpréteur et quel processus parent), la
chaîne de couches (l’encodage de chaque couche et son hash, étiquetés comme
artefacts dérivés), le comportement final et les IOC (indicateurs de
compromission). Ajoutez des conseils de détection exploitables par les
défenseurs : la paire parent-enfant, le motif de ligne de commande, le contenu
4104 à rechercher. Neutralisez chaque URL (hxxp://update[.]example[.]com/lab)
pour que personne ne clique dessus depuis le rapport.
Lab : deux scripts épluchés sans exécuter la charge utile
Vous allez désobfusquer deux petits échantillons inoffensifs dont le seul
comportement réel est d’afficher la chaîne de démonstration
http://update.example.com/lab. Les sorties ci-dessous sont réelles, obtenues
avec Node.js v25.7.0 et Python 3.14.7 sous macOS. PowerShell n’était pas
installé sur cette machine : la couche PowerShell est donc décodée avec Python,
et l’équivalent pwsh est décrit mais pas montré.
-
Créez
invoice_0923.js, un échantillon JavaScript qui utilise un tableau de codes de caractères, une inversion de chaîne eteval:js // invoice_0923.js: harmless lab sample var _0x4f = [59,41,117,40,103,111,108,46,101,108,111,115,110,111,99,32,59,34,98,97,108,47,109,111,99,46,101,108,112,109,97,120,101,34,32,43,32,34,46,101,116,97,100,112,117,47,47,58,112,116,116,104,34,32,61,32,117,32,114,97,118]; var _0x9a = String.fromCharCode.apply(null, _0x4f); var _0x1c = _0x9a.split("").reverse().join(""); eval(_0x1c); -
Créez
cmdline.txt, une ligne de commande PowerShell telle qu’un EDR l’enregistrerait :text powershell.exe -NoP -W Hidden -Enc JABhACAAPQAgACcAVwByAGkAdABlAC0ATwB1AHQAJwAgACsAIAAnAHAAdQB0ACcAOwAgACQAYgAgAD0AIAAnACgAJwAnAGgAdAB0AHAAOgAvAC8AdQBwAGQAJwAnACAAKwAgACcAJwBhAHQAZQAuAGUAeABhAG0AcABsAGUALgBjAG8AbQAvAGwAYQBiACcAJwApACcAOwAgAEkAbgB2AG8AawBlAC0ARQB4AHAAcgBlAHMAcwBpAG8AbgAgACgAJABhACAAKwAgACcAIAAnACAAKwAgACQAYgApAA== -
Vérifiez que l’indicateur n’est pas visible, puis trouvez les points d’exécution :
bash grep -c example invoice_0923.js cmdline.txt grep -nE 'eval|Function\(|Invoke-Expression|IEX|-Enc' invoice_0923.js cmdline.txt | cut -c1-90text invoice_0923.js:0 cmdline.txt:0 invoice_0923.js:5:eval(_0x1c); cmdline.txt:1:powershell.exe -NoP -W Hidden -Enc JABhACAAPQAgACcAVwByAGkAdABlAC0ATwB1AHQAJAucun des deux fichiers ne contient le domaine. Le point d’exécution JavaScript est
eval, à la ligne 5. L’échantillon PowerShell n’a pas encore de point d’exécution visible :-Encmasque toute la commande. -
Lisez le JavaScript avant d’exécuter quoi que ce soit. Les lignes 2 à 4 ne font que construire une chaîne :
fromCharCodetransforme des nombres en caractères, puissplit,reverseetjoinl’inversent. Rien ne touche au système de fichiers ni au réseau avanteval. Neutralisez le point d’exécution et vérifiez la modification :bash sed 's/^eval(/console.log(/' invoice_0923.js > invoice_0923.safe.js diff invoice_0923.js invoice_0923.safe.jstext 5c5 < eval(_0x1c); --- > console.log(_0x1c); -
N’exécutez que la copie neutralisée (dans votre VM de laboratoire) :
bash node invoice_0923.safe.jstext var u = "http://update." + "example.com/lab"; console.log(u);Le texte affiché est la couche 2, sous forme de code source : une concaténation qui construit l’URL et l’affiche. Un véritable échantillon appellerait ici
new ActiveXObject("MSXML2.XMLHTTP")au lieu deconsole.log. La couche 2 n’a pas d’autre point d’exécution, donc l’épluchage s’arrête ; la concaténation est assez simple pour être reconstituée à l’œil. Vous avez maintenant l’URL sans l’avoir exécutée. -
Essayez de décoder le blob PowerShell de manière naïve :
bash awk '{print $NF}' cmdline.txt | base64 -d | head -c 60 | xxd | head -3text 00000000: 2400 6100 2000 3d00 2000 2700 5700 7200 $.a. .=. .'.W.r. 00000010: 6900 7400 6500 2d00 4f00 7500 7400 2700 i.t.e.-.O.u.t.'. 00000020: 2000 2b00 2000 2700 7000 7500 7400 2700 .+. .'.p.u.t.'.Un octet sur deux est nul : c’est du texte UTF-16LE, exactement comme l’exige
-EncodedCommand. -
Enregistrez
decode_enc.py, qui décode l’argument en UTF-16LE, fusionne les concaténations'a' + 'b'de littéraux entre apostrophes, puis traite chaque littéral comme du code potentiel pour la couche suivante :python import base64, re, sys # Tokens: a whole single-quoted literal ('' inside is an escaped quote), # a plus sign, or a run of anything else. TOKEN = re.compile(r"'(?:[^']|'')*'|\+|[^'+]+") def fold(code): """Join 'abc' + 'def' into 'abcdef', scanning left to right.""" out = [] for t in TOKEN.findall(code): t = t.strip() if not t.startswith("'") else t if not t: continue # top of the output stack is <literal> + : merge the two literals if t.startswith("'") and out[-2:-1] and out[-1] == "+" and out[-2].startswith("'"): out.pop() t = out.pop()[:-1] + t[1:] out.append(t) return " ".join(out) def literals(code): """Contents of each literal, with '' turned back into '.""" return [t[1:-1].replace("''", "'") for t in TOKEN.findall(code) if t.startswith("'")] cmdline = open(sys.argv[1], encoding="utf-8").read().strip() # 1. The argument after -e / -enc / -EncodedCommand (PowerShell accepts prefixes). blob = re.search(r"-e[a-z]*\s+([A-Za-z0-9+/=]+)", cmdline, re.I).group(1) print(f"[+] Base64 argument: {len(blob)} chars") # 2. -EncodedCommand is Base64 of UTF-16LE text, not UTF-8. layer1 = base64.b64decode(blob).decode("utf-16le") print("[+] layer 1, decoded:\n " + layer1) print("[+] layer 1, folded:\n " + fold(layer1)) # 3. A literal may itself be code for Invoke-Expression: unescape and fold again. print("[+] layer 2, literals folded:") urls = set() for s in literals(fold(layer1)): inner = fold(s) print(" " + inner) urls.update(re.findall(r"https?://[^\s'\")]+", inner)) print("[+] URLs:", sorted(urls))Le découpeur en jetons consomme chaque littéral en entier depuis son apostrophe ouvrante, car
''à l’intérieur est une apostrophe échappée. Une expression régulière de recherche-remplacement plus simple fusionnait des fragments à l’intérieur de$bet corrompait l’URL. Les décodeurs échouent en silence : comparez toujours leur sortie à l’entrée, à l’œil. -
Exécutez-le :
bash python3 decode_enc.py cmdline.txttext [+] Base64 argument: 296 chars [+] layer 1, decoded: $a = 'Write-Out' + 'put'; $b = '(''http://upd'' + ''ate.example.com/lab'')'; Invoke-Expression ($a + ' ' + $b) [+] layer 1, folded: $a = 'Write-Output' ; $b = '(''http://upd'' + ''ate.example.com/lab'')' ; Invoke-Expression ($a + ' ' + $b) [+] layer 2, literals folded: Write-Output ( 'http://update.example.com/lab' ) [+] URLs: ['http://update.example.com/lab']La couche 1 construit un nom de commande et un argument à partir de fragments et les passe à
Invoke-Expression. La chaîne$bcontient la couche 2 sous forme de code source, avec sa propre concaténation ; la fusionner une fois de plus révèle l’URL. La ligne vide correspond au littéral séparateur' '. Rien n’a été exécuté à aucun moment. -
Si PowerShell est disponible (dans la VM du laboratoire,
pwshsur n’importe quelle plateforme ou Windows PowerShell), le même épluchage utilise l’interpréteur lui-même pour les parties inoffensives. Décodez l’argument avec[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($blob))(Unicodeest le nom que .NET donne à UTF-16LE) et enregistrez-le souslayer1.ps1. Lisez-le, vérifiez que seules des opérations sur des chaînes précèdent le point d’exécution, remplacezInvoke-ExpressionparWrite-Outputet exécutez la copie modifiée avecpwsh -NoProfile -File layer1.ps1. Elle doit afficher la couche 2,Write-Output ('http://upd' + 'ate.example.com/lab'), sous forme de texte. Si vous exécutez plutôt la ligne de commande d’origine dans une VM Windows de laboratoire où la journalisation des blocs de script est activée, l’événement 4104 enregistre à la fois la couche 1 et le bloc créé parInvoke-Expression. -
Collez l’argument Base64 dans CyberChef, appliquez From Base64 puis Decode text (UTF-16LE (1200)), et comparez avec l’étape 8.
Questions : Pourquoi grep ne trouve-t-il ni le domaine ni
Invoke-Expression dans cmdline.txt ? À l’étape 4, quelles lignes
auriez-vous dû neutraliser si la ligne 3 avait été
new ActiveXObject("WScript.Shell").Run(...) au lieu d’une opération sur des
chaînes ? Si l’échantillon avait utilisé "{1}{0}" -f 'ssion','Invoke-Expre'
au lieu de 'Invoke-Expre' + 'ssion', comment faudrait-il modifier
decode_enc.py ? Quels événements 4104 un défenseur verrait-il si cette ligne
de commande s’exécutait sur une machine où la journalisation des blocs de script
est activée, et que contiendrait chacun d’eux ? Rédigez la section IOC d’un
rapport pour ces deux échantillons, avec l’URL neutralisée.
À retenir
- Les malwares de script s’exécutent sur des hôtes livrés avec chaque machine
Windows :
powershell.exe,wscript.exeetcscript.exepour JScript et VBScript, etmshta.exepour les applications HTML. Le processus parent fait partie des éléments de preuve. - L’obfuscation change la façon dont les chaînes sont écrites (concaténation,
codes de caractères, Base64, chaînes de format, bruit d’échappement,
renommage) ; un point d’exécution tel qu’
evalouInvoke-Expressiontransforme le résultat en couche suivante. -EncodedCommandest du Base64 d’UTF-16LE. Décodez-le en UTF-16LE, dans CyberChef ou en trois lignes de Python.- Épluchez en remplaçant le point d’exécution par un affichage, après avoir vérifié que rien avant lui n’agit sur le système, et recommencez jusqu’à ce que le code cesse de décoder du code. Décodez statiquement chaque fois que c’est possible, et n’exécutez quoi que ce soit que dans le laboratoire.
- La journalisation des blocs de script (événement 4104) enregistre chaque bloc
de script au moment où PowerShell le compile, si bien que chaque couche passée
à
Invoke-Expressionapparaît en clair ; AMSI donne la même vue à l’antimalware, ce qui explique que les attaquants cherchent à le désactiver. - Rapportez la livraison, l’hôte, la chaîne de couches avec les hashs, le comportement final et les IOC neutralisés, ainsi que les motifs de ligne de commande et de processus parent que les défenseurs peuvent rechercher.