Sie sind an Cloud Computing interessiert? Legen Sie jetzt los

Abwehrfunktionen innerhalb von Windows für RPC

Stiv Kupchik

Verfasser

Stiv Kupchik

August 10, 2023

Stiv Kupchik

Verfasser

Stiv Kupchik

Stiv Kupchik ist Senior Security Researcher in Tel Aviv, Israel.

In diesem Beitrag erfahren Sie, wie Sie Ereignisse vom RPC-ETW-Anbieter abrufen und analysieren und wie Sie Ereignisse auf potenziell bösartige Aktivitäten überprüfen.

Inhaltsverzeichnis

Der Inhalt dieses Blogs wurde ursprünglich auf der BlackHat USA 2023 vorgestellt.

MS-RPC: Durchbrechen des Protokolls, um Angriffe zu erkennen

Wir tauchen weiter in MS-RPC ein, mit einem weiteren Blogbeitrag. Doch dieses Mal konzentrieren wir uns nicht auf die Angriffsseite und suchen nach Schwachstellen, sondern besprechen einige der in Windows eingebauten Verteidigungsfunktionen. Wir werden sehen, wie Sie diese nutzen können, um einen Blick darauf zu werfen, was im Hintergrund von RPC abläuft, und dabei hoffentlich schändliche Aktivitäten aufdecken.

MS-RPC ist ein integraler Bestandteil des Windows-Betriebssystems. Das bedeutet, dass viele laterale Bewegungspfade darüber verlaufen. Angriffe wie PSExec, geplante Remote-AufgabenDCSyncund PetitPotam werden alle über MS-RPC-Protokolle ausgeführt. Daher kann es schwierig sein, harmlosen von schädlichem Netzwerktraffic zu unterscheiden, wenn man nur die herkömmlichen Netzwerkmetadaten (Quell- und Zielport und IP-Adresse; manchmal auch Prozessinformationen) kennt.

Wir werden unsere Sichtbarkeit mit der Ereignisverfolgung für Windows (Event Tracing for Windows, ETW), einem in das Windows-Betriebssystem integrierten Verfolgungs- und Überwachungsmechanismus, verstärken. ETW bietet uns eine Fundgrube an Informationen, speziell für RPC, vor allem im Vergleich zu herkömmlichen Netzwerkmetadaten.

Das Konzept der Verfolgung von RPC-Ereignissen mit Hilfe des ETW-Anbieters ist nicht neu. Es gibt bereits viele gute Ressourcen und Tools dafür (wir fügen einige davon in unseren Quellen an; bitte teilen Sie uns mit, wenn Sie weitere kennen). Der Großteil der bisherigen Forschung konzentriert sich auf clientseitige Ereignisse, die Angreifer manipulieren oder das Programm verändern können, um die Protokollierung vollständig zu umgehen. Wir werden uns stattdessen auf die Erkennung von Angriffen anhand von serverseitigen Ereignissen konzentrieren, die außerhalb der Reichweite der Angreifer liegen, und sehen, welche besonderen Überlegungen wir anstellen müssen, um sie zu analysieren.

In diesem Beitrag erfahren Sie, wie Sie Ereignisse vom RPC-ETW-Anbieter abrufen und analysieren und wie Sie Ereignisse auf potenziell bösartige Aktivitäten überprüfen. Mit dem ETW-Anbieter können wir auch die genaue Operation sehen, die angefordert wird, wodurch wir eine viel feinere Granularität erhalten, mit der wir Angriffe erkennen können. Wir werden gleich sehen, wie das funktioniert.

Nochmal zur Erinnerung: Was ist RPC?

RPC steht für Remote Procedure Call (also Remoteprozeduraufruf), eine Form der Kommunikation zwischen Prozessen (IPC). Dieses Protokoll wurde speziell entwickelt, um den Remoteaufruf von Funktionen zwischen verschiedenen Prozessen zu ermöglichen. In unserem Fall konzentrieren wir uns auf die Implementierung von Microsoft, MS-RPCfortsetzen.

