DotDumper es un desempaquetador y grabador automático para archivos de objetos de DotNet Framework.La herramienta ya está en Sombrero negro América 2022.
Detectar y clasificar automáticamente cualquier archivo dado de manera confiable generalmente se considera el santo grial del análisis de malware. Las pruebas y tribulaciones para llegar allí son muchas, por lo que la creación de un sistema de este tipo es muy apreciada. Cuando se trata de binarios de destino de DotNet, nuestra nueva herramienta de código abierto, DotDumper, está diseñada para ayudar en varios pasos clave: registrar actividad (en memoria), volcar segmentos de memoria interesantes y extraer funciones de una muestra determinada.
Tabla de Contenidos
¿Por qué elegir DotDumper?
En resumen, el desembalaje manual es un proceso tedioso que consume una cantidad de tiempo desproporcionada para los analistas. Los binarios ofuscados aumentan aún más el tiempo que tarda un analista en descomprimir un archivo determinado. Para escalar esto, las organizaciones necesitan una gran cantidad de analistas que analicen el malware a diario, posiblemente junto con un espacio aislado escalable. Se podría perder un tiempo precioso buscando actividades interesantes o muestras para descubrir nuevas amenazas, en lugar de malware genérico que está muy extendido. Después de todo, los analistas buscan una aguja en un pajar.
Entonces, ¿qué tiene de diferente DotDumper? La ejecución de una muestra de malware basada en DotNet a través de DotDumper proporcionó archivos de registro de llamadas de funciones críticas, contextualizadas y comunes en tres formatos (texto sin formato legible por humanos, JSON y XML), así como copias de segmentos de memoria útiles. Por lo tanto, los analistas pueden navegar por los registros de llamadas de funciones. Además, los archivos volcados se pueden escanear para clasificarlos, lo que brinda información adicional sobre las muestras de malware y los datos que contienen. Esto reduce el tiempo crítico para el proceso de triaje y respuesta a incidentes y libera a los analistas e investigadores del SOC para necesidades de análisis más complejas.
característica
Para registrar y volcar llamadas a funciones contextuales y sus resultados, DotDumper utiliza una combinación de enlaces de reflexión y administrados, todo escrito en C# puro. A continuación, se destacarán y explicarán las características clave junto con un extracto de los resultados de la muestra del ladrón AgentTesla empaquetada por DotDumper, cuyos valores hash son los siguientes.
tipo de hash | valor hash |
---|---|
Algoritmo SHA-256 | b7512e6b8e9517024afdecc9e97121319e7dad2539eb21a79428257401e5558d |
SHA-1 | c10e48ee1f802f730f41f3d11ae9d7bcc649080c |
MD-5 | 23541daadb154f1f59119952e7232d6b |
Uso de la interfaz de línea de comandos
Se puede acceder a DotDumper a través de una interfaz de línea de comandos con varios parámetros. La siguiente imagen muestra el menú de ayuda. Tenga en cuenta que no se tratan todos los parámetros, sino los más utilizados.
Lo mínimo requerido para ejecutar un ejemplo dado es proporcionar el argumento «-archivo» junto con un nombre de archivo o ruta de archivo. Si se proporciona una ruta completa, se utiliza. Si se proporciona un nombre de archivo, comprueba el directorio de trabajo actual y la carpeta donde se encuentra el ejecutable de DotDumper.
A menos que se proporcione un nombre de directorio, el nombre de la carpeta «-log» se establece igual al nombre del archivo de muestra sin extensión (si corresponde). Esta carpeta se encuentra en la misma carpeta que DotDumper, que contendrá los archivos de registro y volcado.
Para un punto de entrada alternativo a una biblioteca o binario, el punto de entrada debe anularse con «-overrideEntry true». Además, se debe proporcionar una clase completa, incluido el espacio de nombres mediante «-fqcn My.NameSpace.MyClass». Esto le dice a DotDumper qué clase elegir, que es donde se recupera el nombre de la función proporcionada (use «-functionName MyFunction»).
Si la función seleccionada requiere argumentos, la cantidad de argumentos y la cantidad de argumentos requeridos deben proporcionarse usando «-argc». Los tipos y valores de parámetros se proporcionarán como «string|myValue int|9». Tenga en cuenta que cuando se usan espacios en el valor, el argumento en la interfaz de la línea de comando debe estar entre comillas para garantizar que se pase como un solo argumento.
Otras opciones que se usan con menos frecuencia, como «-raceTime» o «-deprecated», son seguras en su configuración predeterminada, pero es posible que deban ajustarse en el futuro debido a los cambios en DotNet Framework. Actualmente están expuestos en la interfaz de línea de comandos para permitir cambios fácilmente si es necesario, incluso con versiones anteriores de DotDumper cuando llegue el momento.
registro y volcado
El registro y el volcado son dos funciones principales de DotDumper. Para minimizar el tiempo dedicado al análisis, el registro debe proporcionar contexto al analista. Esto se hace proporcionando al analista la siguiente información para cada llamada de función registrada:
- Rastreo de pila basado en la persona que llama a la función
- Información sobre el objeto ensamblado que realiza la llamada, como el nombre, la versión y el hash criptográfico
- El ensamblaje principal, desde el que se realizó la llamada si no era la muestra original
- Tipos, nombres y valores de parámetros de funciones
- El tipo, nombre y valor (si lo hay) del valor de retorno de la función
- lista de archivos volcados en el disco correspondientes a la llamada de función dada
Tenga en cuenta que para cada archivo de volcado, el nombre de archivo es igual al hash SHA-256 del archivo.
Para aclarar lo anterior, a continuación se incluye un extracto del registro. El extracto muestra los detalles del ejemplo anterior de AgentTesla, donde carga la segunda etapa usando la función Assembly.Load de DotNet.
Primero, se proporciona la hora del sistema local, junto con el tipo de retorno, el nombre y los parámetros de la función original. En segundo lugar, se proporciona un seguimiento de la pila, que muestra que la función principal del ejemplo apunta al constructor, inicializa el componente y llama a dos funciones personalizadas. La función Assembly.Load se llama desde «NavigationLib.TaskEightBestOil.GGGGGGGGGGGGGGGGGGGG(String str)». Esto proporciona al analista un contexto para encontrar el código que rodea esta llamada en caso de interés.
Luego, brinde información sobre la secuencia de llamadas de asamblea. Cuantas más etapas cargue, más complicado será ver por qué etapas progresó una llamada. Por lo general, se espera que una etapa cargue la siguiente, pero en algunos casos, las etapas posteriores utilizan etapas anteriores en un orden no lineal. Además, se proporciona información sobre los componentes originales para enriquecer aún más los datos del analista.
A continuación, se proporciona el hash principal. El padre de una etapa es la etapa anterior, que aún no existe en este ejemplo. Una etapa recién cargada tendrá esta etapa como su padre. Esto facilita a los analistas la correlación de eventos.
Finalmente, almacene el tipo y el valor de retorno de la función, así como el tipo, el nombre y el valor de cada parámetro pasado a la función enganchada. Si el tamaño de cualquier variable es superior a 100 bytes, se almacena en el disco. Luego inserte una referencia en el registro para hacer referencia al archivo en lugar de mostrar el valor. El umbral se establece para evitar problemas al imprimir registros, ya que algunas matrices tienen un tamaño de miles de índices.
reflexión
Según la documentación de Microsoft, la reflexión se resume mejor como «[…] Proporciona objetos que encapsulan ensamblajes, módulos y tipos». En resumen, esto permite que las clases y funciones de DotNet se creen y llamen dinámicamente desde muestras de malware. DotDumper incluye un cargador reflexivo que permite a los analistas cargar y analizar ejecutables y bibliotecas, siempre que se basan en el marco DotNet.
Para usar el cargador, debe optar por anular el punto de entrada en la interfaz de línea de comandos, especificando la clase (incluido el espacio de nombres en el que se encuentra) y el nombre de la función en el archivo dado. Opcionalmente, se pueden proporcionar argumentos a la función especificada, para todos los tipos nativos y sus matrices. Ejemplos de tipos nativos son int, string, char y matrices como int[]cadena[]y personajes[]Todos los parámetros se proporcionarán a través de la interfaz de línea de comandos, donde se especificará el tipo y el valor.
No anular el punto de entrada hace que se utilice el punto de entrada predeterminado. De forma predeterminada, se pasa una matriz de cadenas vacía a la función principal del ejemplo, como si el ejemplo se ejecutara sin argumentos. Además, el cargador generalmente usa la reflexión para llamar a una función dada en una clase dada en la siguiente etapa. A veces también se pasan parámetros, que luego se utilizan para descifrar el recurso. Esto es exactamente lo que sucedió en la muestra de AgentTesla mencionada anteriormente. Los ganchos relacionados con las llamadas de DotDumper registran estos eventos, como se muestra a continuación.
El nombre de la función en la primera línea no es una función interna de DotNet Framework, sino una llamada a una función específica en la segunda etapa. Los tipos y nombres de los tres parámetros se enumeran en la firma de la función. Sus valores se pueden encontrar en la sección de información de parámetros de función. Esto permitirá a los analistas cargar una segunda etapa en un cargador personalizado con valores de parámetros dados, o incluso usar DotDumper para hacer esto cargando una etapa descargada previamente y proporcionando parámetros.
gancho administrado
Antes de sumergirse en los ganchos administrados, debe comprender cómo funcionan los ganchos. Hay dos variables principales a considerar aquí: la función de destino y la función controlada conocida como gancho. En pocas palabras, la memoria en la función de destino (es decir, Assembly.Load) se cambia para saltar al gancho. Por lo tanto, el flujo de ejecución del programa se desplaza. Luego, el gancho puede realizar operaciones arbitrarias, opcionalmente llamando a la función original, después de lo cual devuelve la ejecución a la persona que llama, incluido un valor de retorno si lo desea. El siguiente diagrama ilustra este proceso.
Saber qué es un gancho es fundamental para comprender qué es un gancho administrado. El código administrado se ejecuta en un entorno virtual y administrado, como el tiempo de ejecución de DotNet o la máquina virtual de Java. Obtener la dirección de memoria donde reside una función administrada es diferente de los lenguajes no administrados como C. Una vez que se obtienen las direcciones de memoria correctas de las dos funciones, el enlace se puede establecer mediante el uso de C# inseguro para acceder directamente a la memoria mientras se llama a las funciones nativas de la API de Windows de los servicios de interoperabilidad de DotNet.
fácil de expandir
Dado que DotDumper está escrito en C# puro sin dependencias externas, el marco se puede ampliar fácilmente con Visual Studio. El código está documentado en este blog, GitHub y en clases, funciones y código fuente. Esto, combinado con un esquema de nombres claro, permite que cualquier persona modifique la herramienta como mejor le parezca, minimizando el tiempo y el esfuerzo necesarios para comprender la herramienta. En cambio, permite a los desarrolladores y analistas centrarse en mejorar las herramientas.
Dado que los objetivos y capacidades de DotDumper son claros, parece superponerse con herramientas conocidas disponibles públicamente como ILSpy, dnSpyEx, de4doto pe tamizTenga en cuenta que no pretendemos afirmar que una herramienta sea mejor que otra, sino cómo estas herramientas son diferentes.
El objetivo de DotDumper es registrar y volcar llamadas de funciones críticas, contextualizadas y comunes de muestras de destino de DotNet. ILSpy es un desensamblador y descompilador de DotNet, pero no permite ejecutar archivos. dnSpyEx (y su predecesor dnSpy) utiliza ILSpy como un componente de desensamblador y descompilador, mientras agrega un depurador. Esto permite que las personas inspeccionen y manipulen manualmente la memoria. de4dot solo se usa para desofuscar los binarios de DotNet para que el código sea más legible para el ojo humano. La última herramienta de esta comparación, pe-sieve, está diseñada para detectar y volcar malware en procesos en ejecución, independientemente del lenguaje de programación utilizado. La siguiente tabla proporciona una descripción gráfica de las herramientas anteriores.
futura carrera
DotDumper se encuentra en constante revisión y desarrollo, y todo se centra en dos áreas principales de interés: la corrección de errores y la incorporación de nuevas funciones. El código se probó durante el desarrollo, pero debido a los ganchos inyectados en las funciones de DotNet Framework, puede cambiar y existe una alta probabilidad de que haya errores en el código. Se insta a cualquier persona que encuentre un error a abrir un problema en el repositorio de GitHub, que luego se revisará. También se pueden hacer sugerencias para nuevas funciones a través del repositorio de GitHub.Para aquellos que tienen una cuenta de GitHub, o para aquellos que prefieren no interactuar públicamente, no duden en enviarme un mensaje privado. Gorjeo.
No hace falta decir que si usó DotDumper en su análisis, o lo usó de una manera creativa, ¡no dude en comunicarse con nosotros de manera pública o privada! ¡No hay nada mejor que escuchar herramientas caseras en uso!
¡Más contenido para DotDumper, las actualizaciones se enviarán a la comunidad a medida que estén disponibles!