Bewährte Methoden für Erweiterte Suchanfragen
Gilt für:
- Microsoft Defender XDR
Wenden Sie diese Empfehlungen an, um beim Ausführen komplexer Abfragen schneller Ergebnisse zu erhalten und Timeouts zu vermeiden. Weitere Informationen zur Verbesserung der Abfrageleistung finden Sie inBewährte Methoden für Kusto Anfragen.
Grundlegendes zu CPU-Ressourcenkontingenten
Je nach Größe hat jeder Mandant Zugriff auf eine festgelegte Menge von CPU-Ressourcen, die für die Ausführung erweiterter Huntingabfragen zugeordnet sind. Ausführliche Informationen zu verschiedenen Verwendungsparametern finden Sie unter Erweiterte Huntingkontingente und Verwendungsparameter.
Nach dem Ausführen der Abfrage können Sie die Ausführungszeit und deren Ressourcennutzung (Niedrig, Mittel, Hoch) anzeigen. Hoch gibt an, dass die Abfrage mehr Ressourcen für die Ausführung benötigt hat und verbessert werden kann, um Ergebnisse effizienter zurückzugeben.
Kunden, die regelmäßig mehrere Abfragen ausführen, sollten den Verbrauch nachverfolgen und den Optimierungsleitfaden in diesem Artikel anwenden, um Unterbrechungen aufgrund der Überschreitung von Kontingenten oder Nutzungsparametern zu minimieren.
Sehen Sie sich Die Optimierung von KQL-Abfragen an, um einige der gängigsten Möglichkeiten zur Verbesserung Ihrer Abfragen zu sehen.
Allgemeine Optimierungstipps
Größe neuer Abfragen: Wenn Sie vermuten, dass eine Abfrage ein großes Resultset zurückgibt, bewerten Sie diese zuerst mithilfe des Count-Operators. Verwenden Sie limit oder das zugehörige Synonym
take
, um große Resultsets zu vermeiden.Frühzeitige Anwendung von Filtern– Wenden Sie Zeitfilter und andere Filter an, um das Dataset zu reduzieren, insbesondere vor der Verwendung von Transformations- und Analysefunktionen, z. B. substring(),, replace(),, trim(),, toupper() oder parse_json(). Im folgenden Beispiel wird die Analysefunktion extractjson() verwendet, nachdem Filteroperatoren die Anzahl der Datensätze reduziert haben.
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "UsbDriveMount" | where DeviceName == "user-desktop.domain.com" | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
Hat Beats contains – Um zu vermeiden, dass Teilzeichenfolgen in Wörtern unnötig durchsucht werden, verwenden Sie den
has
-Operator anstelle voncontains
. Weitere Informationen zu ZeichenfolgenoperatorenSuchen in bestimmten Spalten – Suchen Sie in einer bestimmten Spalte, anstatt Volltextsuchen für alle Spalten auszuführen. Verwenden
*
Sie nicht , um alle Spalten zu überprüfen.Bei der Geschwindigkeit wird die Groß-/Kleinschreibung beachtet: Suchvorgänge, bei denen die Groß-/Kleinschreibung beachtet wird, sind spezifischer und im Allgemeinen leistungsstärker. Namen von Zeichenfolgenoperatoren, bei denen die Groß-/Kleinschreibung beachtet wird, z
has_cs
. B. undcontains_cs
, enden in der Regel auf_cs
. Sie können anstelle von auch den Gleichheitsoperator==
verwenden, bei dem die Groß-/Kleinschreibung beachtet wird=~
.Analysieren, nicht extrahieren– Verwenden Sie nach Möglichkeit den Parse-Operator oder eine Analysefunktion wie parse_json(). Vermeiden Sie den
matches regex
Zeichenfolgenoperator oder die extract()-Funktion, die beide reguläre Ausdrücke verwenden. Reservieren Sie die Verwendung von regulären Ausdrücken für komplexere Szenarien. Weitere Informationen zu AnalysefunktionenFiltern von Tabellen nicht ausdrücken– Filtern Sie nicht nach einer berechneten Spalte, wenn Sie nach einer Tabellenspalte filtern können.
Keine Drei-Zeichen-Begriffe– Vermeiden Sie den Vergleich oder die Filterung von Begriffen mit maximal drei Zeichen. Diese Begriffe sind nicht indiziert und erfordern mehr Ressourcen.
Selektives Projizieren: Vereinfachen Sie das Verständnis Ihrer Ergebnisse, indem Sie nur die benötigten Spalten projizieren. Das Projizieren bestimmter Spalten vor dem Ausführen von Join - oder ähnlichen Vorgängen trägt ebenfalls zur Leistungsverbesserung bei.
Optimieren des join
Operators
Der Joinoperator führt Zeilen aus zwei Tabellen durch Übereinstimmen von Werten in angegebenen Spalten zusammen. Wenden Sie diese Tipps an, um Abfragen zu optimieren, die diesen Operator verwenden.
Kleinere Tabelle auf der linken Seite– Der
join
Operator gleicht Datensätze in der Tabelle auf der linken Seite der Join-Anweisung mit Datensätzen auf der rechten Seite ab. Durch die kleinere Tabelle auf der linken Seite müssen weniger Datensätze abgeglichen werden, wodurch die Abfrage beschleunigt wird.In der folgenden Tabelle reduzieren wir die linke Tabelle
DeviceLogonEvents
so, dass sie nur drei bestimmte Geräte abdeckt, bevor sieIdentityLogonEvents
über Konto-SIDs zugeordnet wird.DeviceLogonEvents | where DeviceName in ("device-1.domain.com", "device-2.domain.com", "device-3.domain.com") | where ActionType == "LogonFailed" | join (IdentityLogonEvents | where ActionType == "LogonFailed" | where Protocol == "Kerberos") on AccountSid
Verwenden sie die Variante inner-join – Die standardmäßige Verknüpfungsvariante oder innerunique-join dedupliziert Zeilen in der linken Tabelle durch die Verknüpfungsschlüssel, bevor eine Zeile für jede Übereinstimmung an die rechte Tabelle zurückgegeben wird. Wenn die linke Tabelle mehrere Zeilen mit demselben Wert für den
join
Schlüssel enthält, werden diese Zeilen dedupliziert, um für jeden eindeutigen Wert eine einzelne zufällige Zeile zu hinterlassen.Dieses Standardverhalten kann wichtige Informationen aus der linken Tabelle weglassen, die nützliche Erkenntnisse liefern können. Die folgende Abfrage zeigt beispielsweise nur eine E-Mail an, die eine bestimmte Anlage enthält, auch wenn dieselbe Anlage mit mehreren E-Mail-Nachrichten gesendet wurde:
EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Um diese Einschränkung zu beheben, wenden wir die Innere Join-Variante an, indem angegeben
kind=inner
wird, dass alle Zeilen in der linken Tabelle mit übereinstimmenden Werten auf der rechten Seite angezeigt werden:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Einbinden von Datensätzen aus einem Zeitfenster: Bei der Untersuchung von Sicherheitsereignissen suchen Analysten nach verwandten Ereignissen, die im selben Zeitraum auftreten. Die Anwendung desselben Ansatzes bei der Verwendung von
join
profitiert auch von der Leistung, da die Anzahl der zu überprüfenden Datensätze reduziert wird.Die folgende Abfrage sucht innerhalb von 30 Minuten nach dem Empfang einer schädlichen Datei nach Anmeldeereignissen:
EmailEvents | where Timestamp > ago(7d) | where ThreatTypes has "Malware" | project EmailReceivedTime = Timestamp, Subject, SenderFromAddress, AccountName = tostring(split(RecipientEmailAddress, "@")[0]) | join ( DeviceLogonEvents | where Timestamp > ago(7d) | project LogonTime = Timestamp, AccountName, DeviceName ) on AccountName | where (LogonTime - EmailReceivedTime) between (0min .. 30min)
Anwenden von Zeitfiltern auf beiden Seiten – Auch wenn Sie kein bestimmtes Zeitfenster untersuchen, kann das Anwenden von Zeitfiltern auf die linke und rechte Tabelle die Anzahl der Datensätze reduzieren, um die Leistung zu überprüfen und zu verbessern
join
. Die folgende Abfrage gilt fürTimestamp > ago(1h)
beide Tabellen, sodass nur Datensätze aus der letzten Stunde verknüpft werden:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Verwenden von Hinweisen für die Leistung– Verwenden Sie Hinweise mit dem
join
Operator, um das Back-End anzuweisen, die Last zu verteilen, wenn ressourcenintensive Vorgänge ausgeführt werden. Weitere Informationen zu JoinhinweisenDer Shuffle-Hinweis trägt beispielsweise dazu bei, die Abfrageleistung beim Verknüpfen von Tabellen mit einem Schlüssel mit hoher Kardinalität – einem Schlüssel mit vielen eindeutigen Werten – zu verbessern, z
AccountObjectId
. B. in der folgenden Abfrage:IdentityInfo | where JobTitle == "CONSULTANT" | join hint.shufflekey = AccountObjectId (IdentityDirectoryEvents | where Application == "Active Directory" | where ActionType == "Private data retrieval") on AccountObjectId
Der Broadcasthinweis ist hilfreich, wenn die linke Tabelle klein ist (bis zu 100.000 Datensätze) und die rechte Tabelle extrem groß ist. Die folgende Abfrage versucht beispielsweise, einige E-Mails mit bestimmten Themen mit allen Nachrichten, die Links in der
EmailUrlInfo
Tabelle enthalten, zu verbinden:EmailEvents | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now") | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
Optimieren des summarize
Operators
Der summarize-Operator aggregiert den Inhalt einer Tabelle. Wenden Sie diese Tipps an, um Abfragen zu optimieren, die diesen Operator verwenden.
Unterschiedliche Werte finden – Verwenden Sie
summarize
im Allgemeinen, um unterschiedliche Werte zu finden, die sich wiederholen können. Es kann unnötig sein, sie zum Aggregieren von Spalten zu verwenden, die keine sich wiederholenden Werte aufweisen.Während eine einzelne E-Mail Teil mehrerer Ereignisse sein kann, ist das folgende Beispiel keine effiziente Verwendung von,
summarize
da eine Netzwerknachrichten-ID für eine einzelne E-Mail immer eine eindeutige Absenderadresse enthält.EmailEvents | where Timestamp > ago(1h) | summarize by NetworkMessageId, SenderFromAddress
Der
summarize
Operator kann leicht durchproject
ersetzt werden, was potenziell die gleichen Ergebnisse liefert, während weniger Ressourcen verbraucht werden:EmailEvents | where Timestamp > ago(1h) | project NetworkMessageId, SenderFromAddress
Das folgende Beispiel ist eine effizientere Verwendung von,
summarize
da es mehrere unterschiedliche Instanzen einer Absenderadresse geben kann, die E-Mails an dieselbe Empfängeradresse senden. Solche Kombinationen unterscheiden sich weniger und weisen wahrscheinlich Duplikate auf.EmailEvents | where Timestamp > ago(1h) | summarize by SenderFromAddress, RecipientEmailAddress
Shuffle the query (Mischen der Abfrage): Während
summarize
am besten in Spalten mit sich wiederholenden Werten verwendet wird, können dieselben Spalten auch eine hohe Kardinalität oder eine große Anzahl eindeutiger Werte aufweisen. Wie derjoin
-Operator können Sie auch den Shuffle-Hinweis mitsummarize
anwenden, um die Verarbeitungslast zu verteilen und die Leistung zu verbessern, wenn Sie mit Spalten mit hoher Kardinalität arbeiten.In der folgenden Abfrage wird verwendet
summarize
, um unterschiedliche Empfänger-E-Mail-Adressen zu zählen, die in großen Organisationen zu Hunderttausenden ausgeführt werden können. Um die Leistung zu verbessern, enthälthint.shufflekey
es :EmailEvents | where Timestamp > ago(1h) | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
Abfrageszenarien
Identifizieren eindeutiger Prozesse mit Prozess-IDs
Prozess-IDs (PIDs) werden in Windows für neue Prozesse wiederverwendet. Allein können sie nicht als eindeutige Identifikatoren für bestimmte Prozesse dienen.
Um einen eindeutiger Bezeichner für einen Prozess auf einer bestimmten Maschine abzurufen, verwenden Sie die Prozess-ID zusammen mit der Prozesserstellungszeit. Wenn Sie Daten um Prozesse zusammenführen oder zusammenfassen, fügen Sie Spalten für die Maschinenkennung (entweder DeviceId
oderDeviceName
), die Prozess-ID (ProcessId
oderInitiatingProcessId
) und die Prozesserstellungszeit (ProcessCreationTime
oderInitiatingProcessCreationTime
) hinzu.
Die folgende Beispielabfrage findet Prozesse, die über Port 445 (SMB) auf mehr als 10 IP-Adressen zugreifen und möglicherweise nach Dateifreigaben suchen.
Beispielabfrage:
DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(12h) and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessFileName
| where RemoteIPCount > 10
Die Query fasst sowohlInitiatingProcessId
als auchInitiatingProcessCreationTime
zusammen, um einen einzelnen Prozess darzustellen, ohne mehrere Prozesse mit derselben Prozess-ID miteinander zu vermischen.
Abfragebefehlszeilen
Es gibt zahlreiche Möglichkeiten, eine Befehlszeile zu erstellen, um eine Aufgabe auszuführen. Beispielsweise könnte ein Angreifer ohne Pfad, ohne Dateierweiterung, mit Umgebungsvariablen oder mit Anführungszeichen auf eine Bilddatei verweisen. Der Angreifer könnte auch die Reihenfolge der Parameter ändern oder mehrere Anführungszeichen und Leerzeichen hinzufügen.
Wenden Sie die folgenden Methoden an, um dauerhaftere Abfragen für Befehlszeilen zu erstellen:
- Identifizieren Sie die bekannten Prozesse (z. B.net.exe oder psexec.exe), indem Sie die Dateinamenfelder abgleichen, anstatt in der Befehlszeile selbst zu filtern.
- Analysieren von Befehlszeilenabschnitten mithilfe der funktion parse_command_line()
- Wenn Sie nach Befehlszeilenargumenten suchen, suchen Sie nicht nach einer genauen Übereinstimmung für mehrere unabhängige Argumente in einer bestimmten Reihenfolge. Verwenden Sie stattdessen reguläre Ausdrücke oder verwenden Sie mehrere separate enthaltene Operatoren.
- Verwenden Sie Übereinstimmungen zwischen Groß- und Kleinschreibung. Verwenden Sie
=~
beispielsweise ,in~
undcontains
anstelle von==
,in
undcontains_cs
. - Um die Techniken zur Befehlszeilenverschleierung zu verringern, sollten Sie Anführungszeichen entfernen, Kommas durch Leerzeichen ersetzen und mehrere aufeinanderfolgende Leerzeichen durch ein einziges Leerzeichen ersetzen. Es gibt komplexere Verschleierungstechniken, die andere Ansätze erfordern, aber diese Optimierungen können dazu beitragen, gängige Methoden zu beheben.
Die folgenden Beispiele zeigen verschiedene Möglichkeiten zum Erstellen einer Abfrage, die nach der Datei sucht,net.exe , um den Firewalldienst "MpsSvc" zu beenden:
// Non-durable query - do not use
DeviceProcessEvents
| where ProcessCommandLine == "net stop MpsSvc"
| limit 10
// Better query - filters on file name, does case-insensitive matches
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe") and ProcessCommandLine contains "stop" and ProcessCommandLine contains "MpsSvc"
// Best query also ignores quotes
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe")
| extend CanonicalCommandLine=replace("\"", "", ProcessCommandLine)
| where CanonicalCommandLine contains "stop" and CanonicalCommandLine contains "MpsSvc"
Erfassen von Daten aus externen Quellen
Wenn Sie lange Listen oder große Tabellen in Ihre Abfrage integrieren möchten, verwenden Sie den externaldata-Operator , um Daten aus einem angegebenen URI zu erfassen. Sie können Daten aus Dateien in TXT, CSV, JSON oder anderen Formaten abrufen. Das folgende Beispiel zeigt, wie Sie die umfangreiche Liste der SHA-256-Schadsoftware-Hashes nutzen können, die von MalwareBazaar (abuse.ch) bereitgestellt werden, um Anlagen in E-Mails zu überprüfen:
let abuse_sha256 = (externaldata(sha256_hash: string)
[@"https://bazaar.abuse.ch/export/txt/sha256/recent/"]
with (format="txt"))
| where sha256_hash !startswith "#"
| project sha256_hash;
abuse_sha256
| join (EmailAttachmentInfo
| where Timestamp > ago(1d)
) on $left.sha256_hash == $right.SHA256
| project Timestamp,SenderFromAddress,RecipientEmailAddress,FileName,FileType,
SHA256,ThreatTypes,DetectionMethods
Analysieren von Zeichenfolgen
Es gibt verschiedene Funktionen, mit denen Sie Zeichenfolgen effizient behandeln können, die analysiert oder konvertiert werden müssen.
String | Funktion | Verwendungsbeispiel |
---|---|---|
Befehlszeilen | parse_command_line() | Extrahieren Sie den Befehl und alle Argumente. |
Paths | parse_path() | Extrahieren Sie die Abschnitte eines Datei- oder Ordnerpfads. |
Versionsnummern | parse_version() | Dekonstruieren Sie eine Versionsnummer mit bis zu vier Abschnitten und bis zu acht Zeichen pro Abschnitt. Verwenden Sie die analysierten Daten, um das Versionsalter zu vergleichen. |
IPv4-Adressen | parse_ipv4() | Konvertieren sie eine IPv4-Adresse in eine lange ganze Zahl. Um IPv4-Adressen ohne Konvertierung zu vergleichen, verwenden Sie ipv4_compare(). |
IPv6-Adressen | parse_ipv6() | Konvertieren Sie eine IPv4- oder IPv6-Adresse in die kanonische IPv6-Notation. Verwenden Sie zum Vergleichen von IPv6-Adressen ipv6_compare(). |
Weitere Informationen zu allen unterstützten Analysefunktionen finden Sie unter Kusto-Zeichenfolgenfunktionen.
Hinweis
Einige Tabellen in diesem Artikel sind in Microsoft Defender for Endpoint möglicherweise nicht verfügbar. Aktivieren Sie Microsoft Defender XDR, um mithilfe weiterer Datenquellen nach Bedrohungen zu suchen. Sie können Ihre Workflows für die erweiterte Suche von Microsoft Defender for Endpoint auf Microsoft Defender XDR verschieben, indem Sie die Schritte unter Migrieren von Abfragen für erweiterte Suche von Microsoft Defender for Endpoint ausführen.
Verwandte Themen
- Dokumentation zur Kusto-Abfragesprache
- Kontingente und Verwendungsparameter
- Behandeln von Fehlern bei der erweiterten Suche
- Übersicht über die erweiterte Suche
- Lernen der Abfragesprache
Tipp
Möchten Sie mehr erfahren? Wenden Sie sich an die Microsoft Security-Community in unserer Tech Community: Microsoft Defender XDR Tech Community.