ByteRay Blog

Powershell Hunting II: Powershell Downloads

Powershell Obfuscation

Warum Powershell für Angreifer interessant ist

Powershell ist ein fester Bestandteil moderner Windows-Systeme und bietet durch .NET-Anbindung mächtige Möglichkeiten zur Automatisierung. Diese Eigenschaften machen es auch für Angreifer attraktiv:

- Auf jedem Windows-System vorhanden (kein zusätzlicher Code nötig)
- Direkter Zugriff auf Windows-APIs und Netzwerkressourcen
- Kann Code aus dem Speicher ausführen (fileless execution)
- Oft unzureichend überwacht oder eingeschränkt

Warum Encoded Powershell ein Risiko darstellt
Angreifer codieren PowerShell-Befehle um Sicherheitsmechanismen zu umgehen:

* Einfache Signaturen und Filter, Intrusion Detection Systems und SIEM-Regeln können die eigentlichen Befehle nicht erkennen, wenn sie   Base64-kodiert sind. Die Codierung verschleiert    Schlüsselwörter wie `Invoke-Expression`, `Net.WebClient`, `DownloadString` oder    Namen bekannter Schadsoftware wie Mimikatz.  
* Fileless Execution: Codierte Befehle werden oft verwendet, um Skripte oder Binaries direkt aus dem Internet in den Speicher zu laden und auszuführen (`IEX (New-Object    Net.WebClient).DownloadString(...)`), ohne dass eine Datei auf die Festplatte geschrieben wird. Dies    erschwert die forensische Analyse und die Erkennung erheblich.
* Umgehung von Execution Policies: Die Verwendung von Parametern wie `-Command` oder `-EncodedCommand` kann die Powershell Execution Policy umgehen, die standardmäßig das Ausführen    von Skripten einschränken soll.
* Verschleierung der Gesamtaktivität: Codierung ist oft Teil einer mehrstufigen Obfuskationsstrategie. Angreifer können Base64 mit anderen Techniken wie String-Manipulation,    Zeichenkettenzerlegung, zufälliger Groß- / Kleinschreibung oder Komprimierung kombinieren, um die Analyse weiter zu erschweren.
* Living off the Land: Als vorinstalliertes Windows Tool verhält sich Powershell mit codierten Befehlen unaufälliger als externe Tools / Frameworks.  

Legitime Verwendung

Laut Microsoft-Dokumentation ist der Hauptzweck von `-EncodedCommand`, die Ausführung von Befehlen zu ermöglichen, die komplexe Anführungszeichen, geschweifte Klammern oder andere Sonderzeichen enthalten, die bei der direkten Übergabe an die Kommandozeile zu Problemen führen könnten. Durch die Kodierung des Befehls als Base64-String werden diese Interpretationsprobleme vermieden. Außerdem sind wir in Kundenumgebungen bereits auf die folgenden Verwendungen gestoßen:

* Systemadministration & Automatisierung:
 * Ausführen von Skripten mit komplexen Parametern: Übergabe von Parametern, die Sonderzeichen, JSON-Strings oder andere komplexe Daten enthalten, an ein PowerShell-Skript, das über      powershell.exe aufgerufen wird.
 * Scheduled Tasks / Remote-Ausführung: In Szenarien, in denen Befehle über mehrere Schichten oder Systeme übergeben werden, die komplexe Strings verändern könnten (z.B. Argumente im      Task Scheduler, bestimmte Remote-Management-Tools, Skripte, die andere Skripte aufrufen).
 * Einbetten von Befehlen: Speichern oder Übertragen von Powershell-Befehlen innerhalb anderer Dateiformate (z.B. XML-Konfigurationsdateien, JSON-Nutzdaten, HTML-Seiten), bei denen die      direkte Einbettung zu Syntaxkonflikten führen würde.
* Interoperabilität: Bei Aufrufen von Powershell aus anderen Programmier- oder Skriptsprachen (z.B. Python, Batch, C#), bei denen das korrekte Escaping der Powershell-Syntax schwierig sein    kann.
 * Seltene / Nischenfälle: In wenigen Fällen verwendet von legitimen Software-Installationsprogrammen oder Konfigurationstools.
 * Einbetten von nicht-textuellen Daten (wie Icons für GUI-Skripte) direkt in eine Skriptdatei. Dies verwendet jedoch typischerweise `[Convert]::FromBase64String` innerhalb des Skripts, nicht      den `-EncodedCommand` Parameter beim Aufruf.

Der `-EncodedCommand` Parameter

Der Parameter `-EncodedCommand` ist ein legitimes Feature von `powershell.exe` (Windows Powershell 5.1 und früher) und `pwsh.exe` (Powershell 6 und neuer). Powershell erlaubt es, Parameter in vollständig ausgeschriebener Form (z. B. `-EncodedCommand`) oder in verkürzter Schreibweise (wie `-enc` oder sogar `-e`) zu verwenden, solange die Kurzform eindeutig auf einen gültigen Parameter verweist. Dieses Verhalten basiert auf einem eingebauten Mechanismus zur automatischen Parametervervollständigung, der dem Nutzer Arbeit abnehmen und Fehler reduzieren soll. Um codierte Powershell-Befehle zu finden, reicht es also nicht nach dem Parameter `-EncodedCommand` zu suchen. Die alternativen Schreibweisen müssen in der Suche berücksichtigt werden.

Powershell Base64 Encoding
# Der ursprüngliche Befehl
$command = "ping 8.8.8.8"

# 1. String in Bytes umwandeln (unter Verwendung von UTF-16LE)
$bytesUtf16le = [System.Text.Encoding]::Unicode.GetBytes($command)

# 2. Bytes in einen Base64-String umwandeln
$base64CommandUtf16le = [System.Convert]::ToBase64String($bytesUtf16le)

# Ausgabe
Write-Host "Ursprünglicher Befehl: $command"
Write-Host "Bytes (UTF-16LE) als Hex-String: $($bytesUtf16le | ForEach-Object { $_.ToString('X2') })"
Write-Host "Base64-kodierter Befehl (aus UTF-16LE Bytes): $base64CommandUtf16le"
Ausführen des Encoded Command
powershell.exe -EncodedCommand $encodedCommand
CrowdStrike NG-SIEM Query
1//Get Powershell and pwsh events
2#event_simpleName=ProcessRollup2 
3| event_platform=Win 
4| ImageFileName=/\\(powershell|pwsh)\.exe/i
5//Search for "-EncodeCommand" and variations   
6| groupby([ParentBaseFileName, CommandLine], function=stats([count(aid, distinct=true, as="uniqueEndpointCount"), count(aid, as="executionCount")]), limit=max)
7//Set endpoint prevalence threshold
8| uniqueEndpointCount < 3
9//Calculating command length & Isolate Base64 sting
10| cmdLength := length("CommandLine")
11//| CommandLine=/\s(|[\^])-(|[\^])[e]{1,2}[ncodema^]*\s(?<base64String>\S+)|^/i
12//| CommandLine=/\s-[eE^]{1,2}[ncodema^]*\s(?<base64String>\S+)/i
13| CommandLine=/\s(|[\^])-(|[\^])[e]{1,2}[ncodema^]*\s(?<base64String>\S+)/i
14//| replace("^", with="", field=base64String, as=CleanBase64String)
15//Get Entropy of Base64 String
16| b64Entropy := shannonEntropy("base64String")
17// Set entropy threshold
18| b64Entropy > ?EntropyGreaterThan
19//Decode encoded command blob
20| decodedCommand := base64Decode(base64String, charset="UTF-16LE")
21//Outputting to table
22| table([ParentBaseFileName, uniqueEndpointCount, executionCount, cmdLength,  b64Entropy, decodedCommand, CommandLine])
23//Uncomment next line to search URLs in the decoded command
24//| decodedCommand=/https?/i
25//Uncomment next line to search IP:Port in the decoded command 
26//|regex("(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\:(?<port>\d{2,5})", field=decodedCommand)
27//Uncomment next line to search IP in the decoded command 
28//|regex("(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})", field=decodedCommand)
✉️ Newsletter
Erhalten Sie aktuelle Einblicke, Sicherheitsupdates und exklusive Fachartikel direkt in Ihr Postfach. Jetzt unseren Newsletter abonnieren!
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.