capa-real iguala a capa sin ceder nada—. La Parte 5 abrirá el motor; esta cierra las reglas.C2: hablar con casa#
Todo malware que no sea un simple destructor necesita comunicarse: recibir órdenes, exfiltrar. Y comunicarse en Windows significa llamar a un puñado acotado de APIs de red. capa las agrupa por capacidad, y todas comparten la misma virtud para el análisis estático: la API está en la tabla de imports.
El caso más escueto —abrir una URL con la API de alto nivel de WinINet— es literalmente una hoja:
rule:
meta:
name: connect to URL
namespace: communication/http/client
scopes:
static: instruction
mbc:
- Communication::HTTP Communication::Open URL [C0002.004]
features:
- and:
- api: wininet.InternetOpenUrl
Regla communication/http/client/connect-to-URL — capa-rules (Mandiant, Apache-2.0).
Sus parientes son igual de directas, con un detalle bonito de bajo nivel. send data on socket y resolve DNS no solo listan la API por nombre, sino también por ordinal, porque ws2_32.dll exporta muchas de sus funciones sin nombre:
features:
- or:
- api: ws2_32.send
- api: ws2_32.#19 = send # exportada por ordinal
- api: ws2_32.WSASend
- api: ws2_32.sendto
- api: System.Net.Sockets.Socket::Send
Regla communication/socket/send/send-data-on-socket (recortada) — capa-rules (Mandiant, Apache-2.0).
Ese ws2_32.#19 es capa reconociendo que un binario puede importar send por número (#19) en vez de por nombre, un viejo truco de ofuscación ligera. La regla lo cubre igual. resolve DNS hace lo mismo con gethostbyname / getaddrinfo / DnsQuery, y connect to HTTP server con InternetConnect / WinHttpConnect.
connect to URL/HTTP · resolve DNS · send/receive on socket: APROXIMADAS
— capa-real las evalúa todas: confirma que las APIs de red están importadas. No puede afirmar la secuencia (abrir socket → conectar → enviar), pero la presencia del conjunto ya dibuja la capacidad de red. Es exactamente el tipo de comportamiento —“esta pieza sabe hablar por HTTP”— que un triage estático reconstruye bien. (El matiz honesto de siempre: presencia no es uso. Un binario que importa send puede comunicarse; capa-real lo marca como capaz, no como culpable.)
Colección: robar lo que se ve y se teclea#
La colección tiene dos comportamientos emblemáticos que caen, muy didácticamente, a lados opuestos de la frontera.
Keylogging por sondeo es puro import. El malware pregunta en un bucle por el estado de cada tecla:
rule:
meta:
name: log keystrokes via polling
namespace: collection/keylog
scopes:
static: function
att&ck:
- Collection::Input Capture::Keylogging [T1056.001]
mbc:
- Collection::Keylogging::Polling [F0002.002]
features:
- or:
- api: user32.GetAsyncKeyState
- api: user32.GetKeyState
- api: user32.GetKeyboardState
- api: user32.VkKeyScan
Regla collection/keylog/log-keystrokes-via-polling (Lab 11-03 del PMA) — capa-rules (Mandiant, Apache-2.0).
log keystrokes via polling: APROXIMADA
— Un or de APIs de teclado: capa-real lo caza. GetAsyncKeyState fuera de un juego o una utilidad de accesibilidad es una señal de keylogger de manual.
La captura de pantalla, en cambio, se le escapa —y por una razón que ya conocemos bien—:
features:
- or:
- and:
- or: [ {api: user32.GetWindowDC}, {api: user32.GetDC} ]
- or: [ {api: gdi32.BitBlt}, {api: gdi32.GetDIBits} ]
- api: gdi32.CreateCompatibleDC
- api: gdi32.CreateCompatibleBitmap
- basic block:
- and: [ {api: BitBlt}, {characteristic: tight loop} ]
Regla collection/screenshot/capture-screenshot (recortada) — capa-rules (Mandiant, Apache-2.0).
capture screenshot: FUERA DE ALCANCE
— Aunque sus hojas sean APIs, la regla exige la coreografía GDI completa —obtener un contexto de dispositivo, crear uno compatible, un bitmap compatible, y hacer el BitBlt— acoplada en una misma función. Es el patrón de la Parte 2: no basta que las APIs existan, tienen que concurrir. BitBlt sola no es una captura de pantalla —la usa cualquier programa que dibuje—; la captura es la secuencia. Y la secuencia es lo que el file-scope no ve. (El monitoreo de portapapeles y la captura de audio, más simples, sí son aproximables.)
Impacto: el ransomware, por sus bordes#
Llegamos al daño. El ransomware hace tres cosas: cifrar los ficheros, inhabilitar la recuperación, y pedir el rescate. Lo revelador es que capa-real ve bien dos de las tres, y justo la central —el cifrado— es la que más se le escapa.
Inhabilitar la recuperación es su firma más fiable, y es file-scope. Borrar las instantáneas de volumen se hace ejecutando comandos conocidos, y la regla los busca como cadenas (ya la vimos en la Parte 0):
features:
- or:
- string: /vssadmin.* delete shadows/i
- string: /vssadmin.* resize shadowstorage/i
- string: /wmic.* shadowcopy delete/i
Regla impact/inhibit-system-recovery/delete-volume-shadow-copies — capa-rules (Mandiant, Apache-2.0).
delete volume shadow copies: APROXIMADA
— capa-real la caza: son regex de cadenas. Junto con reference cryptocurrency strings EXACTA
(direcciones de wallet y jerga de rescate en la nota) y las rutas de la nota de rescate, forma un bloque de “señales periféricas del ransomware” que el estático detecta con soltura.
Pero el cifrado en sí —el corazón del ransomware— es terreno hostil, y aquí hay un matiz que corrige la intuición fácil. Uno diría: “la cripto por algoritmo (RC4, Parte 1) es fuera de alcance, pero la cripto por API de Windows será aproximable”. No siempre. Miren encrypt data using AES via WinAPI:
features:
- and:
- or:
- number: 0x6611 = CALG_AES
- number: 0x660E = CALG_AES_128
- number: 0x6610 = CALG_AES_256
- or:
- api: CryptGenKey
- api: CryptDeriveKey
- api: CryptImportKey
Regla data-manipulation/encryption/aes/encrypt-data-using-AES-via-WinAPI (recortada) — capa-rules (Mandiant, Apache-2.0).
encrypt data using AES via WinAPI: FUERA DE ALCANCE
— Aunque use APIs de Windows (CryptGenKey, de advapi32), su primer requisito obligatorio es la constante number: 0x6611 = CALG_AES —el identificador de algoritmo que se le pasa a la API para pedir AES—. Y un number es, como sabemos desde la Parte 1, un operando inmediato: invisible al file-scope. capa no se conforma con “usa la API de cripto”; exige “le pide AES a la API de cripto”, y esa precisión vive en la constante.
movzx, tight loop, conteo de bloques). El AES por WinAPI queda fuera de alcance por el motivo opuesto en apariencia pero idéntico en el fondo: por una constante inmediata (CALG_AES). Uno es demasiado “de bajo nivel” (algoritmo a mano), el otro demasiado “de alto nivel” (API estándar), y sin embargo los dos exigen mirar dentro de una instrucción. La conclusión de la serie, condensada: no importa si el comportamiento es primitivo o sofisticado; importa si su evidencia distintiva es un nombre (import, cadena, sección, export → estático) o un valor/forma dentro de una instrucción (number, mnemonic, characteristic → dinámico). El cifrado, casi siempre, es lo segundo.Así, capa-real retrata al ransomware por sus bordes —borra las copias, menciona criptomonedas, deja la nota— pero no siempre puede confirmar el acto central de cifrar. Para un defensor es una lección de encuadre: las señales periféricas del ransomware son baratas y estáticas; la confirmación del cifrado es cara y dinámica.
Compiladores y packers: el cien por cien de capa-real#
Cerramos con el bloque donde el file-scope no cede nada. Identificar con qué se compiló o empaquetó un binario es, por naturaleza, estático: el toolchain deja huellas indelebles en cadenas, símbolos y secciones. compiled with Go es el arquetipo:
features:
- or:
- string: /^Go build ID:/
- substring: "go.buildid"
- string: /\bgo1\.\d/
- substring: "runtime.main"
- substring: "main.main"
Regla compiler/go/compiled-with-Go (recortada) — capa-rules (Mandiant, Apache-2.0).
compiled with Go: EXACTA
— El runtime de Go incrusta su build ID y los nombres de sus símbolos (runtime.main); no hay forma de compilar Go sin dejarlos. Lo mismo con compiled with Nim (símbolos NimMain, ficheros system.nim) EXACTA
, py2exe, AutoIt, y el resto de la familia.
Y los packers, igual. packed with VMProtect los caza por sus secciones y por los mensajes que incrusta su stub:
features:
- or:
- string: "A debugger has been found running in your system."
- section: .vmp0
- section: .vmp1
- section: .vmp2
Regla anti-analysis/packer/vmprotect/packed-with-VMProtect (recortada) — capa-rules (Mandiant, Apache-2.0).
packed with VMProtect: EXACTA
, como packed with UPX (secciones UPX0/UPX1, Parte 0). Secciones y cadenas: file-scope puro.
capa-real dice “esto es Go” o “esto está empaquetado con VMProtect”, acierta como capa, sin reservas, porque la evidencia es una propiedad indeleble del fichero. No todo cae de este lado —compiled with Rust, por ejemplo, se apoya en features más finas y queda fuera—, pero el grueso de la familia es el terreno donde el triage estático es, sencillamente, suficiente.El mapa de la frontera, cuarta entrega#
| Capacidad | La evidencia que la distingue | En capa-real |
|---|---|---|
| Comunicación C2 (HTTP/socket/DNS) | Imports de red (InternetOpenUrl, send) | Aproximada |
| Keylogging por sondeo | Imports de teclado (GetAsyncKeyState) | Aproximada |
| Captura de pantalla | Secuencia GDI acoplada en una función | Fuera de alcance |
| Borrar copias de sombra | Cadenas (vssadmin delete shadows) | Aproximada |
| Referencias a criptomoneda / nota | Cadenas (wallets, jerga de rescate) | Exacta |
| Cifrar (AES-WinAPI, RC4) | Constante CALG_AES / forma de instrucción | Fuera de alcance |
| Compilador (Go, Nim, py2exe) | Cadenas y símbolos del toolchain | Exacta |
| Packer (UPX, VMProtect) | Secciones y cadenas del stub | Exacta |
El retrato final del kill chain confirma el principio de toda la serie. La red y la colección por API son aproximables porque el malware, para actuar, tiene que importar funciones nombrables. El cifrado se escapa porque su evidencia distintiva es una constante o una forma de instrucción. Y la identidad del binario —quién lo compiló, quién lo empaquetó— es exacta porque es una propiedad estática e indeleble. El file-scope no ve “lo que el malware hace en vivo”; ve “lo que el malware es y lo que declara saber hacer”. Para triage, resulta que eso es muchísimo.
Para el defensor#
Qué llevarse a la práctica.
- La huella de compilador es oro de triage y clustering. Saber que una muestra es Go, Nim o Rust orienta el reversing (convenciones de llamada, runtime), agrupa familias y a veces delata al actor (el auge de Nim/Rust en loaders recientes no es casual). Es información que tu pipeline estático extrae gratis y con certeza —extráela siempre—.
- Las señales periféricas del ransomware son baratas; el cifrado, caro.
vssadmin delete shadows, una nota de rescate, una dirección de wallet, imports deadvapi32/bcrypt: todo eso lo cazas en estático y basta para elevar la prioridad. Pero confirmar el cifrado masivo —y frenarlo— exige comportamiento en vivo: picos de entropía en escrituras de fichero, renombrados en masa. No esperes que el estático te dé esa confirmación. - Presencia de API de red ≠ C2. Igual que
capa-real, tu triage veráInternetOpenUrlows2_32.sendy sabrá que la pieza puede comunicarse; no verá con quién. La atribución del C2 (dominios, IPs, patrones de baliza) vive en cadenas —a veces— y en la ejecución —siempre—. Empareja el indicio estático del import con la telemetría de red, como en la kill chain de Nightmare Eclipse.
Con esto cerramos el recorrido por el corpus. En la Parte 5, la última, cambiamos de asiento: dejamos de leer reglas y abrimos el motor que las evalúa. capa-real, por dentro —el compilador que traduce el YAML a un pack ejecutable en el navegador, el evaluador del árbol lógico, el gate del 57,9 %, y los gotchas que aparecen cuando uno intenta, de verdad, leer el comportamiento de un binario sin desensamblarlo—.
Referencias#
- capa-rules (Apache-2.0): https://github.com/mandiant/capa-rules
- MITRE ATT&CK — Application Layer Protocol (T1071): https://attack.mitre.org/techniques/T1071/
- MITRE ATT&CK — Input Capture: Keylogging (T1056.001): https://attack.mitre.org/techniques/T1056/001/
- MITRE ATT&CK — Inhibit System Recovery (T1490): https://attack.mitre.org/techniques/T1490/
- Malware Behavior Catalog — Cryptography: https://github.com/MBCProject/mbc-markdown/blob/main/micro-behaviors/cryptography/README.md
- Sikorski & Honig — Practical Malware Analysis, lab 11-03 (keylogging por sondeo): https://nostarch.com/malware