Cargas útiles de malware y balizas: tipos de cargas útiles maliciosas
En la publicación de blog anterior, discutimos cómo las cargas útiles facilitan las comunicaciones maliciosas y cómo un atacante puede obtener el control de un sistema luego de ejecutar con éxito las cargas útiles y las balizas. Empleamos un canal de comunicaciones reverse_tcp facilitado por la carga útil de Meterpreter (del Marco Metasploit).
Esta publicación se centrará en comprender más sobre los distintos tipos de cargas útiles y ejemplos de las técnicas de manipulación de memoria que pueden emplear.
El malware se volvió más sofisticado en composición y ejecución, especialmente en contraste con los días de virus más simples, como el programa Creeper a principios de los años setenta. Para los actores de amenazas motivados principalmente por objetivos clandestinos, permanecer bajo el radar para persistir en una red sin ser detectados durante un periodo prolongado suele ser un enfoque principal. Por lo tanto, emplearán las técnicas correspondientes, como señuelos, codificación, ofuscación, cifrado e imitación para lograr el nivel deseado de seguridad operativa.
Hay varios formatos ejecutables disponibles para un actor de amenazas. La elección del atacante depende de los vectores de ataque iniciales y de las acciones posteriores a la explotación. Estos son algunos de los formatos ejecutables y de transformación de Metasploit Framework.

Como se demostró en la primera parte, la baliza o carga útil es el implante en una máquina o red víctima que le da al atacante una entrada y luego un punto de apoyo. Es una parte importante del arsenal de malware y del ciclo de vida general del ataque, lo que permite al actor de amenazas acceder activamente a más actividades maliciosas.
Cuando se trata de la categorización amplia real, una carga útil puede estar "preparada" o "sin etapas". Un actor de amenazas puede elegir uno sobre el otro dependiendo de varios factores, el principal de los cuales pueden ser consideraciones de seguridad operativa.
¿Qué son las cargas útiles por etapas?
Las cargas por etapas desglosan las distintas fases de un ataque, a menudo empleando múltiples fases de cargas útiles que una sola carga útil realizó de otro modo. Estas cargas útiles generalmente se dividen en un ejecutable "stager" (carga útil inicial o baliza) y un ejecutable "stage" (carga útil principal).
Un stager es un pequeño ejecutable que es una carga inicial. Es un fragmento de código relativamente pequeño que se ejecuta para preparar para una carga útil mucho más grande y capaz conocida como carga útil de etapa. Esto significa que "el stager prepara el escenario". Un stager generalmente será parte de algún código de explotación cuando la entrada inicial aproveche una vulnerabilidad. Aquí, el código de explotación explotará con éxito la vulnerabilidad de destino y luego ejecutará el código de ensayo (carga útil). El stager luego entra en acción.
La tarea principal del stager es ejecutar correctamente sin ser detectado, volver a comunicar con la infraestructura del atacante para descargar la carga principal deseada y luego configurar el sistema para ejecutar esa carga útil. La etapa descargada o la carga principal más grande pueden ser una o más cargas útiles según la capacidad requerida por el atacante. Una vez descargada la fase, el preparador pasa el control de ejecución para continuar con la actividad maliciosa.
En el caso de un exploit de vulnerabilidad, el programa de exploit inicial hace algo similar para el stager que el stager para la carga útil de la etapa principal, en términos de asignación de recursos en el sistema comprometido. Seguirá un patrón similar a este:

En el ejemplo siguiente se muestra una carga preconfigurada como un ejecutable de Windows.

Las cargas provisionales se adaptan a escenarios en los que puede haber restricciones relacionadas con el sistema, como el espacio en disco y memoria, cuando se trata de la entrega y ejecución de la carga, como en el caso del shellcode empleado para explotar una vulnerabilidad de desbordamiento de búfer.
¿Qué son las cargas útiles sin etapas?
Lo opuesto a las cargas útiles por etapas son las cargas útiles sin etapas. Las cargas útiles sin etapas son autónomas y generalmente mucho más grandes que las cargas útiles por etapas. Por lo general, combinan todas las capacidades requeridas de un atacante en un ejecutable.
Aquí, normalmente no hay necesidad de una carga útil inicial (stager) que descargue la carga principal (stager). Una vez que se ejecuta la carga útil sin etapas, tendrá todas las capacidades necesarias para realizar acciones maliciosas, como inyección de memoria, llamada a la infraestructura del atacante y entrega de un shell para el atacante.
En el ejemplo siguiente se muestra una carga útil tcp inversa de Meterpreter sin etapas como un ejecutable de Windows.

