
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:
Living off the Land-Taktiken nutzen Powershell, um Angriffe unauffällig und ohne zusätzliche Tools durchzuführen.
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.
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 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.
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.
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.
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.
Je nach Umgebung kann eine Anpassung des Zeitraums für bessere Ergebnisse sorgen.
// Average Powershell Command Length
#event_simpleName=ProcessRollup2
| ImageFileName=/\\(powershell(_ise)?|pwsh)\.exe/i
| CommandLength := length("CommandLine") | CommandLength>0
| aid=?AID
// Classify Data into Historical and LastDay
| case {
test(@timestamp < (end() - duration(7d))) | DataSet:="Historical";
test(@timestamp > (end() - duration(1d))) | DataSet:="LastDay";
*}
// Calculate Average Command Length
| groupBy([DataSet, aid], function=avg(CommandLength))
| case {
DataSet="Historical" | rename(field="_avg", as="historicalAvg");
DataSet="LastDay" | rename(field="_avg", as="todaysAvg");
*
}
// Aggregate Averages
| groupBy([aid], function=[avg("historicalAvg", as=historicalAvg), avg("todaysAvg", as=todaysAvg)])
// Calculate Percentage Increase
| PercentIncrease := (todaysAvg - historicalAvg) / historicalAvg * 100
| format("%d", field=PercentIncrease, as=PercentIncrease)
| format(format="%.2f", field=[historicalAvg], as=historicalAvg)
// Filter and Sort Results
| PercentIncrease > 0
| sort(PercentIncrease, limit=10000)Vom Exploit bis zur Defense: Echte Security-Insights aus unserem SOC und Research-Lab