TLP:CLEARSerie de estudio, defensiva y educativa. Continúa la Parte 2. Todo el YAML citado pertenece a **capa-rules de Mandiant** (Apache-2.0), commit `aed45e2`; se lee, no se construye nada. Los veredictos «En capa-real» se verificaron contra el pack real que embarca APT115.
Dónde estamos. Las Partes 1 y 2 fueron un catálogo de derrotas honestas del file-scope: la cripto por algoritmo, el anti-debug por instrucción, la inyección y la persistencia compuestas caen todas del lado difícil. Prometí que la frontera se movería. Esta parte lo cumple: el acceso a credenciales y el descubrimiento son, en buena medida, evaluables sin desensamblar —el atacante que roba secretos deja rutas de fichero, cadenas y consultas SQL a la vista—. La excepción es la evasión por parcheo en memoria (AMSI, ETW), que sigue siendo territorio del desensamblado. Tres sub-tácticas, tres veredictos distintos.

Evasión por tampering: apagar la vigilancia desde dentro#

El malware moderno rara vez “desactiva el antivirus”: lo desarma quirúrgicamente en memoria. Los dos objetivos favoritos son AMSI (Antimalware Scan Interface, la interfaz por la que Windows ofrece el contenido —scripts, macros— a Defender para que lo escanee) y ETW (Event Tracing for Windows, el bus de telemetría del que se nutren EDR y auditoría). Neutralizar cualquiera de los dos sigue la misma receta: localizar la función clave en la DLL cargada (AmsiScanBuffer, EtwEventWrite), hacer su página de memoria escribible, y sobrescribir sus primeros bytes con un retorno inmediato. A partir de ahí, el escaneo o la traza devuelven “todo bien” sin mirar nada.

Miren cómo capa modela el parcheo de AMSI:

rule:
  meta:
    name: patch Antimalware Scan Interface function
    namespace: anti-analysis/anti-av
    scopes:
      static: function
    att&ck:
      - Defense Evasion::Impair Defenses::Disable or Modify Tools [T1562.001]
    mbc:
      - Defense Evasion::Disable or Evade Security Tools [F0004]
  features:
    - and:
      - match: change memory protection
      - or:
        - string: "AmsiScanBuffer"
        - string: "AmsiScanString"
        - string: "AmsiOpenSession"
        - string: "AmsiInitialize"
      - optional:
        - match: write process memory
        - string: "amsi.dll"

Regla anti-analysis/anti-av/patch-Antimalware-Scan-Interface-function — capa-rules (Mandiant, Apache-2.0).

La estructura es reveladora. La segunda condición es un or de cadenasAmsiScanBuffer, etc.—, que capa-real vería sin problema. Pero la primera condición, obligatoria, es match: change memory protection: la misma regla que en la Parte 1 resultó estar fuera del alcance del file-scope, porque capa la modela a nivel de bloque exigiendo la llamada a VirtualProtect junto a su constante. El parcheo de ETW (T1562.001 , misma match: change memory protection) es idéntico, cambiando las cadenas Amsi* por EtwEventWrite / NtTraceEvent.

patch AMSI · patch ETW: FUERA DE ALCANCE — Y es coherente con toda la serie: el tampering no es “importa una API”, es “modifica el código de otra función en memoria”. La cadena AmsiScanBuffer es visible, sí, pero por sí sola no distingue al que parchea AMSI del que simplemente lo invoca de forma legítima. Lo que los separa —el cambio de protección de memoria seguido de una escritura sobre esa función— vive en el plano dinámico. El borrado de logs (clear Windows event logs, T1070.001 ) corre la misma suerte por una razón emparentada: mezcla ramas de API con ramas de basic block/call que consultan wevtutil, y capa-real no reconstruye ese acoplamiento.

Un patrón que ya es ley. Tres partes seguidas, el mismo diagnóstico para la evasión de gama alta: anti-debug (Parte 1), inyección (Parte 2), tampering de AMSI/ETW (aquí). Todo lo que manipula el estado en ejecución —tiempo, memoria de otra función, banderas del procesador— es intrínsecamente dinámico y queda fuera del file-scope. El análisis estático puede decirte que un binario sabe cómo se llama AmsiScanBuffer; no puede decirte que lo va a parchear. Para esa clase de comportamiento, el sandbox no es un lujo: es el único instrumento.

Credenciales: donde el file-scope recupera terreno#

Aquí gira la historia. Robar credenciales consiste, casi siempre, en saber dónde viven —qué fichero, qué clave de registro, qué base de datos— y esa geografía se escribe con cadenas y rutas que el binario lleva incrustadas. Es el terreno natural del análisis estático.

Secretos en la nube: rutas de fichero, sin más#

El ejemplo más nítido. Para robar las credenciales de AWS, el malware lee el fichero donde el CLI las guarda. La regla es un catálogo de esas rutas:

rule:
  meta:
    name: access AWS credentials
    namespace: collection/cloud/aws
    scopes:
      static: function
    att&ck:
      - Credential Access::Unsecured Credentials::Credentials In Files [T1552.001]
    references:
      - https://unit42.paloaltonetworks.com/teamtnt-operations-cloud-environments/
  features:
    - or:
      - string: ".aws/config"
      - string: ".aws/credentials"
      - string: ".boto"
      - string: ".s3cfg"
      - string: ".s3ql/authinfo2"

Regla collection/cloud/aws/access-AWS-credentials (recortada) — capa-rules (Mandiant, Apache-2.0).

Un or de rutas, nada más. Sus hermanas para GCP (.config/gcloud/credentials.db), Docker (.docker/config.json), Kubernetes y Cloudflare (/etc/cloudflared/config.yml) tienen exactamente la misma forma. Referencia a la operación de TeamTNT en la nube: es el modus operandi de los ladrones de secretos cloud, y consiste en leer rutas conocidas.

access AWS/GCP/Docker/Cloudflare credentials: APROXIMADAScapa-real las caza todas. Un matiz de precisión digno de la Parte 0: el scope estático de estas reglas es function, no file —capa quiere la ruta dentro de la función que la usa—, así que técnicamente son aproximaciones, no exactas. capa-real degrada el scope: confirma que la cadena .aws/credentials está en el binario, sin poder atarla a la función que la lee. Para robo de secretos cloud, esa señal basta y sobra: pocos binarios legítimos incrustan .s3ql/authinfo2.

Contraseñas de navegador: una regla exacta y sofisticada#

El caso más bonito de la parte. Robar las contraseñas de Chrome/Edge significa leer su base SQLite Login Data y ejecutar un SELECT sobre la tabla logins. capa modela ese comportamiento con una precisión quirúrgica —y, notablemente, a scope file—:

rule:
  meta:
    name: gather chrome based browser login information
    namespace: collection/browser
    scopes:
      static: file
    att&ck:
      - Credential Access::Credentials from Password Stores::Credentials from Web Browsers [T1555.003]
  features:
    - and:
      - or:
        - string: /\\+(Edge|Chrome|Chromium|Brave...|YandexBrowser|Orbitum|...)\\+User Data\\+Default(\\+Network)?\\+(Cookies|Login Data)/i
        - string: /\\Opera Software\\Opera Stable\\(Login Data|Cookies)/i
      - or:
        - string: /SELECT ((...|username_value|password_value),?\s?)+ FROM logins/i
        - 2 or more:
          - substring: "username_value"
          - substring: "password_value"
          - substring: "origin_url"
          - substring: "signon_realm"
          - substring: "action_url"

Regla collection/browser/gather-chrome-based-browser-login-information (recortada) — capa-rules (Mandiant, Apache-2.0).

Léanla como dos exigencias encadenadas por and: (a) referencia la ruta de la base de datos de un navegador basado en Chromium, y (b) contiene la consulta SQL que extrae las credenciales —bien la sentencia SELECT ... FROM logins completa, bien dos o más de los nombres de columna (password_value, signon_realm, action_url…)—. Ese 2 or more sobre substring es la gramática de la Parte 0 dándole robustez: aunque el atacante construya la consulta por partes, si aparecen dos de esas columnas, cae.

gather chrome based browser login information: EXACTA — Scope file, hojas todas de cadena/substring: capa-real la evalúa idéntica a capa. Es la demostración de que “exacto” no significa “simple”: esta regla es de las más elaboradas del ruleset y aun así vive entera en el plano estático, porque todo su vocabulario son cadenas que el ladrón necesita llevar incrustadas para saber qué robar y cómo consultarlo.

Y un uso elegante de export#

Un tercer sabor de credencial, para completar el repertorio de features de la Parte 0. Una DLL maliciosa puede registrarse como proveedor de red para interceptar credenciales de logon; para ello debe exportar funciones con nombres fijos que Windows busca:

  features:
    - and:
      - export: NPGetCaps
      - or:
        - export: NPLogonNotify
        - export: NPPasswordChangeNotify

Regla persistence/authentication-process/act-as-credential-manager-DLL — capa-rules (Mandiant, Apache-2.0).

La feature export: mira la tabla de exportaciones del binario —información puramente estática, de cabecera—. act as credential manager DLL: EXACTA . Es un recordatorio de que el file-scope no es solo “cadenas e imports”: los nombres exportados, las secciones y el formato son igual de estáticos y capa los explota.

LSASS y el Credential Manager: por API#

El robo de credenciales de sistema —volcar la memoria de LSASS, leer el Credential Manager— se detecta por la API que lo hace. create process memory minidump es, en su núcleo, una sola hoja:

  features:
    - or:
      - api: dbghelp.MiniDumpWriteDump

MiniDumpWriteDump (de dbghelp.dll) es la función canónica para volcar la memoria de un proceso —el método clásico de dump de LSASS—. create process memory minidump · acquire credentials from Windows Credential Manager: APROXIMADAS : capa-real confirma el import. No puede afirmar que el objetivo sea LSASS (eso está en el argumento, en runtime), pero un binario ajeno a la depuración que importa MiniDumpWriteDump es sospechoso por sí mismo.

Descubrimiento: reconocimiento por presencia de API#

El último eslabón antes de la acción es el reconocimiento: enumerar procesos, sondear el sistema, listar servicios. Se hace con APIs de Windows bien conocidas, y por eso el file-scope lo cubre con holgura. enumerate processes (T1057 ) es un buen ejemplo, y de paso repite la lección del optional de la Parte 2:

  features:
    - or:
      - api: System.Diagnostics.Process::GetProcesses
      - and:
        - api: kernel32.Process32First
        - api: kernel32.Process32Next
        - optional:
          - basic block:
            - and:
              - or:
                - number: 0xF = TH32CS_SNAPALL
                - number: 0x2 = TH32CS_SNAPPROCESS
              - api: kernel32.CreateToolhelp32Snapshot

Regla host-interaction/process/list/enumerate-processes (recortada) — capa-rules (Mandiant, Apache-2.0).

Los requisitos duros son APIs (Process32First/Process32Next, el patrón clásico de recorrido de procesos). Las constantes number incómodas —TH32CS_SNAPPROCESS— viven, otra vez, bajo optional, así que capa-real las poda y se queda con las APIs. enumerate processes: APROXIMADA . Lo mismo vale para get system information on Windows (T1082 : GetSystemInfo, NtQuerySystemInformation) y para la enumeración de servicios: reconocimiento por presencia de API, donde capa-real es fuerte.

El mapa de la frontera, tercera entrega#

CapacidadLa evidencia que la distingueEn capa-real
Parchear AMSI / ETWchange memory protection + cadena de la funciónFuera de alcance
Borrar logs de eventosAPI + wevtutil acoplados por scopeFuera de alcance
Robar credenciales cloudRutas de fichero (.aws/credentials)Aproximada
Robar contraseñas de navegadorRuta de la BD + consulta SQLExacta
DLL proveedora de credencialesExports (NPGetCaps)Exacta
Volcar LSASS / Credential ManagerImport (MiniDumpWriteDump)Aproximada
Enumerar procesos / sistemaImports (Process32Next…)Aproximada

El contraste con las dos partes anteriores es el titular de esta. Donde la evasión, la inyección y la cripto avanzada le ganaban al file-scope, las credenciales y el descubrimiento se lo entregan. La razón es de fondo y vale como principio: para robar un secreto o reconocer un sistema, el malware tiene que nombrar lo que busca —la ruta del fichero, la columna SQL, la API del recorrido—, y nombrar es dejar evidencia estática. Solo el tampering en memoria, que no roba ni nombra nada sino que reescribe código ajeno, se mantiene invisible. La frontera no es arbitraria: separa lo que el malware declara de lo que el malware hace en vivo.

Para el defensor#

Qué llevarse a la práctica.

  • Las rutas de credenciales son detección de altísima señal. .aws/credentials, .docker/config.json, Login Data, signon_realm incrustados en un ejecutable son casi siempre robo de secretos: se traducen directo a reglas YARA de cadenas con muy pocos falsos positivos. Es el bloque donde tu triage estático es tan bueno como capa —aprovéchalo—.
  • MiniDumpWriteDump fuera de un depurador = candidato a dump de LSASS. El import por sí solo justifica triage; la confirmación (que el proceso objetivo sea lsass.exe) exige telemetría de ejecución. Empareja el indicio estático con la vigilancia de accesos a LSASS (Sysmon EventID 10) para cerrar el caso.
  • Para el tampering, asume que el estático es ciego. Un binario que referencia AmsiScanBuffer o EtwEventWrite puede estar parcheándolos o usándolos legítimamente; el file-scope no los distingue —como no los distingue capa-real—. La señal real es dinámica: la escritura sobre las páginas de amsi.dll/ntdll.dll, o la caída súbita de eventos ETW. Es el mismo principio de Nightmare Eclipse —cuando no puedes impedir ni ver el ataque en reposo, la pregunta pasa a ser “¿qué se observa cuando ocurre?"—.

En la Parte 4 llegamos al final del kill chain: C2, colección e impacto. Veremos la comunicación de red (HTTP, socket, DNS), el keylogging y la captura de pantalla, el ransomware —cifrar y arrasar las copias de seguridad— y el bloque donde capa-real brilla al 100 %: las huellas de compiladores y packers. Y cerraremos el recorrido por el corpus antes de abrir, en la Parte 5, el motor por dentro.

Referencias#