La decisión sobre qué tipo de carga útil usar como parte de una campaña maliciosa se conoce como "consideraciones de seguridad operativa" para ese actor de amenazas. El tipo de infraestructura del atacante correspondiente que respalda la campaña maliciosa está influenciado en parte por estas consideraciones.
Cargas útiles por etapas vs. sin etapas
Las cargas útiles por etapas de Metasploit tienen el símbolo de barra diagonal ( / ) luego de la palabra Meterpreter. En la captura de pantalla siguiente se muestran ejemplos de cargas útiles de Meterpreter preconfiguradas de Windows.

Las cargas sin etapas emplean el símbolo de subrayado ( _ ) luego de la palabra Meterpreter. En la captura de pantalla siguiente se muestran ejemplos de cargas sin etapas de Windows Meterpreter.

Y el siguiente ejemplo muestra ambas categorías de cargas útiles.

Las cargas útiles sin etapas son autónomas y no requieren el paso adicional de enviar una etapa (carga útil principal) a la máquina víctima una vez que el malware realiza una devolución de llamada a la infraestructura del atacante. Observe en la captura de pantalla a continuación que después de que se inicia el controlador TCP inverso, el siguiente paso es abrir una sesión práctica de shell remoto de Meterpreter en la máquina víctima de inmediato, sin la necesidad de enviar más cargas útiles, como una etapa.

En la captura de pantalla a continuación, podemos ver que existe el paso adicional de enviar la etapa después de que el stager realice una devolución de llamada a la infraestructura del atacante: "enviar la etapa (175174) a 203.0.113.1".

Otra diferencia entre etapas y sin etapas es el tamaño de las cargas útiles. En la captura de pantalla siguiente, la carga sin etapas (meeting_update_stageless.exe) es mucho más grande con 245 KB en comparación con la carga inicial por etapas (web1_meeting_update.exe) con 73 KB.

¿Qué es shellcode?
Shellcode es un código malicioso que intenta secuestrar el flujo normal de un programa en ejecución en la memoria de la computadora. Luego redirige el flujo para que se ejecute el código malicioso, en lugar del programa normal, lo que le da al atacante un shell o acceso práctico. A menudo se trata de balizas o cargas útiles en forma de código de programación de bajo nivel o un código de máquina combinado con un exploit. Los exploits son piezas de código nativo o de bajo nivel que aprovechan con éxito una vulnerabilidad.
Las vulnerabilidades explotadas a menudo implican un desbordamiento de búfer en la memoria de una aplicación donde el atacante invadió la memoria asignada para redirigir el flujo normal del programa. Un exploit exitoso conducirá a la ejecución de una carga útil, que es el malware.
En sus formas más puras, Shellcode será código nativo o ensamblador comúnmente empleado en exploits relacionados con la memoria.
En el ejemplo siguiente se muestra el código de shell de Powershell (ps1).

