// BTLO  ·  Incident Response

Macro-ni

BTLO Easy Cyberchef

Scenario

A phishing email bypassed the filter with a Word document attached. IR pulled it into the sandbox, observed suspicious activity on launch, and handed it off for deeper analysis. The objective is to decode the payload layer by layer and identify the C2 infrastructure.


Methodology

Initial Triage — Extracting the VBA

The sample is a .docm — a macro-enabled Word document. Unzipping it exposes the OLE container at word/vbaProject.bin. The instinct is to run strings or cat against it, but the magic bytes d0 cf 11 e0 identify it as a Compound Document Binary Format file. Raw text extraction from an OLE container produces garbage output.

The correct tool is olevba, which parses the OLE structure and prints macro source cleanly:

olevba ~/Desktop/Macroni.docx

The macro is a Sub RunCommand() that concatenates a massive Base64 blob across dozens of lines using pl = pl & before executing it as:

powershell -nop -w hidden -encodedcommand <blob>

Layer 1 — Base64 + UTF-16LE

Rather than manually copying each pl = pl & fragment, strip and join them in one command:

olevba ~/Desktop/sample/Macroni/word/vbaProject.bin | grep "pl = pl &" | sed 's/.*pl = pl & "//;s/"//' | tr -d '\n'

This produces a single clean Base64 string with no VBA syntax attached. Paste it into CyberChef and apply:

From Base64 → Decode text → UTF-16LE The output is readable PowerShell revealing a second stage — a MemoryStream loading another Base64 blob via [Convert]::FromBase64String(...).

Layer 2 — Gzip

The inner blob starts with H4sI — the Base64 representation of the gzip magic bytes 1f 8b. This immediately signals the second layer is gzip compressed. Copy the inner Base64 string from inside FromBase64String( to the closing ) and apply in a new CyberChef tab:

From Base64 → Gunzip The decompressed output is a PowerShell reflective loader. Scrolling through reveals the third layer — a byte array variable and an XOR loop.

Layer 3 — XOR

The decompressed script contains:

powershell

[Byte[]]$var_code = [System.Convert]::FromBase64String('<blob>')

for ($x = 0; $x -lt $var_code.Count; $x++) {
    $var_code[$x] = $var_code[$x] -bxor 35
}

$var_code holds the encrypted shellcode. The XOR key is decimal 35. After the loop the shellcode is allocated via VirtualAlloc and executed through a delegate — a standard reflective shellcode injection pattern.

Decoding the $var_code blob in CyberChef:

From Base64 → XOR (key: 35, decimal) The decrypted output contains the C2 callback URL and User-Agent string:

User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)
...176.103.56.89

C2 IP: 176.103.56.89


Attack Summary

Phase Action
Initial Access Phishing email delivers macro-enabled .docm attachment
Execution VBA macro runs powershell -nop -w hidden -encodedcommand on document open
Obfuscation Layer 1 PowerShell blob Base64 encoded, UTF-16LE wide string
Obfuscation Layer 2 Inner payload gzip compressed and Base64 encoded
Obfuscation Layer 3 Shellcode XOR encrypted with key 35, stored in $var_code
C2 Shellcode beacons to 176[.]103[.]56[.]89 via VirtualAlloc + delegate execution

IOCs

Type Value
IP (C2) 176[.]103[.]56[.]89
File Macroni.docm
File word/vbaProject.bin
Execution powershell -nop -w hidden -encodedcommand
User-Agent Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)
XOR Key 35 (decimal)

MITRE ATT&CK

Technique ID Description
Spearphishing Attachment T1566.001 Macro-enabled Word document delivered via phishing email
PowerShell T1059.001 Macro executes powershell -nop -w hidden -encodedcommand
Obfuscated Files or Information: Compile After Delivery T1027.010 VBA concatenates payload at runtime to evade static detection
Obfuscated Files or Information T1027 Three-layer obfuscation: Base64, gzip, XOR
Deobfuscate/Decode Files or Information T1140 XOR key 35 decrypts shellcode from $var_code byte array
Application Layer Protocol T1071.001 Shellcode beacons to C2 over HTTP with spoofed IE8 User-Agent

Defender Takeaways

Disable macros by default — The entire chain starts with a VBA macro executing on document open. Group Policy enforcement of Block macros from running in Office files from the internet breaks the kill chain at the first step. Macro execution in phishing documents is a solved problem that many environments still haven’t solved.

olevba over strings for OLE files — Raw string extraction from .bin OLE containers produces noise, not signal. olevba parses the Compound Document structure correctly and outputs clean macro source. This is the right first tool for any .docm, .xlsm, or suspicious Office file.

H4sI is a detection primitive — Any Base64 blob starting with H4sI is gzip compressed content. This pattern appears constantly in malware loaders and is trivially detectable via YARA or SIEM rules targeting PowerShell script blocks. A rule hunting for H4sI in decoded PowerShell command line arguments will catch a broad class of stagers.

XOR with single-byte keys is weak but effective — Decimal 35 is a single-byte XOR key. It defeats signature-based AV but not behaviour-based detection. Endpoint solutions monitoring VirtualAlloc + delegate execution chains would catch the shellcode injection regardless of what key was used to encrypt the payload.

Layered obfuscation is a signal in itself — Three encoding layers on a document macro is not normal software behaviour. SIEM rules alerting on PowerShell processes spawned by WINWORD.EXE with -encodedcommand in the command line would have flagged this at execution without needing to decode anything.


The extracted powershell command contains what type of encryption?
Click flag to reveal base64
After decrypting first layer of encryption. We get another layer of encryption. If decrypted, what type of archive file data will it be?
Click to reveal answer gzip
Upon saving the archive and extracting the content. We see third layer of encryption present. What byte array variable is having that data?
Click flag to reveal var_code
The same byte array is getting XORed by a decimal value. What is it?
Click to reveal answer 35
Can you decrypt this layer of obfuscation and find the C2 IP?
Click flag to reveal 176.103.56.89
🔒
// active lab
writeup locked
withheld in accordance with platform guidelines
to avoid spoiling live challenges.
password provided to recruiters on request.