DLL Hijacking - la sub-técnica de MITRE para inyectar DLLs

DLL Hijacking - inyección de DLLs para evasión y persistencia

Existe una sub-técnica de MITRE para inyectar DLLs, llamada DLL Hijacking. Y en este post, encontrarás una explicación teórica y una sencilla prueba de concepto, con una DLL programada en C++ que abre la calculadora de Windows en caso de inyección exitosa.

Boletines e informes como éste se publican cada semana en Twitter del grupo de Inteligencia de Amenazas de ISH, el Heimdall. ¡Síguenos allí!

Dynamic-link library, o DLL, es la implementación realizada por Microsoft del concepto de bibliotecas compartidas. Se trata de módulos que contienen funciones que pueden ser utilizadas por otros ejecutables. La idea es que los desarrolladores no necesiten reinventar la rueda para utilizar funciones elementales del sistema operativo, como leer archivos, crear sockets para conectarse a Internet, etc. Las DLL del sistema proporcionan código listo para estas funciones (API) y el ejecutable en cuestión sólo importa las que son relevantes.

Carga de módulos en un proceso

Cada programa a ejecutar recibe un conjunto de recursos de memoria necesarios para su funcionamiento. A este conjunto de recursos lo llamamos proceso. Una parte crucial de la inicialización de cualquier proceso es la importación de todas las DLL especificadas en el ejecutable.

La imagen siguiente ofrece un ejemplo de la información de importación contenida en un fichero de este tipo.

Figura 1: lista de módulos que deben importarse en el bloc de notas (notepad.exe)

La importación de todos estos módulos es responsabilidad del Cargador de imágenesun componente de ntdll.dll (este módulo es esencial para la inicialización de todos los procesos en un entorno Windows).

El objetivo de este informe no es explicar en detalle cómo funciona Windows a la hora de crear un proceso. Sin embargo, proporcionaremos a lo largo del informe nombres de funciones y módulos implicados en esta creación, junto con referencias que el lector podrá consultar si desea profundizar en el tema.

El funcionamiento del cargador de imágenes implica una serie de funciones ntdll.dll, con especial énfasis en LdrpLoadDll, LdrpCheckForLoadedDll y LdrpMapDll. Para cada uno de los módulos enumerados, LdrpLoadDll iniciará el proceso de importación. Parte de este proceso es comprobar si la DLL deseada ya ha sido importada; esto es lo que hace la función LdrpCheckForLoadedDll. Si el módulo no es ya parte del proceso, depende de LdrpMapDll encontrar el módulo en el disco y cargarlo en memoria. Entender cómo se produce la búsqueda en disco es crucial para entender cómo funciona el secuestro de DLL.

Orden de búsqueda

Existe un orden específico de directorios desde donde se obtendrá cualquier módulo a importar. El orden por defecto para Windows es el siguiente:

  1. El directorio desde el que se lanzó la aplicación
  2. El directorio del sistema (C:³³Windows³System32)
  3. La versión de 16 bits del directorio del sistema
  4. El directorio de Windows (C:³³)
  5. El directorio actual

Llamamos a esta secuencia predeterminada porque está definida por una clave en el registro, HKEY_LOCAL_MACHINE\System\CurrentControlSet\ControlSession Manager\SafeDllSearchMode, que está activada por defecto. Si se desactiva, la secuencia cambia a:

  1. El directorio desde el que se lanzó la aplicación
  2. El directorio actual
  3. El directorio del sistema (C:³³Windows³System32)
  4. La versión de 16 bits del directorio del sistema
  5. El directorio de Windows (C:³³)

Una forma de observar esta búsqueda en acción es utilizando el Monitor de Procesos de SysInternals. La imagen de abajo demuestra esta rutina con la ejecución del proceso explorer.exe.

Figura 2: explorador buscando DLL en el directorio desde el que se ejecutó

Las operaciones CreateFile son acciones de búsqueda de los módulos especificados en la tabla de importación del ejecutable del explorador. Importante: las búsquedas en C:\Windows mostradas arriba no son el resultado del cuarto paso de la orden de búsqueda, sino del primero. La ruta completa de este fichero es C:\Windowsexplorer.exe, por lo que C:\Windows es el directorio desde el que se lanzó la aplicación.

Figura 3: resultado de la acción CreateFile para cscapi.dll

Hemos resaltado sólo las operaciones que tuvieron como resultado"NAME NOT FOUND", lo que significa que el módulo no se encontró en ese directorio. LdrpMapDll procederá entonces al siguiente directorio, hasta que encuentre el módulo deseado. En este caso, por ejemplo, cscapi.dll se encuentra en C:\Windows\System32. Por lo tanto, será encontrado en el segundo paso de la búsqueda y mapeado a la memoria del proceso.

Pero, ¿qué pasaría si hubiera una DLL con el mismo nombre en C:\Windows?

Secuestro de DLL

El secuestro de DLL es una táctica para cargar módulos maliciosos en un proceso aprovechando el orden de búsqueda utilizado por Windows. Respondiendo a la pregunta planteada en el punto anterior: si hubiera una librería llamada cscapi.dll en el mismo directorio que explorer.exe, se cargaría en lugar del módulo legítimo ubicado en C:\WindowsSystem32.