En este ejemplo concreto se usa una biblioteca de vínculos dinámicos (DLL) de Windows insertada en la memoria a través de un cargador reflectante. El shellcode se genera en forma alfanumérica. Una vez ejecutado con éxito, puede volver a conectarse al atacante a través de una sesión TCP de DNS inversa generada desde Metasploit Framework.
La elección del mecanismo de entrega, el tipo de exploit y el sistema de destino vulnerable determinan la elección de la baliza o la carga útil vinculada al ataque. El exploit se emplea para aprovechar una aplicación vulnerable antes de obtener acceso al sistema operativo subyacente. En tal caso, se puede emplear código específico para la aplicación correspondiente (por ejemplo, PHP o ASP para aplicaciones front-end de servidor sitio web).
Características de Shellcode
Hay algunas consideraciones y características importantes para garantizar la ejecución exitosa de Shellcode y mantener una alta seguridad operativa.
El código debe:
- Tenga todas las instrucciones necesarias para ejecutar el shell deseado sin dejar de ser relativamente pequeño en tamaño.
- Sea "independiente de la posición" en la memoria: esto es crucial ya que a menudo no es posible saber de antemano dónde se cargará en la memoria del proceso vulnerable objetivo.
- No contener nada que pueda causar errores potenciales o hacer que todo el proceso se bloquee; por ejemplo, debido a caracteres nulos (0x00).
- Ser capaz de aprovechar alguna asignación de memoria existente empleando algunas técnicas de inyección: inyección de código o reflexión.
A partir de este punto, el atacante deberá seleccionar el tipo de ejecutable posterior a la explotación adecuado para ejecutar en el sistema de destino, como EXE o DLL para Windows, ELF para Linux y APK para Android. Una vez más, se prefieren las técnicas posteriores a la explotación de solo memoria para aumentar la seguridad operativa.
¿Qué es la inyección de código y la inyección de DLL?
La inyección de DLL es el proceso de ejecución de código (DLL) en el contexto de otro proceso. Las cargas útiles de Meterpreter emplean técnicas de inyección DLL para mecanismos de sigilo y evasión.
En Windows, una biblioteca de vínculos dinámicos o DLL ("Biblioteca compartida" en Linux) es un fragmento de código almacenado como un archivo de biblioteca compartida. Esto significa que puede ser empleado por diferentes programas informáticos cuando lo necesiten. El sistema operativo (en este caso, Windows) se encarga de escribir y cargar la biblioteca, lo que se realiza en tiempo de ejecución. Un programa puede simplemente llamar o hacer referencia al archivo DLL requerido para usar el código que contiene.
Esto es útil para los programadores porque solo escriben código una vez, lo compilan y almacenan como una biblioteca compartida o DLL, luego lo usan cuando sea necesario y por múltiples programas.
La principal diferencia entre un archivo DLL y un archivo EXE es que un archivo DLL no se puede ejecutar de forma independiente. Necesita un programa como un EXE para llamar o hacer referencia y luego ejecutarlo. En el ejemplo siguiente se muestran los archivos DLL en un sistema operativo Windows, que normalmente se almacenan en la carpeta C:\Windows\ WinSxS (WinSxS significa Windows Side-by-Side).

