29 sept 2020

Cifrando en PowerShell para bypassear un antivirus perimetral (ya no tan noob):

¡Muy buenas!

Hace unos meses, en este post, os mostramos unos scripts muy sencillitos para codificar y descodificar en base 64 un script malicioso para intentar bypassear algún equipo perimetral. Os comentamos que esa era la solución de andar por casa y sin ganas de arremangarse mucho, y que la forma "leet" de hacer este bypass de perímetro era cifrando el contenido, que obviamente no es tan trivial. 
En el post de hoy me gustaría hablaros de SecureString. Esto es una clase .NET que representa un texto que debe mantenerse confidencial (por ejemplo, mediante su eliminación de la memoria del equipo cuando ya no se necesite). Pese a que no se recomienda su uso, ya que el contenido secreto... no es tan secreto en memoria como nos gustaría, nos puede servir perfectamente para bypassear un antivirus perimetral. Para ello, debemos saber que SecureString usa DPAPI para almacenar una variable string de hasta 65.536 caracteres cifrada en memoria, y que permite "devolver" este objeto a un string cifrado en AES o en texto plano: esto es lo que nos interesa :).

Bien... ¿y cómo? Lo primero, definiremos la variable de texto que queremos enviar:

$secret_SS = <SECRETO> | ConvertTo-SecureString -AsPlainText -Force

Ahora mismo tenemos la variable almacenada cifrada en memoria... pero esto no nos sirve de mucho, ¿verdad?

Como veis, una SecureString a pelo no es muy útil en este caso...

Lo que nos interesa es sacar el valor de esta variable cifrado con AES, para lo que necesitaremos una clave (un array de 128-bit (16 bytes), 192-bit (24 bytes) o 256-bit (32 bytes)). Podemos definir el array de bytes a capón o usar una función hash para hacer esto más leet y agradable.

[Byte[]] $key = (1, 255, 232, 211, 123, ... , 159, 81) #Definimos el array directamente
---
$sha256 = New-Object System.Security.Cryptography.SHA256CryptoServiceProvider #Queremos una clave de 32 bytes
$key = $sha256.ComputeHash([system.Text.Encoding]::UTF8.GetBytes(<CONTRASEÑA>)) #Hacemos el SHA256 de la contraseña que hayamos elegido, devolviendo un array de bytes

En cualquier caso, una vez hemos definido la clave, podemos obtener el secreto cifrado en AES con el siguiente comando

$secret_AES = $secret_SS| ConvertFrom-SecureString -Key $key

Aquí vemos cómo obtenemos al contenido cifrado en AES-256

Ahora nos toca descifrar este texto lo cual, como podréis intuir, es un proceso muy similar. Primero, una vez definida la variable con el string en AES, definimos de nuevo la clave de descifrado, al igual que en el lado cifrador. Tras esto, pasamos el string en AES a SecureString con la clave usada para cifrar:

$secret_SS = $secret_AES | ConvertTo-SecureString -Key $key

Finalmente, sólo nos faltaría recuperar el valor de la SecureString en claro. Para ello existen dos maneras en función de la versión de PowerShell usada:
  • Powershell < v6.0 (en Windows vale para cualquier versión):
$clearSecret = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret_SS))
  • PowerShell >= v6.0:
$clearSecret = ConvertFrom-SecureString -SecureString $secret_SS -AsPlainText
Aquí desciframos el secreto cifrado en AES-256

Como veis, este método es relativamente sencillo para cifrar nuestros payloads entre sistemas con PowerShell (recordad que la versión 7 se puede instalar en Linux) y bypassear un antivirus perimetral, o lo que surja. Ahora bien, ¿hay una forma de cifrar en AES a bajo nivel para interactuar con otros lenguajes y no depender exclusivamente de PowerShell? ¿Y si yo quisiera cifrar en Python y descifrar en PowerShell?

Pues eso os lo contamos otro día :)

¡Gracias por leer!


No hay comentarios:

Publicar un comentario