¡Muy buenas a todos! Hace unas cuantas publicaciones, hablamos sobre la introducción a AMSI. Hoy vamos a continuar adentrándonos en
el mundo de AMSI. En esta ocasión, vamos a profundizar en las funciones que
utiliza intérpretes como PowerShell para comunicarse con las medidas de
seguridad gracias a la API AMSI.
Dado que AMSI forma parte de la Windows API (user-mode),
está documentada por Microsoft para que los desarrolladores no tengan problema
en usarla. Es decir, las librerías de la Windows API tienen documentación
acerca de cómo se estructuran sus funciones, que parámetros espera cada
función, valores que recibe, etc.
En primer lugar, tenemos la función AmsiInitialize. Esta
función se llama antes de que ejecutemos cualquier comando en la PowerShell, es
decir, esta función se llama en el momento en el que se ejecuta el intérprete.
Por lo tanto, es una función complicada para atacar y hacer un bypass, debido a
que no se puede modificar la lógica mediante comandos PowerShell.
La documentación de Microsoft nos dice que esta función
recibe dos parámetros:
- El primer parámetro es el nombre de la aplicación que va a utilizar la DLL, que en nuestro caso va a ser PowerShell.
- El segundo parámetro es un puntero vacío llamado amsiContext. Este puntero será rellenado por la función AmsiInitialize y será utilizado con el resto de las funciones.
Tanto en esta función como en la mayoría de las funciones de
AMSI se devuelve HRESULT, que es un código que representa si la función se ha
ejecutado de forma exitosa o no. La lista de códigos podemos verla en la
documentación de Microsoft.
La siguiente función es AmsiOpenSession. Cada vez que se
ejecuta un comando PowerShell, es necesario crear una sesión con AMSI, por lo
que se llamará a esta función.
La documentación de Microsoft nos dice que esta función
también recibe dos parámetros:
- El primer argumento es amsiContext, que equivale al contexto que se ha rellenado en la anterior función.
- El segundo argumento es un puntero vacío llamado amsiSession. Este puntero será rellenado por la función AmsiOpenSession y será utilizado por otras funciones.
Al igual que AmsiInitialize, devuelve un código que
representa si la función se ha ejecutado de forma exitosa o no.
En tercer lugar, tenemos AmsiScanString y AmsiScanBuffer.
Ambas funciones tienen el mismo cometido y la forma de utilizarse es bastante
similar. Tanto es así, que en realidad AmsiScanString llama por debajo a
AmsiScanBuffer. La labor de estas funciones es capturar el contenido de un
comando para que posteriormente lo escanee el AV/EDR.
La documentación de Microsoft nos
dice que AmsiScanString recibe cinco parámetros:
- El primer argumento representa contexto que ha rellenado la primera función (AmsiInitialize).
- El segundo argumento es un string, que representa el contenido del comando.
- El tercer argumento es una especie de identificador.
- El cuarto argumento es la sesión, que como hemos visto previamente, se ha rellenado en la función AmsiOpenSession.
- Por último, con el quinto argumento la función recibe un puntero vacío que va a representar el resultado del escaneo (es decir, si el AV/EDR dice que es malware o no). Los valores pueden ser los siguientes:
Por otro lado, AmsiScanBuffer
recibe, en lugar de un string que va a representar el comando, un buffer.
Además, también es necesario indicar la longitud de ese buffer. Los demás
argumentos son iguales para ambas funciones.
Por último, tanto para
AmsiScanString como para AmsiScanBuffer se devuelve HRESULT, que de la misma
forma que en las funciones anteriores, representa si la función se ha ejecutado
correctamente. Cabe destacar que no hay que confundir el resultado del escaneo
con el resultado de la función, que son dos valores completamente diferentes.
A pesar de eso, son valores que
sí que son dependientes entre sí. El contenido de result no puede ser
AMSI_RESULT_DETECTED, si HRESULT es E_INVALIDARG, puesto que esto significaría
que no se ejecutaría el análisis porque los argumentos son inválidos.
Pero si por un casual, por algún
tipo de error, el contenido de result representa que existe malware,
pero HRESULT es INVALIDARG, el comando se ejecutará con normalidad, ya que
primero se comprobará HRESULT, y si este indica que se ha ejecutado la función
con normalidad, comprobará el contenido de result.
Por último, tenemos la función
AmsiCloseSession, que tiene lugar cuando ya se ha realizado el escaneo. Esta
función se llama también cuando se ejecuta un comando en PowerShell. La tarea
de esta función es cerrar la sesión para que no se pueda volver a utilizar.
La documentación de Microsoft nos
dice que recibe dos parámetros:
- El contexto de la inicialización.
- La sesión que se quiere cerrar.
Al ser una función void,
no devuelve nada. Es decir, por parte de Microsoft no existe control de errores
para saber si la sesión se ha cerrado con éxito, o ha habido algún tipo de
fallo.
Por lo tanto, cuando abrimos una
PowerShell, el flujo de llamadas a la API sería el siguiente:
Y en el momento que ya está la PowerShell abierta y ejecutamos comandos, el
flujo de llamadas a la API sería el siguiente:
Y por hoy,¡ ya está bien!, continuaremos con AMSI en las próximas entregas.
Juan Gabriel Ruiz, Senior Security Analyst at Zerolynx y Justo Martín, Security Analyst at Zerolynx.