Las capacidades de las DLL también las hacen muy útiles para los actores de amenazas. La inyección de código en el nivel básico implica un intento por parte de un proceso (malicioso) de anexar (u obtener un identificador) a un proceso remoto (proceso víctima). A continuación, asigna suficiente memoria o cambia las licencias de página en el proceso víctima para ejecutar código nuevo, como un archivo DLL, luego de lo cual copia (inserta) el código malicioso DLL en el espacio de memoria del proceso víctima nuevo o que ya se está ejecutando.
A continuación, se inicia un nuevo subproceso, que es la forma en que los procesos ejecutan tareas específicas, en el proceso víctima para ejecutar las instrucciones contenidas en el código insertado o DLL.
Un subproceso comparte el mismo espacio de memoria que el proceso que lo inició, mientras que los diferentes procesos tienen distintos espacios de memoria asignados, especialmente en los casos en que no comparten ninguna variable. Esto lo aplica el sistema operativo. Sin embargo, los sistemas operativos proporcionan mecanismos para que los procesos se comuniquen cuando sea necesario mediante la comunicación entre procesos (IPC), como canalizaciones (con nombre o anónimas), sockets, semáforos, memoria compartida y colas de mensajes.
En los sistemas operativos Windows, la inyección de código implica el uso de API y funciones legítimas de Windows con fines maliciosos. Por ejemplo:
- OpenProcess se usa para obtener un identificador en un proceso,
- VirtualAllocEx facilita la asignación de suficiente memoria en ese proceso remoto o,
- VirtualProtectEx se puede usar para invalidar las licencias de memoria (página) y, a continuación,
- WriteProcessMemory escribe el código malicioso, como un archivo DLL, en el proceso víctima.
- CreateRemoteThread, RtlCreateUserThread o NtCreateThreadEx se usa para crear un nuevo subproceso (que es la forma en que los procesos ejecutan tareas específicas) y ejecutar la capacidad maliciosa, como robar credenciales o ejecutar ransomware.
Para cargar un archivo DLL en Windows, es necesario llamar a las funciones LoadLibraryA o LoadLibraryExA, que forman parte de libloaderapi.h. Estas funciones, como dice Microsoft, "cargan el módulo especificado en el espacio de direcciones del proceso de llamada". El uso de LoadLibrary para cargar un archivo DLL significa que los archivos DLL deben cargar desde el disco.
Sin embargo, con la técnica de inserción de DLL reflectante, se puede cargar un archivo DLL directamente desde la memoria, una funcionalidad que LoadLibrary no ofrece actualmente. Un actor de amenazas puede usar la inyección de DLL reflectante para autocargar su código malicioso completamente en la memoria sin la necesidad de invocar el cargador nativo de Windows en el disco. Aquí, emplean un cargador personalizado en lugar de Windows LoadLibrary.
Los atacantes también pueden usar otras técnicas de inyección y manipulación de procesos, como:
- Vaciado de procesos : donde el malware iniciará un proceso de la víctima en un estado suspendido. Luego vacía la memoria para dejar espacio para el nuevo código, cambia las licencias de la página, inyecta código malicioso y reanuda el proceso para ejecutar el código malicioso inyectado.
- Carga lateral de DLL : donde un programa de Windows vulnerable legítimo y a menudo más antiguo (proceso de víctima) se ve obligado a cargar una DLL maliciosa, que deliberadamente se denomina igual que una legítima esperada por la víctima y se coloca en el mismo directorio (lado a lado) que el programa vulnerable. El proceso del programa víctima buscará primero en su carpeta inmediata para localizar la DLL maliciosa renombrada (suplantada). Esta técnica aprovecha el orden de búsqueda de DLL empleado por el cargador de Windows para tener éxito.
Estas técnicas intentan hacer que la actividad maliciosa parezca legítima, evadiendo así la detección para persistir en un sistema comprometido.
A partir de las técnicas de manipulación de procesos discutidas en la primera parte de este serial, podemos ver a continuación que la migración de la web1_meeting_update.exe maliciosa original implicó alguna inyección de código.
En este ejemplo, la carga maliciosa inició (generó) un proceso de notepad.exe completamente nuevo como el proceso de víctima deseado para inyectar código. El proceso malicioso original (PID 2472) se inyecta o migra al nuevo proceso notepad.exe víctima (PID 1768).

Notepad.exe es un proceso confiable de Microsoft y, como tal, esto le da al atacante la capacidad de disfrazar el proceso original de aspecto malicioso por uno más confiable. Notepad.exe es un editor de texto básico de Windows que no necesita llamar (hacer referencia) a ninguna API, función o DLL de Windows requerida para la conexión de red, que en nuestro ejemplo, el proceso malicioso ejecutó.
Se requiere un análisis dinámico para detectar la inyección que se produce en la memoria. El mapeo de dependencias de aplicaciones también es necesario para detectar las comunicaciones de red sospechosas del editor de texto, en este caso notepad.exe proceso, realizar una conexión de red.
Conclusion
Siguiendo con la primera parte de este serial de blogs, analizamos algunas de las categorías y tipos de cargas útiles que un actor de amenazas puede usar y por qué pueden decidir usar un tipo sobre otro.
La mayoría de los atacantes de amenazas harán todo lo posible para cerciorar de tener una entrada inicial exitosa, mantener la persistencia y evitar la detección. Hasta ahora vimos que la combinación de herramientas, técnicas y procedimientos significa que se necesita una combinación igualmente capaz de herramientas y procedimientos de seguridad para evitar un ataque exitoso, o más probable que asuma una violación y evite que un incidente cibernético inicial se convierta en una violación importante.
En la parte final de este serial, discutiremos algunas técnicas y capacidades más y, lo más importante, exploraremos el análisis y la mitigación.