Análisis de Malware Macro en Documentos de Office
Cuándo Usar
- Un documento sospechoso de Office (.doc, .docm, .xls, .xlsm, .ppt) ha sido marcado por la seguridad del correo electrónico.
- Investigar campañas de phishing que entregan documentos de Office armados.
- Extraer código de macro VBA para identificar la URL de descarga del payload y el método de ejecución.
- Analizar código VBA ofuscado para comprender la cadena de ataque completa.
- Determinar si un documento usa DDE, ActiveX o inyección remota de plantillas en lugar de macros.
No usar para analizar amenazas de Office que no son macros (DDE, inyección remota de plantillas); aunque esta habilidad cubre la detección de estos, puede ser necesario un análisis especializado.
Requisitos Previos
- Python 3.8+ con oletools instalado (
pip install oletools) - oledump.py de Didier Stevens (https://blog.didierstevens.com/programs/oledump-py/)
- Máquina virtual de análisis aislada sin Microsoft Office instalado (previene la ejecución accidental)
- XLMDeobfuscator para análisis de macros de Excel 4.0 (pip install xlmdeobfuscator)
- LibreOffice para renderizado seguro de documentos (no ejecuta macros VBA por defecto)
Flujo de Trabajo
Paso 1: Triaje Inicial del Documento
Determinar si el documento contiene macros u otro contenido activo:
# Triaje rápido con olevba
olevba suspect.docm
# Verificar flujos OLE y macros
oleid suspect.docm
# Indicadores de salida:
# Macros VBA: Verdadero/Falso
# Macros XLM: Verdadero/Falso
# Relaciones Externas: Verdadero/Falso (plantilla remota)
# ObjectPool: Verdadero/Falso (objetos incrustados)
# Flash: Verdadero/Falso (objetos SWF)
# Análisis OLE completo
oledump.py suspect.docm
# Listar todos los flujos OLE con indicadores de macro
# Flujos marcados con 'M' contienen macros VBA
# Flujos marcados con 'm' contienen atributos de macro
Paso 2: Extraer y Analizar Código VBA
Extraer la fuente completa de la macro VBA:
# Extraer VBA con desofuscación completa
olevba --decode --deobf suspect.docm
# Extraer solo el código fuente VBA
olevba --code suspect.docm > extracted_vba.txt
# Extracción detallada con oledump
oledump.py -s 8 -v suspect.docm # Flujo 8 (ajustar según el listado de flujos)
# Extraer todos los flujos de macro
oledump.py -p plugin_vba_dco suspect.docm
Elementos Clave de VBA a Identificar:
━━━━━━━━━━━━━━━━━━━━━━━━━━━
Disparadores de Auto-Ejecución:
- Auto_Open / AutoOpen (Word)
- Auto_Close / AutoClose
- Document_Open / Document_Close
- Workbook_Open (Excel)
- AutoExec
Funciones Sospechosas:
- Shell() / Shell.Application
- WScript.Shell.Run / Exec
- CreateObject("WScript.Shell")
- Ejecución de PowerShell
- URLDownloadToFile
- MSXML2.XMLHTTP (peticiones HTTP)
- ADODB.Stream (escritura de archivos)
- Environ() (variables de entorno)
- CallByName (llamadas a métodos indirectas)
Paso 3: Desofuscar Código VBA
Eliminar las capas de ofuscación para revelar el payload:
# Técnicas de desofuscación VBA
import re
def desofuscar_vba(codigo):
# 1. Resolver llamadas Chr(): Chr(104) & Chr(116) -> "ht"
def resolver_chr(coincidencia):
try:
return chr(int(coincidencia.group(1)))
except:
return coincidencia.group(0)
codigo = re.sub(r'Chr\$?\((\d+)\)', resolver_chr, codigo)
# 2. Eliminar concatenación de cadenas: "htt" & "p://" -> "http://"
codigo = re.sub(r'"\s*&\s*"', '', codigo)
# 3. Resolver llamadas ChrW: ChrW(104)
codigo = re.sub(r'ChrW\$?\((\d+)\)', resolver_chr, codigo)
# 4. Resolver StrReverse: StrReverse("exe.daolnwod") -> "download.exe"
def resolver_reversa(coincidencia):
return '"' + coincidencia.group(1)[::-1] + '"'
codigo = re.sub(r'StrReverse\("([^"]+)"\)', resolver_reversa, codigo)
# 5. Eliminar ofuscación con Mid$/Left$/Right$ (complejo, marcar para revisión manual)
# 6. Resolver Replace(): Replace("Powxershxell", "x", "")
def resolver_reemplazar(coincidencia):
original = coincidencia.group(1)
buscar = coincidencia.group(2)
reemplazar_con = coincidencia.group(3)
return '"' + original.replace(buscar, reemplazar_con) + '"'
codigo = re.sub(r'Replace\("([^"]+)",\s*"([^"]+)",\s*"([^"]*)"\)', resolver_reemplazar, codigo)
return codigo
with open("extracted_vba.txt") as f:
codigo_vba = f.read()
desofuscado = desofuscar_vba(codigo_vba)
print(desofuscado)
Paso 4: Analizar Macros de Excel 4.0 (XLM)
Manejar macros heredadas de Excel que eluden la detección de VBA:
# Detectar macros XLM
olevba --xlm suspect.xlsm
# Desofuscar macros XLM
xlmdeobfuscator -f suspect.xlsm
# Análisis manual de XLM con oledump
oledump.py suspect.xlsm -p plugin_biff.py
# Funciones de macro XLM (Excel 4.0) a vigilar:
# EXEC() - Ejecutar comando de shell
# CALL() - Llamar a función DLL
# REGISTER() - Registrar función DLL
# URLDownloadToFileA - Descargar archivo
# ALERT() - Mostrar mensaje (ingeniería social)
# HALT() - Detener ejecución
# GOTO() - Flujo de control
# IF() - Ejecución condicional
Paso 5: Verificar Vectores de Ataque Sin Macros
Examinar el documento en busca de DDE, plantillas remotas y objetos incrustados:
# Verificar DDE (Dynamic Data Exchange)
python3 -c "
import zipfile
import xml.etree.ElementTree as ET
import re
z = zipfile.ZipFile('suspect.docx')
for nombre in z.namelist():
if nombre.endswith('.xml') or nombre.endswith('.rels'):
contenido = z.read(nombre).decode('utf-8', errors='ignore')
# Códigos de campo DDE
if 'DDEAUTO' in contenido or 'DDE ' in contenido:
print(f'[!] DDE encontrado en {nombre}')
dde_match = re.findall(r'DDEAUTO[^\"]*\"([^\"]+)\"', contenido)
for m in dde_match:
print(f' Comando: {m}')
# Plantilla remota
if 'attachedTemplate' in contenido or 'Target=' in contenido:
urls = re.findall(r'Target=\"(https?://[^\"]+)\"', contenido)
for url in urls:
print(f'[!] URL de plantilla remota: {url}')
"
# Verificar objetos OLE incrustados
oledump.py -p plugin_msg.py suspect.docm
# Verificar relaciones en busca de referencias externas
python3 -c "
import zipfile
z = zipfile.ZipFile('suspect.docx')
for nombre in z.namelist():
if '.rels' in nombre:
contenido = z.read(nombre).decode('utf-8', errors='ignore')
if 'http' in contenido.lower() or 'ftp' in contenido.lower():
print(f'Referencia externa en {nombre}:')
import re
urls = re.findall(r'Target=\\