MS-RPC ist als Client-Server-Modell konzipiert. Der Server definiert die Schnittstelle, die er zur Verfügung stellt, mit Hilfe der Interface Definition Language (IDL). In der IDL gibt es einen universell eindeutigen Bezeichner (UUID) für die Schnittstelle sowie die Funktionsdefinitionen für die Funktionen, die sie bereitstellt (Abbildung 1).

  [
    uuid(12345678-4000-2006-0000-20000000001a)
]

interface Test
{
    void Foo([in] int number,  [in] char *message);
    void Bar([out] int * result);
}

Abb. 1: Beispiel einer IDL-Schnittstellendefinition

Die Kommunikation erfolgt über bestimmte Transportprotokolle. Jeder Transport hat seine eigene Art von Endpunkt (Abbildung 2). Am besten lässt sich dies anhand eines Beispiels erklären: Die RPC-Kommunikation kann über TCP erfolgen. In diesem Fall ist der Transport TCP und der Endpunkt ist der TCP-Socket, der durch eine Portnummer identifiziert wird.

Transporte

Endpunkte

TCP-

Benannte Pipe

UDP

ALPC

HTTP

Hyper-V-Socket

<Port-Nummer>

<Pipe-Name>

<Port-Nummer>

<ALPC-Port>

<Hostname>

<UUID>

Abb. 2: Die gängigen Transportprotokolle und ihre jeweiligen Endpunkttypen

Es ist wichtig zu wissen, dass Funktionen in der IDL-Datei zwar einen lesbaren Namen haben, aber im Netzwerk anders identifiziert werden. Anstelle eines Zeichenfolgennamens werden sie durch eine Ganzzahl, genannt Opnum (Abkürzung für Operationsnummer), identifiziert. Sie wird in der Regel in der Reihenfolge zugewiesen, in der die Funktionen in der IDL-Schnittstellendefinition erscheinen (im Beispiel in Abbildung 1 wird Foo also mit Opnum 0 identifiziert, während Bar Opnum 1 ist). Dies ist später wichtig, wenn wir die Opnum der relevanten Funktionen kennen müssen, um sie in den Daten, die wir sehen werden, zu identifizieren.

Einen längeren und ausführlicheren Überblick über MS-RPC finden Sie in unserem vorherigen Beitrag oder in einer unserer Konferenzpräsentationen auf der HexaCon oder DEF CON zu diesem Thema.

ETW definieren

Event Tracing for Windows (ETW) ist ein integrierter Ablaufverfolgungs- und Protokollierungsmechanismus, der im Windows-Kernel implementiert ist. Es funktioniert nach einem Anbieter-Verbraucher-Modell: Anbieter senden Ereignisse an den Kernel, der sie an beliebige Verbraucherprogramme umleitet. Sowohl Anbieter als auch Verbraucher müssen sich im Voraus beim Kernel registrieren (Abbildung 3).

Da die Weiterleitung von Ereignissen durch den Kernel erfolgt, werden Ereignisse, die von Anbietern gesendet werden, für die es aber keine Verbraucher gibt, einfach verworfen und ins Leere geschickt.

Reporting-Ablauf für ETW-Ereignisse Abb. 3: Eine Darstellung des Reporting-Ablaufs für ETW-Ereignisse

Microsoft-Windows-RPC

Der RPC-ETW-Anbieter wird in der RPC-Laufzeit rpcrt4.dll implementiert. Es gibt 13 verschiedene Ereignisse, aber wir sind hauptsächlich an vier von ihnen interessiert – die Ereignisse 5 und 7 für den Start und das Ende von Client-Aufrufen und die Ereignisse 6 und 8 für den Start und das Ende von Server-Aufrufen. Wir konzentrieren uns auf die Ereignisse beim Start des Aufrufs (Abbildung 4), da sie die meisten Informationen liefern. (Die Aufrufstoppereignisse geben einfach den RPC-Rückgabestatus an.) Client- und Serverereignisse haben dasselbe Format.

  <template tid="RpcServerCallStartArgs_V1">
     <data name="InterfaceUuid" inType="win:GUID"/>
     <data name="ProcNum" inType="win:UInt32"/>
     <data name="Protocol" inType="win:UInt32"/>
     <data name="NetworkAddress" inType="win:UnicodeString"/>
     <data name="Endpoint" inType="win:UnicodeString"/>
     <data name="Options" inType="win:UnicodeString"/>
     <data name="AuthenticationLevel" inType="win:UInt32"/>
     <data name="AuthenticationService" inType="win:UInt32"/>
     <data name="ImpersonationLevel" inType="win:UInt32"/>
  </template>

