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.

Die Abfragedetails auf der Registerkarte **Ergebnisse** im Microsoft Defender-Portal

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 von contains. Weitere Informationen zu Zeichenfolgenoperatoren

  • Suchen 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. und contains_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 Analysefunktionen

  • Filtern 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 sie IdentityLogonEvents ü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ür Timestamp > 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 Joinhinweisen

    Der 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 durch projectersetzt 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 der join -Operator können Sie auch den Shuffle-Hinweis mit summarize 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ält hint.shufflekeyes :

    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 (ProcessIdoderInitiatingProcessId) 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~und contains anstelle von ==, inund contains_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.

Tipp

Möchten Sie mehr erfahren? Engage mit der Microsoft-Sicherheitscommunity in unserer Tech Community: Microsoft Defender XDR Tech Community.