Ist die Erkennung eines randomisierten C2-Agents allein auf Basis von Netzwerkverkehrsdaten möglich oder nicht? Wir haben es genau getestet.
Rückblick
Trovent Security hatte 2023 zusammen mit Timo Sablowski von carmasec einen Test zur Erkennung von C2-Kommunikation eines rein auf IMAP basierenden Command and Control (C2) Agents durchgeführt. Der Test fokussierte sich seinerzeit auf die Detektion des deterministischen Verhaltens des C2-Agents im Netzwerkverkehr.
Fazit damals: Gewisse Indikatoren (Dauer der Session und Frequenz) lassen sich erkennen, aber diese sind jeweils für sich kein zuverlässiges Detektionskriterium. Unsere Schlussfolgerung aus Sicht des Systems zur Angriffserkennung: Nur durch das Verbinden von detektierten Indikatoren mit Kontextwissen über die IT-Infrastruktur kann man eine zuverlässige Erkennung erreichen.
Nächster Test, nun noch kniffliger
Um es auf Seiten des Blue-Teams, also für die Erkennung, im Vergleich zum ursprünglichen Test noch ein Stück schwieriger zu machen, hat Timo einen neuen Agenten entwickelt, um das vorhersagbare Verhalten gänzlich zu vermeiden.
Daher integrierte Timo an zwei entscheidenden Stellen Randomisierungen. Sowohl die Paketgröße als auch die Sendungszeitpunkte erfuhren entsprechende Modifikationen. Für die Paketgröße kam ein zufälliger Wert zwischen 10 und 2000 zum Einsatz, während der Sendungszeitpunkt zusätzlich durch einen randomisierten Jitter verschleiert wurde.
In diesem Artikel geht es nun darum, ob und wie ein solcher C2-Agent theoretisch (und praktisch) rein auf Basis von Netzwerkverkehrsdaten detektiert werden kann. Spoiler: Es ist schwieriger als gedacht!
Wie könnte trotz Randomisierung detektiert werden?
Die Erkennung von C2-Agenten allein aus Netzwerkverkehrsdaten ist in gewisser Weise eine Königsklasse für sich. Insbesondere, da es sich nur um einen kleinen Teil eines relativ langen Angriffsprozesses (im Kontext der Killchain) handelt, der potenziell detektiert werden kann.
In einem typischen Angriffsszenario müsste ein Angreifer als Grundvoraussetzung die IT-Infrastruktur eines Unternehmens erst kompromittieren, um in der Folge einen C2-Agent deployen zu können. Üblicherweise würde dieser Vorgang bereits eine Vielzahl von Indikatoren (IOCs) auslösen, die sich detektieren ließen.
In unserem konstruierten Testszenario haben wir jedoch nicht die Möglichkeit die Indikatoren einer solchen Kompromittierung zu detektieren, sondern führen ausschließlich Analysen anhand des Netzwerkverkehrs durch (also nach der angenommenen Installation des C2-Agents).
Noch mehr Hürden
Eine zusätzliche Schwierigkeit: Der C2-Agent weist durch seine Randomisierung nicht das typische Beaconing-Verhalten auf, wonach wir normalerweise Ausschau gehalten hätten.
Um dieses Problem zu lösen, haben wir uns von Methoden zur Erkennung von Bilanzfälschung inspirieren lassen. Das Benfordsche Gesetz beschreibt eine Gesetzmäßigkeit in der Verteilung der führenden Ziffern von Zahlen in empirischen Datensätzen. Ausgehend von dieser Gesetzmäßigkeit können Bilanzprüfer Zahlendatensätze analysieren und schlussfolgern, ob bestimmte Verteilungen auf Betrug hinweisen, während andere Verteilungen “natürlich” sind.
Dieses Vorgehen wollten wir auch für unser C2-Detektionsszenario verwenden und haben folgenden Test durchgeführt.
Paketgrößengenerierung
Für den randomisierten C2-Agent wird die Paketgröße zufällig festgelegt. Die einfachste Möglichkeit dies zu tun, wäre die random.random() Funktion in Python. Da die Werte, die von dieser Funktion generiert werden, zwischen 0 und 1 liegen, könnte ein möglicher C2-Agent seine Paketgrößen wie folgt erzeugen, wenn diese zwischen 1 und 1000 liegen sollen:
packet_size = 1 + round(random.random() * 999) # [1, 1000]
Die resultierende Verteilung ist eine Gleichverteilung für die Ziffern 1 bis 9 , wie in der nachfolgenden Abbildung zu sehen ist (blaue Balken).
Nun hat uns Timo für die Analyse des Netzwerkverkehrs verraten, dass die generierten Paketgrößen des C2-Agenten zwischen 10 und 2000 liegen, sofern keine größeren Datenmengen übertragen werden. Eine mögliche Implementierung hierfür wäre:
packet_size = 10 + round(random.random() * 1990) # [10, 2000]
Hier sehen wir im Balkendiagramm (grüne Balken), dass ein Großteil der Zahlen mit einer Eins beginnt. Dies ist naheliegend, da die Hälfte der generierten Zahlen die Form 1xxx
haben. Subtrahiert man diese 5000 (50% von 10.000 Samples) von den 5584, bekommen wir die erwarteten ~580 wie im Falle der anderen führenden Ziffern auch. Die Gleichverteilungscharakter ist also noch immer ersichtlich und in der Abbildung leicht erkennbar.
Angenommen, ein Angreifer generiert so Zahlen zwischen 10 und 6000:
packet_size = 10 + round(random.random() * 5990) # [10, 6000]
Nun ist die Verteilung wieder etwas anders, aber die Gleichverteilungscharakter ist noch immer ersichtlich (s.o.).
Natürliche Paketgrößen
Da wir nun wissen, wie sich zufällige Paketgrößen (basierend auf gleichverteilten Zufallsfunktionen) verhalten, müssen wir uns zum Vergleich Paketgrößen von “natürlichen” Verteilungen anschauen. Nur so haben wir einen Chance, die randomisiert generierten Pakete des C2-Netzwerkverkehrs anhand ihrer Größe in “natürlichem” Datenverkehr zu identifizieren.
Hierfür betrachten wir zwei verschiedene Beispiele:
CPU-Auslastung
Um zu zeigen, dass natürlich auftretende Zahlen nicht dieser Art der Verteilung folgen, haben wir folgendes Beispiel konstruiert:
packet_size = 1 + round(random.random() * psutils.cpu_percent()) # [1, CPU_PERCENT]
Sobald Zufallswerte auf einem natürlichen Wert basieren, kann man die oben in der Randomisierung identifizierten Muster nicht mehr erkennen. Die Ziffern 1 und 2 kommen sehr häufig vor, während die anderen Ziffern in abnehmender Stärke auftreten.
Dies ist ein Indikator, dass sich randomisiert generierte Paketgrößen von natürlichem Datenverkehr unterscheiden lassen.
DNS-Traffic
Ein weiteres Vergleichsbeispiel sind die Mitschnitte des DNS-Verkehrs von Forward-Lookups nach A-Records in unserem Testlabor. Die Paketgrößen hier starten hauptsächlich mit 5 oder 6. Und die Verteilung ist somit – genau wie die CPU-basierte – von der durch den C2-Agent verursachten Verteilung zu unterscheiden.
Das Ergebnis: Es ist uns möglich, zufällig generierte Paketgrößen des C2-Agenten von natürlichem Datenverkehr wirklich zu unterscheiden.
Lässt sich das in der Praxis umsetzen?
In unserem Test konnten wir feststellen, dass die zufällig generierten Paketgrößen in ihrer Größenverteilung durchaus vom natürlichen Datenverkehr abweichen und somit erkennbar sind. Aber ist das auch in der Praxis umsetzbar?
Unsere Tests haben gezeigt, dass in der Realität diese Art der Analyse stark davon abhängig ist, ob die verfügbaren, aus Netzwerkverkehr gewonnen Paketgrößen, den Zufallszahlen entsprechen. Dies setzt jedoch einen hohen Präzisionsgrad hinsichtlich der tatsächlichen Paketgrößen voraus.
Und dieser Präzisionsgrad hängt nicht zuletzt vom verwendeten Protokoll, anderweitigen Kompressionen sowie eingesetzten Verschlüsselungen ab. UDP ist für unsere Analyse das beste Protokoll, da die Paketgrößen eins-zu-eins übertragen werden.
Jedoch sah die Realität in unserem Testszenario etwas anders aus:
🛑 Der C2-Agent verwendet TCP, was zu einer Fragmentierung der Nachrichtenlänge führt.
🛑 Die Überwachung der Netzwerkverkehrsdaten erfolgt mittels NetFlow, das für die Paketgrößenberechnung Aggregation verwendet.
🛑 Vom C2-Agent ausgehender Datenverkehr wird zusätzlich AES-verschlüsselt. Das kann, je nach Implementierung und Modus, ebenfalls die Paketgröße verändern.
Wir müssen also schlussfolgern, dass aus den oben genannten Gründen die Erkennung von C2-Verkehr in Datenverkehr mit randomisiert generierten Paketgrößen nicht möglich ist -zumindest nicht zuverlässig.
Fazit zum Test
Für einen einfachen C2-Agenten mit randomisierten Paketgrößen, der seine zufälligen Paketgrößen direkt über UDP sendet, sind wir in der Lage, diese mit Trovent MDR zuverlässig und ausschließlich anhand des Netzwerkverkehrs zu erkennen.
Jedoch ist die Detektion eines C2-Agents, der TCP nutzt und mit randomisiert generierten Paketgrößen operiert, unter ausschließlicher Nutzung von Netzwerkverkehrsdaten sehr schwierig – und bestenfalls nicht zuverlässig.
Damit der oben beschriebene Ansatz der Detektion von randomisiert erzeugten Paketgrößen in natürlichem Datenverkehr funktionieren kann, benötigen wir die sehr präzise Feststellung von Datenpaketgrößen. Beispielsweise am Endpoint direkt, was aber auch zu einem sehr großen, vermutlich nicht praktikablen Logdatenvolumen führen würde.
Hierfür genügen die von NetFlow generierten Daten leider nicht. Außerdem können Maßnahmen, welche die Paketgröße weiter verfälschen (zum Beispiel Kompression, Verschlüsselung mit Padding) den in diesem Artikel beschriebenen Detektionsansatz weiter erschweren.
Schlussfolgerung für die Umsetzung der Angriffserkennung
➡ Die Tatsache, dass derart genaue Messungen der Paketgröße für die Angriffserkennung nicht zur Verfügung stehen, unterstreicht die Notwendigkeit, C2-Angriffe in den verschiedenen Phasen eines Angriffs erkennen zu können.
➡ Wir haben es eingangs erwähnt: In einem realistischen Angriffsszenario müsste ein Angreifer zunächst die IT-Infrastruktur erfolgreich kompromittieren, um einen C2-Agent entsprechend auf einem Zielsystem installieren und aktivieren zu können.
➡ Allgemein formuliert: Für einen erfolgreichen C2-Angriff müssen mehrere Schritte in der sogenannten Killchain erfolgreich durchschritten werden. Und in jeder der Killchain-Phasen entstehen Indikatoren (IOCs) sowie Verhaltensanomalien, die im Rahmen einer Angriffserkennung eingesetzt werden können – idealerweise weit bevor ein ausgeklügelter Agent wie der C2-Agent von Timo damit beginnt, Daten zu versenden.
➡ Eine effektives System zur Angriffserkennung – SzA – muss immer davon ausgehen, dass es einem Angreifer gelingt in einzelnen Phasen eines Angriffs unbemerkt Fortschritte zu erzielen. Aus diesem Grund setzt das SzA von Trovent konsequent auf die passende Kombination aus regelbasierter und Machine-Learning-gestützter Erkennung, um die Wahrscheinlichkeit der Erkennung in verschiedenen Killchain-Phasen zu maximieren.
Sie suchen ein System zur Angriffserkennung bzw. eine Managed Detection & Response Lösung? Oder möchten Sie wissen, wie die Context Engine von Trovent funktioniert? Kontaktieren Sie uns! Wir stehen gerne für ein Gespräch und für eine Demonstration unserer Lösungen bereit.