Abb. 4: Ereignisschema für Start des RPC ETW-Aufrufs

Theoretisch sollten uns die Daten im Ereignis alle Metadaten liefern, die wir benötigen, um Einblicke in den RPC-Traffic zu gewinnen – wir wissen jetzt, was von der Schnittstellen-UUID und Opnum angefordert wird und wir kennen auch die Quell- oder Zieladresse (je nachdem, ob wir uns Client- oder Server-Ereignisse ansehen) – und zwar anhand des Felds „NetworkAddress“. So einfach kann es aber nicht sein, oder?

Ist es auch nicht. Es zeigt sich, dass die RPC-Laufzeitumgebung das Feld NetworkAddress bei der Verarbeitung von Serveraufruf-Ereignissen nicht ausfüllt. Wir müssen also einen anderen Weg finden, um diese Daten zu finden, wenn wir die Verbindungs-Metadaten haben wollen. Für Clientereignisse ist dieses Feld jedoch ausgefüllt.

Ein Snippet der RPC-Laufzeit. Innerhalb einer if-Prüfung, die die globale Variable Microsoft_Windows_RPCEnableBits überprüft, gibt es einen Aufruf der Funktion EtwEventWriteTransfer. Die Funktion empfängt viele Argumente, wobei das der Netzwerkadresse als 0 gesendet wird. Abb. 5: Ein Snippet der RPC-Laufzeit; hervorgehoben ist das Feld für die Netzwerkadresse, die 0 lautet

Das wirft die Frage auf: Können wir Serverereignisse ignorieren und uns nur auf die Clientseite verlassen? Die Antwort darauf lautet: Nein. Da wir versuchen, schädliches Verhalten zu finden, das von dem vom Angreifer kontrollierten Computer stammt, können wir nicht sicher sein, dass sie nicht den clientseitigen ETW-Anbieter manipulieren (und ihn ausschalten oder seine Ereignisse blockieren). Wir können auch nicht sicher sein, dass sie die RPC-Laufzeit durchlaufen werden.

Die beliebte Python-Bibliothek Impacket, die häufig in Machbarkeitsnachweisen und Tools für Angriffe verwendet wird (und Implementierungen von Netzwerkangriffen wie z. B. PSExec enthält), implementiert den RPC-Traffic, sodass Angreifer, die ihn verwenden, die RPC-Laufzeit umgehen können und nicht im ETW-Anbieter registriert werden. Da Serverereignisse außerhalb der Kontrolle der Angreifer liegen, ist es sinnvoller, sich auf sie zu verlassen.Deshalb konzentrieren wir uns jetzt darauf, wie wir die Netzwerkdaten von anderer Stelle erhalten und sie mit dem RPC-Ereignis abgleichen können.

Abgleich von RPC-Ereignissen mit Netzwerkflüssen

Wir verlassen nun unseren Fokus auf RPC-ETW-Anbieter kurz, um uns andere ETW-Anbieter anzusehen, nämlich den TCP- und SMB-Anbieter . Diese beiden Protokolle sind die gängigen Transportprotokolle für RPC-Traffic. Da wir gesagt haben, dass der RPC-Endpunkttyp vom Transportprotokoll abhängt, können wir den Endpunkt (Portnummer, Pipe-Name usw.), wie wir ihn vom RPC-Anbieter erhalten, mit dem entsprechenden Wert im ETW-Transportanbieter abgleichen.

Für TCP ist das ziemlich einfach. Sehen wir uns die Ereignis-ID 1017 namens TcpAcceptListenerCompletean, die ausgelöst wird, sobald der TCP-Dreiwege-Handshake abgeschlossen ist.

Sie enthält (im Grunde) zwei Felder: LocalAddress und RemoteAddress. (Da wir uns mit Serverereignissen befassen, bezieht sich local auf den Server und remote auf den Client.) Die Werte der Adressfelder sind binär und enthalten die Adressfamilie, die IP-Adresse und die Portnummer (Abbildung 6).

0

1

2

3

4

5

6

7

Adressfamilie

Portnummer

Adresswert (IP bei AF_INET)

Abb. 6: Das Format des binären Adressfeldes

Wir müssen lediglich die IP-Adresse des Kunden aus dem Feld RemoteAddress extrahieren und mit dem (Ziel-)Port im Feld LocalAddress verfolgen. Sobald wir nun ein RPC-Ereignis auf unserem Server über dieselbe TCP-Portnummer erhalten, können wir anhand der Port-zu-IP-Zuordnung, die wir aus dem TCP-Anbieter extrahiert haben, feststellen, woher es stammt (Abbildung 7).

Eine Infografik, die zeigt, wie RPC- und TCP-Ereignisse abgeglichen werden. Beide Ereignisse werden als passende Puzzleteile dargestellt. Das RPC-Ereignis enthält das Feld „Endpoint“, das der lokale TCP-Port ist. Das TCP-Ereignis enthält das Feld „LocalAddress“, das auch den lokalen TCP-Port enthält. Außerdem ist das Feld „RemoteAddress“ mit der Client-IP enthalten. Abb. 7: Abgleich der TCP- und RPC-Ereignisse

Bei SMB ist die Situation ein wenig komplizierter, da verschiedene Informationen in verschiedenen ETW-Ereignissen erscheinen. Bei dem Endpunkt handelt es sich um eine benannte Pipe, die einer Datei entspricht. Allerdings kann über dieselbe SMB-Sitzung auf mehrere Dateien zugegriffen werden. Um den Endpunkt mit seiner Netzwerkquelle abzugleichen, müssen wir in diesem Fall also zwei Ereignisse verfolgen: eines für die eigentliche Verbindung und eines für die Dateianfrage (Abbildung 8).

Für das Verbindungsereignis haben wir die Ereignis-ID 500, Smb2ConnectionAcceptStart, die ausgelöst wird, wenn die SMB-Verbindung hergestellt wird. Wir erhalten die Quell-IP und eine Verbindungs-UUID. Dann suchen wir nach der Ereignis-ID 8, Smb2RequestCreate_V2, die den angeforderten Dateinamen und dasselbe Feld für die Verbindungs-UUID enthält. Jetzt müssen wir nur noch einen Querverweis zwischen beiden Ereignissen über die UUID der Verbindung herstellen, um den Pipe-Namen mit der IP-Adresse abzugleichen, die ihn angefordert hat (und später müssen wir den Pipe-Namen mit dem Endpunkt des RPC-Aufrufs abgleichen).

Ein Diagramm, das den Prozess zum Abgleichen von SMB mit RPC-Ereignissen zeigt. Im Ereignis Smb2ConnectionAcceptStart speichern wir die IP-Adresse und verwenden die Verbindungs-GUID, um sie mit dem darauf folgenden Ereignis Smb2RequestCreate_V2 abzugleichen. Innerhalb von Smb2RequestCreate_V2 können wir den Pipe-Namen nehmen, der dann mit dem Endpunktfeld innerhalb des Ereignisses RpcServerCallStart_V1 abgeglichen wird, das vom RPC-Anbieter stammt. Davon behalten wir die Felder „Opnum“ und „Interface“, die uns zusammen mit dem zuvor gespeicherten IP-Feld Einblicke liefern. Abb. 8: Verwenden von SMB-ETW-Ereignissen für den Abgleich von Quell-IP mit RPC-ETW-Ereignissen

Um Ihnen die Mühe zu ersparen, all diese Abgleiche selbst zu implementieren, haben wir diesen Algorithmus implementiert. Sie finden das Tool RPC Visibility in unserem GitHub-Repository. Das Tool ist in Python geschrieben und speichert den aufgezeichneten RPC-Netzwerktraffic zur einfachen Visualisierung in einer Neo4j-Datenbank.

Angriffserkennung

Jetzt, da wir alle Informationen haben, die wir brauchen, können wir uns endlich der Erkennung schädlicher RPC-Flows zuwenden. Der allgemeine Prozess ist einfach: Wir finden einen Angriff, der über RPC ausgeführt wird, führen einen PoC durch und sehen, welche RPC-Schnittstelle er verwendet, sowie die angeforderte Operation. Dann können wir einfach eine Signatur erstellen, die auf diese Operation passt. Wir haben Signaturabfragen, die mit der Neo4j-Datenbankimplementierung funktionieren, in unsere Version aufgenommen, aber lesen Sie weiter für die allgemeine Logik und die Bedingungen.

PSExec

PSExec ist sowohl ein allgemeiner Name für eine Angriffstechnik als auch ein Tool von Sysinternals (das Tool, das der Technik ihren Namen gab). Grundsätzlich kopiert das Tool eine Dienst-Binärdatei in die ADMIN$-Freigabe (den Windows-Installationsordner) des entfernten Ziels und kommuniziert dann mit dem Dienstmanager über dessen RPC-Schnittstelle (MS-SCMR), um einen Dienst für die Binärdatei zu erstellen und auszuführen (Abbildung 9).

Der Ablauf eines PsExec-Angriffs. Ein Hacker, ein PC und zwei Pfeile zwischen beiden. Der erste Pfeil trägt die Bezeichnung „1. SMB“ und enthält den Text „CP psexecsvc.exe \\VICTIM\ADMIN$\psexesvc.exe“, um den Befehl zum Kopieren der Dienstbinärdatei auf den betroffenen Computer anzuzeigen. Der zweite Pfeil trägt die Bezeichnung „2. RPC, MS-SCMR“ und enthält den Text „RCreateServiceW(\\victim, C:\Windows\psexesvc.exe)“, um die SCMR-Funktion anzuzeigen, die aufgerufen wird, um den Dienst remote zu starten. Abb. 9: Ablauf eines PsExec-Angriffs

Es gibt viele legitime Gründe für die Kontaktaufnahme mit Remote-Computern über SCMR, z. B. Watchdogs, die den Status von entfernten Diensten abfragen. Es gibt viel weniger Gründe, neue Dienste remote zu erstellen. Daher möchten wir nicht bei jeder Verbindung über SCMR (die wir auch ohne RPC ETW erkennen können, indem wir einfach eine eingehende Netzwerkverbindung mit dem Service Manager-Prozess abgleichen services.exe) Alarme auslösen, sondern nur bei Verbindungen, die Remote-Dienste erstellen.

Dementsprechend sollte unsere Signatur wie folgt aussehen (allgemein ausgedrückt, nicht spezifisch für unsere RPC-Visibility-Implementierung):

interface_uuid ==  “367ABB81-9844-35F1-AD32-98F038001003” AND (opnum == 0xC OR opnum == 0x18)

Wobei 0xC die Opnum für RCreateServiceW und 0x18 für RCreateServiceA ist.

Remote Task Scheduler

Ähnlich wie PSExec kann der Taskplaner verwendet werden, um ein Remote-Binary zu starten und auf diese Weise eine laterale Netzwerkbewegung zu erreichen. Es muss nicht einmal ein neues Binary gestartet werden, da es genauso gut eine CMD- oder PowerShell-Konsole starten und ein online gehostetes Binary herunterladen kann.

Ähnlich wie bei PSExec wollen wir nicht nur jeden Zugriff auf den Taskplaner-Dienst erkennen, sondern sind vor allem an RPC-Aufrufen an SchRpcRegisterTask interessiert.

interface_uuid ==  “86D35949-83C9-4044-B424-DB363231FD0C” AND opnum == 0x1

DCSync

DCSync ist ein weiterer RPC-basierter Angriff, der jedoch auf Domain-Controller abzielt. In diesem Fall verbindet sich der Angreifer mit dem echten Domain-Controller und gibt vor, ein neuer Domain-Controller zu sein. Er bittet dann darum, die Anmeldedatenbank des Domänencontrollers zu replizieren und erhält so Zugriff auf die KRBTGT-Passwort-Hashes.

Die Replikationsanforderung erfolgt über die Schnittstelle MS-DRSR und verwendet die spezifische Funktion DRSGetNCChanges (Opnum 3).

interface_uuid ==  “e3514235-4b06-11d1-ab04-00c04fc2dcd2” AND opnum == 0x3

PetitPotam

PetitPotam ist ein Angriff auf den EFS-Dienst (Encrypted File System). Grundsätzlich können Angreifer eine Verbindung zur EFS-RPC-Schnittstelle (MS-EFSR) herstellen und anweisen, eine Remote-Datei zu öffnen, die durch einen UNC-Pfad angegeben ist. Das würde dann eine ausgehende SMB-Verbindung mit Authentifizierung auslösen, die der Angreifer dann weiterleiten kann.

Als der Angriffs-PoC veröffentlicht wurde, verwendete er EfsRpcOpenFileRaw (Opnum 0), das inzwischen gepatcht wurde. Topotam, der Forscher, der die Schwachstelle entdeckte, fand auch heraus, dass EfsRpcEncryptFileSrv (Opnum 4) den gleichen Fehler hatte.

interface_uuid ==  “c681d488-d850-11d0-8c52-00c04fd90f7e” AND (opnum == 0x0 OR opnum == 0x4)

Nachteile

Auch wenn der RPC ETW-Anbieter eine Menge Informationen bereithält, ist er keine zentrale Anlaufstelle für alle unsere Sicherheitsbedürfnisse. Es ist zwar wichtig zu wissen, welche Operation in jedem Netzwerkfluss angefordert wird, aber die wichtigste Information, nämlich welche Daten bei jeder Anforderung übergeben wurden, wird nicht protokolliert. Das bedeutet, dass die Erkennung von lateralen Netzwerkbewegungen über den ETW-Anbieter nach wie vor nur ein heuristischer Ansatz ist, wenn auch mit viel mehr Kontext als bei den traditionellen Netzwerk-Viertupeln.

Außerdem handelt es sich um eine reine Erkennungsmethode, die nicht dazu verwendet werden kann, Angriffe zu stoppen oder darauf zu reagieren. Microsoft stellt uns einen weiteren eingebauten Abwehrmechanismus für RPC zur Verfügung – den RPC-Filter in der Windows-Firewall. Weitere Informationen zu diesem Filtermechanismus finden Sie in unserem Leitfaden zum RPC-Filter (Remote Procedure Call).

Zusammenfassung

Der RPC ETW-Anbieter ist keine neue Ergänzung zu Windows aber er wurde bei der Netzwerkverteidigung bisher weitgehend vernachlässigt. Es gibt zwar einige Tools, die mit ihm interagieren und Ereignisse verarbeiten, aber sie richten sich meist an Sicherheitsforscher und konzentrieren sich weniger auf die Netzwerkseite.

In diesem Beitrag haben wir erörtert, wie wir den RPC-ETW-Anbieter in Verbindung mit den TCP- und SMB-Anbietern verwenden können, um Einblick in die angeforderten RPC-Operationen zu erhalten, die aus dem Netzwerk kommen. Damit verfügen wir über einen heuristischen Ansatz, mit dem wir mögliche schädliche Anfragen erkennen können, die für laterale Netzwerkbewegungen genutzt werden können.

Quellen



Stiv Kupchik

Verfasser

Stiv Kupchik

August 10, 2023

Stiv Kupchik

Verfasser

Stiv Kupchik

Stiv Kupchik ist Senior Security Researcher in Tel Aviv, Israel.