reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
name: analyzing-malicious-office-macros
|
||||
description: 'Analyzes malicious Office documents by extracting and reviewing VBA macros and
|
||||
OLE/OOXML structure: auto-exec triggers, obfuscation, dropped payloads, and shell/PowerShell
|
||||
invocation. Activates for requests to analyze a malicious Word/Excel document, VBA macro, or
|
||||
maldoc.'
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- malware
|
||||
- maldoc
|
||||
- vba
|
||||
- office
|
||||
- olevba
|
||||
- phishing
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1566.001
|
||||
- T1059.005
|
||||
- T1204.002
|
||||
d3fend:
|
||||
- D3-FCR
|
||||
- D3-DA
|
||||
references:
|
||||
- 'oletools (olevba) — https://github.com/decalage2/oletools'
|
||||
- 'MS-OVBA VBA file format — https://learn.microsoft.com/openspecs/office_file_formats/ms-ovba/'
|
||||
---
|
||||
|
||||
# Analyzing Malicious Office Macros
|
||||
|
||||
## When to Use
|
||||
|
||||
- A Word/Excel/PowerPoint document is suspected of delivering malware via macros.
|
||||
- You need to extract VBA, identify auto-exec triggers, and see what the macro does.
|
||||
- You are determining the next-stage payload or URL a maldoc fetches.
|
||||
|
||||
**Do not use** the document's "Enable Content" path to analyze it — never open a maldoc in
|
||||
Office. Extract and read the macro statically; detonate only in the isolated lab if needed.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `oletools` (`pip install oletools`) for `olevba`/`oleid`, or the bundled OLE/OOXML extractor.
|
||||
- The document in neutralized form inside the lab.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Do **not** open the document in Microsoft Office. Macros may auto-run on open/close.
|
||||
- Static extraction only; if dynamic analysis is required, detonate in the isolated victim VM.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Identify the container and locate macros
|
||||
|
||||
OOXML (`.docm`, `.xlsm`) is a ZIP; legacy (`.doc`, `.xls`) is OLE2. Macros live in a
|
||||
`vbaProject.bin` OLE stream. Extract it:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py extract maldoc.docm
|
||||
```
|
||||
|
||||
### Step 2: Flag auto-exec triggers
|
||||
|
||||
Look for entry points that run without user action beyond enabling macros:
|
||||
|
||||
```text
|
||||
AutoOpen, Document_Open, AutoClose, Workbook_Open, Auto_Open, Document_Close
|
||||
```
|
||||
|
||||
### Step 3: Find suspicious calls
|
||||
|
||||
Identify execution and download primitives:
|
||||
|
||||
```text
|
||||
Shell, WScript.Shell, CreateObject, powershell, cmd /c
|
||||
URLDownloadToFile, MSXML2.XMLHTTP, ADODB.Stream (drop to disk)
|
||||
Environ, GetObject("winmgmts:") (WMI)
|
||||
```
|
||||
|
||||
### Step 4: Deobfuscate
|
||||
|
||||
Maldocs commonly use Chr()/string concatenation, base64, and split-and-join. Resolve these to
|
||||
recover the real command and any URL or dropped path.
|
||||
|
||||
### Step 5: Extract IOCs and the next stage
|
||||
|
||||
Pull URLs, dropped file paths, and the spawned command line. These become the report's IOCs
|
||||
and pivot points.
|
||||
|
||||
## Validation
|
||||
|
||||
- The recovered command line and URL make sense together (download → drop → execute).
|
||||
- Auto-exec triggers explain how the macro starts.
|
||||
- Deobfuscated strings match what dynamic detonation would reveal (if you confirm in the lab).
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Opening the document in Office "just to see" — this executes the macro.
|
||||
- Stopping at obfuscated source without resolving it; the IOCs are inside the obfuscation.
|
||||
- Missing stomped VBA (p-code present, source removed) — check for VBA stomping and analyze
|
||||
p-code if the source looks empty.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the extractor and
|
||||
indicator scanner.
|
||||
- oletools and MS-OVBA (linked in frontmatter).
|
||||
@@ -0,0 +1,44 @@
|
||||
# API Reference: Analyzing Malicious Office Macros
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` extracts VBA macros and flags auto-exec triggers, suspicious calls, and
|
||||
URLs. It prefers `oletools`/olevba and falls back to locating `vbaProject.bin` in OOXML ZIP
|
||||
containers (or scanning OLE2 bytes) when oletools is unavailable.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| oletools | >= 0.60 | VBA extraction (olevba), OLE inspection (optional but recommended) |
|
||||
| (stdlib) | — | zipfile/regex fallback |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| olevba / ol0id (oletools) | Macro extraction and document triage |
|
||||
| oledump.py | Manual OLE stream inspection |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `analyze(path)`
|
||||
Dispatches to olevba or the minimal extractor; returns macro presence, auto-exec triggers,
|
||||
suspicious calls, and URLs.
|
||||
|
||||
### `_scan_text(text)`
|
||||
Flags auto-exec entry points, suspicious VBA calls, and extracts URLs from macro source.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
pip install oletools
|
||||
python scripts/analyst.py extract maldoc.docm
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- oletools (olevba) — https://github.com/decalage2/oletools
|
||||
- MS-OVBA VBA file format —
|
||||
https://learn.microsoft.com/openspecs/office_file_formats/ms-ovba/
|
||||
- VBA auto-exec trigger names (Office VBA object model documentation)
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract and triage VBA macros from Office documents.
|
||||
|
||||
Prefers ``oletools`` (olevba) for robust extraction; falls back to locating ``vbaProject.bin``
|
||||
inside OOXML (ZIP) containers and flagging suspicious indicators in the raw bytes. Never opens
|
||||
the document in Office and never executes anything.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
AUTOEXEC = ["AutoOpen", "Document_Open", "AutoClose", "Document_Close",
|
||||
"Workbook_Open", "Auto_Open", "Auto_Close"]
|
||||
SUSPICIOUS = ["Shell", "WScript.Shell", "CreateObject", "powershell", "cmd",
|
||||
"URLDownloadToFile", "XMLHTTP", "ADODB.Stream", "Environ",
|
||||
"winmgmts", "Base64", "Chr(", "GetObject"]
|
||||
|
||||
|
||||
def _scan_text(text: str) -> dict:
|
||||
found_auto = [k for k in AUTOEXEC if k.lower() in text.lower()]
|
||||
found_sus = [k for k in SUSPICIOUS if k.lower() in text.lower()]
|
||||
urls = sorted(set(re.findall(r"https?://[^\s\"'<>)]+", text)))
|
||||
return {"auto_exec": found_auto, "suspicious": found_sus, "urls": urls}
|
||||
|
||||
|
||||
def extract_with_olevba(path: str) -> dict:
|
||||
from oletools.olevba import VBA_Parser # type: ignore
|
||||
vp = VBA_Parser(path)
|
||||
macros = []
|
||||
if vp.detect_vba_macros():
|
||||
for _, _, name, code in vp.extract_macros():
|
||||
macros.append({"name": name, **_scan_text(code),
|
||||
"lines": code.count("\n") + 1})
|
||||
vp.close()
|
||||
return {"engine": "olevba", "has_macros": bool(macros), "macros": macros}
|
||||
|
||||
|
||||
def extract_minimal(path: str) -> dict:
|
||||
p = Path(path)
|
||||
data = p.read_bytes()
|
||||
indicators = {}
|
||||
if zipfile.is_zipfile(path):
|
||||
with zipfile.ZipFile(path) as zf:
|
||||
vba = [n for n in zf.namelist() if n.lower().endswith("vbaproject.bin")]
|
||||
if not vba:
|
||||
return {"engine": "minimal", "has_macros": False,
|
||||
"note": "no vbaProject.bin in OOXML container"}
|
||||
blob = zf.read(vba[0])
|
||||
# vbaProject.bin is OLE/compressed; scan recoverable ASCII for indicators
|
||||
text = blob.decode("latin-1", "replace")
|
||||
indicators = _scan_text(text)
|
||||
return {"engine": "minimal", "has_macros": True,
|
||||
"stream": vba[0], "indicators": indicators,
|
||||
"note": "install oletools for full VBA source extraction"}
|
||||
# legacy OLE2: scan whole file bytes for indicator strings
|
||||
text = data.decode("latin-1", "replace")
|
||||
return {"engine": "minimal", "has_macros": b"Attribut" in data,
|
||||
"indicators": _scan_text(text),
|
||||
"note": "legacy OLE2; install oletools for reliable extraction"}
|
||||
|
||||
|
||||
def analyze(path: str) -> dict:
|
||||
try:
|
||||
return extract_with_olevba(path)
|
||||
except ImportError:
|
||||
return extract_minimal(path)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Analyze Office macros")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("extract")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(analyze(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user