Pero, ¿para qué sirve cargar una DLL falsa en un proceso? Al fin y al cabo, no contendrá las funciones que el proceso quiere importar, por lo que en teoría no se ejecutaría nada de su código. Para ello necesitamos entender otro componente del cargador de imágenes, LdrpRunInitializeRoutinesque se encarga de invocar el punto de entrada de ejecutables y librerías.

Esta rutina, así como las otras funciones de ntdll.dll que mencionamos en artículos anteriores, no están documentadas oficialmente por Microsoft. Sin embargo, existe una API que sí está documentada y que llama a LdrLoadDLLL: LoadLibraryA.

La documentación oficial detalla que tras el proceso de mapeo de una librería, esta API invoca a la función DLLMain del módulo, con el valor DLL_PROCESS_ATTACH. En realidad, se trata de la invocación del punto de entrada realizada por LdrpRunInitializeRoutines que mencionábamos al principio de este artículo. Traduzcamos a términos menos técnicos lo que implica esta invocación.

Según la documentación de DLLMain, el valor DLL_PROCESS_ATTACH significa que la DLL en cuestión está siendo cargada en la memoria de un nuevo proceso. Lo que hará LdrpRunInitializeRoutines es ejecutar el código que esté ligado a ese valor en el momento en que se importe ese módulo.

Esto responde a la pregunta del principio de este artículo: no necesitamos que una función exportada desde una DLL sea llamada para que algún código en ella se ejecute. Sólo necesitamos que el código en cuestión esté asociado al valor DLL_PROCESS_ATTACH. Hemos creado un sencillo POC para demostrar mejor estos conceptos. Su código sigue a continuación:

Figura 4: Código POC para demostrar el secuestro de DLL

Fíjate en lo que viene justo después de case DLL_PROCESS_ATTACH: WinExec("calc.exe", 0). Traduciendo: en cuanto nuestra librería sea mapeada a la memoria de un proceso, éste abrirá la calculadora. Para probar la teoría que hemos expuesto hasta ahora, necesitamos compilar nuestro código, llamarlo cscapi.dll y poner el fichero en el mismo directorio que explorer.exe

Figura 5: POC DLL guardada en el mismo directorio que explorer.exe

Ahora sólo queda terminar el proceso explorer.exe y reiniciarlo.

Figura 6: Cierre y reinicio de explorer.exe

Si todo ha ido bien, el nuevo proceso debería encontrar cscapi.dll en el directorio C:\Windows. El registro del Monitor de Procesos de abajo demuestra esta acción:

Figura 7: Nuestro cscapi.dll se encuentra en la tabla C:\Windows

Tras encontrar el archivo, el módulo en cuestión será importado por el proceso.

Figura 8: Módulo cargado correctamente en el proceso explorer.exe

Si se ejecuta nuestro código en DLL_ PROCESS_ATTACH, deberíamos ver que explorer.exe crea un proceso de calculadora de Windows (calc.exe).

Figura 9: explorer.exe creando un proceso de calculadora de Windows

Nuestra prueba de concepto de secuestro de DLL funcionó a la perfección.

Conclusión

El secuestro de DLL es una técnica muy útil para obtener persistencia en un sistema, al tiempo que evita trucos ya conocidos como las claves de registro y los directorios que ejecutan programas durante la fase de inicio de Windows.

Este método también tiene sus puntos débiles. En primer lugar, al utilizar métodos legítimos de Windows para cargar una biblioteca en memoria, este método rellena regiones de la memoria del proceso con la ruta completa al módulo malicioso. En manos de un analista experimentado, la carga de DLL desde ubicaciones incorrectas es un signo claro de la presencia de malware.

Otro problema reside en el hecho de que esta técnica sustituye a una DLL legítima. Esto significa que cualquier función presente en el módulo que ha sido reemplazado ya no está disponible para el proceso objetivo. Los intentos fallidos de secuestro pueden eliminar funciones importantes para una aplicación, provocando inestabilidad o incluso fallos.

Por último, está el problema de los permisos. El explorer.exe, elegido por nosotros para demostrar esta técnica, puede parecer un buen objetivo. Es un ejecutable presente en cualquier versión de Windows y cuya ejecución está garantizada. Pero reside en un directorio del sistema. Esto significa que sólo pudimos colocar nuestra DLL maliciosa en la misma carpeta que él porque tenemos permisos de administrador en la máquina. Para un atacante que acaba de obtener el primer acceso a un activo, utilizar el explorador para esta técnica es imposible.

Referencias

  1. https://www.youtube.com/watch?v=3eROsG_WNpE&ab_channel=IppSec
  2. https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain
  3. https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#standard-search-order-for-desktop-applications
  4. https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya
  5. https://docs.microsoft.com/en-us/sysinternals/downloads/procmon
  6. https://web.archive.org/web/20010618170125/http://www.microsoft.com:80/msj/0999/hood/hood0999top.htm
  7. https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/windows-2000-loader-what-goes-on-inside-windows-2000-solving-the-mysteries-of-the-loader
  8. https://guidedhacking.com/threads/dll-injection-methods.14569/
  9. https://docs.microsoft.com/en-us/sysinternals/resources/windows-internals

Por Alexandre Siviero, Laura Cardillo y Átila Altoé

Dejar un comentario

Su dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *.