ByteRay Blog

Powershell Hunting IV: Powershell Command Length

Powershell Command Length

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

Living off the Land\-Taktiken nutzen Powershell, um Angriffe unauffällig und ohne zusätzliche Tools durchzuführen.

Abweichungen der Befehlslänge

Angreifer verwenden häufig Methoden in Powershell, die zu ungewöhnlich langen Befehlszeilen führen. Das Überwachen von Abweichungen gegenüber der üblichen Befehlslänge kann daher ein wertvoller Ansatz zur Erkennung verdächtiger Aktivitäten sein. Ungewöhnlich lange Befehle können ein Indikator für einen Angriff sein.

  * Verschleierung / Obfuscation

      * Kodierung: Verwendung von Base64, Hexadezimal oder ASCII zur Darstellung von Befehlen oder Script-Blöcken.  
      * Escape-Zeichen: Einsatz von Backticks (\) innerhalb von Zeichenketten oder Befehlen, um die Lesbarkeit zu beeinträchtigen.  
      * Leerzeichen & Kommentare: Einfügen unnötiger Leerzeichen oder Kommentare.
      * Einbetten von Payloads & Skripten: Einfügen ganzer Skripte oder binärer Payloads direkt in die Befehlszeile.
   
  * Dateilose Ausführung & LotL: Fileless-Angriffe oder welche, die nur integrierte Werkzeuge verwenden (Living off the Land), verlassen       sich oft auf komplexe, einzeilige Befehle, die an powershell.exe übergeben werden, um Payloads von entfernten Quellen       herunterzuladen und auszuführen oder verkettete Aktionen durchzuführen. Diese komplexen Anweisungen führen naturgemäß zu       längeren Befehlen.  
 
   * Offensive Frameworks: Werkzeuge wie Empire, PowerSploit, Cobalt Strike usw. nutzen Powershell intensiv und generieren oft lange,       verschleierte Befehle für ihre Payloads.
 
  * Ungewöhnlich lange Befehle sind ein starker Indikator, da sie direkt mit gängigen Angreifertechniken zur Umgehung und Ausführung       korrelieren ([T1027.010] Obfuscated Files or Information). Legitime administrative Aufgaben erfordern selten die extremen Längen, die       oft durch die genannten Methoden erzeugt werden. Legitime Administratoren verwenden typischerweise Skriptdateien (.ps1) für       komplexe Logik , nicht verschleierte Einzeiler. Angreifer verwenden lange, verschleierte Befehlszeilen genau deshalb, um das Schreiben       von Dateien auf die Festplatte zu vermeiden (dateilose Ausführung) und um einfache Erkennungslogiken zu umgehen. Dieser       Unterschied in der Betriebspraxis macht die Länge zu einem relativ nützlichen Unterscheidungsmerkmal.

Die Macht des Baselining: "Normal" etablieren

Grundlagen der Anomalieerkennung

Die Kernidee besteht darin, darzustellen was "normale" Aktivität für die länge der ausgeführten Powershell-Befehle ist, und dann Abweichungen von dieser Norm zu identifizieren.  

Erstellung der Baseline

Die Abfrage analysiert historische Powershell-Executions und die jeweiligen Befehlslängen über einen definierten Zeitraum. Sie berechnet statistische Maße der zentralen Tendenz (in diesem Fall den Mittelwert) für Befehlslängen während dieses Baseline-Zeitraums. Dies definiert den erwarteten Bereich der Befehlslängen.  

Zeitraum: 7-Tage-Baseline

  * Erfassung wöchentlicher Zyklen: Viele organisatorische Arbeitsabläufe und administrative Aufgaben folgen wöchentlichen Mustern (z. B.       unterschiedliche Aktivitäten an Wochentagen vs. Wochenenden, wöchentliche Berichte, Patching-Zeitpläne). Eine 7-Tage-Baseline reicht       oft aus, um diese typischen Rhythmen zu erfassen.  
  * Stabilität vs. Anpassungsfähigkeit: der gewählte Zeitraum ist eine Balance. Kürzere Baselines (z. B. 24 Stunden) können übermäßig       empfindlich auf tägliche Schwankungen reagieren. Längere Baselines passen sich möglicherweise zu langsam an legitime       Verhaltensänderungen an oder riskieren, vergangene Anomalien in das "normale" Profil zu integrieren.  
   * Ein 7-Tage-Fenster glättet tägliche Variationen und bleibt gleichzeitig einigermaßen anpassungsfähig. Die Wahl einer 7-Tage-Baseline ist       eine bewusste Abwägung, die darauf abzielt, das Signal/Rausch Verhältnis für die Erkennung von Anomalien im Zusammenhang mit       wöchentlichen menschlichen/systemischen Verhaltensmustern zu maximieren. Geschäfts- und IT-Betriebe haben oft wöchentliche       Rhythmen. Ein Server könnte bestimmte Wartungsskripte nur am Wochenende ausführen, oder Benutzer könnten bestimmte Aufgaben       nur montags erledigen. Wenn am Mittwoch ein sehr langer Befehl auftaucht, aber ähnliche lange Befehle jeden Samstag aufgrund von       Backups normal sind, hilft die 7-Tage-Baseline dabei, das potenziell anomale Ereignis am Mittwoch vom normalen Ereignis am Samstag       zu unterscheiden. Dies reduziert Fehlalarme im Vergleich zu einer kürzeren Baseline, die den wöchentlichen Kontext nicht erfasst.
  * Anpassung an die betriebliche Realität: Je nach Umgebung kann eine Anpassung des Zeitraums für bessere Ergebnisse sorgen.


CrowdStrike NextGen SIEM Query

1// Average Powershell Command Length
2#event_simpleName=ProcessRollup2
3| ImageFileName=/\\(powershell(_ise)?|pwsh)\.exe/i
4| CommandLength := length("CommandLine") | CommandLength>0
5| aid=?AID
6// Classify Data into Historical and LastDay
7| case {
8    test(@timestamp < (end() - duration(7d))) | DataSet:="Historical";
9    test(@timestamp > (end() - duration(1d))) | DataSet:="LastDay"; 
10    *}
11// Calculate Average Command Length
12| groupBy([DataSet, aid], function=avg(CommandLength))
13| case {
14    DataSet="Historical" | rename(field="_avg", as="historicalAvg");
15    DataSet="LastDay" | rename(field="_avg", as="todaysAvg");
16    *
17}
18// Aggregate Averages
19| groupBy([aid], function=[avg("historicalAvg", as=historicalAvg), avg("todaysAvg", as=todaysAvg)])
20// Calculate Percentage Increase
21| PercentIncrease := (todaysAvg - historicalAvg) / historicalAvg * 100
22| format("%d", field=PercentIncrease, as=PercentIncrease)
23| format(format="%.2f", field=[historicalAvg], as=historicalAvg)
24// Filter and Sort Results
25| PercentIncrease > 0
26| sort(PercentIncrease, limit=10000)
✉️ 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.