Metodtips för avancerad jaktfråga

Gäller för:

  • Microsoft Defender XDR

Använd dessa rekommendationer för att få resultat snabbare och undvika timeouter när du kör komplexa frågor. Mer information om hur du förbättrar frågeprestanda finns i Metodtips för Kusto-frågor.

Förstå CPU-resurskvoter

Beroende på dess storlek har varje klientorganisation åtkomst till en angiven mängd cpu-resurser som allokerats för att köra avancerade jaktfrågor. Detaljerad information om olika användningsparametrar finns i avancerade jaktkvoter och användningsparametrar.

När du har kört frågan kan du se körningstiden och dess resursanvändning (låg, medel, hög). Hög indikerar att frågan tog fler resurser att köra och kunde förbättras för att returnera resultat mer effektivt.

Frågeinformationen under fliken **Resultat** i Microsoft Defender-portalen

Kunder som kör flera frågor regelbundet bör spåra förbrukning och använda optimeringsvägledningen i den här artikeln för att minimera störningar till följd av att kvoter eller användningsparametrar överskrids.

Titta på Optimera KQL-frågor för att se några av de vanligaste sätten att förbättra dina frågor.

Allmänna optimeringstips

  • Ändra storlek på nya frågor – Om du misstänker att en fråga returnerar en stor resultatuppsättning utvärderar du den först med hjälp av count-operatorn. Använd gräns eller dess synonym take för att undvika stora resultatuppsättningar.

  • Använd filter tidigt – Använd tidsfilter och andra filter för att minska datamängden, särskilt innan du använder transformerings- och parsningsfunktioner, till exempel substring(), replace(), trim(), toupper()eller parse_json(). I exemplet nedan används parsningsfunktionen extractjson() efter att filtreringsoperatorer har minskat antalet poster.

    DeviceEvents
    | where Timestamp > ago(1d)
    | where ActionType == "UsbDriveMount"
    | where DeviceName == "user-desktop.domain.com"
    | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
    
  • Has beats contains – Om du vill undvika att söka i delsträngar i ord i onödan använder du operatorn has i stället för contains. Läs mer om strängoperatorer

  • Titta i specifika kolumner – Titta i en specifik kolumn i stället för att köra fulltextsökningar i alla kolumner. Använd * inte för att kontrollera alla kolumner.

  • Skiftlägeskänslig för hastighet – Skiftlägeskänsliga sökningar är mer specifika och generellt mer högpresterande. Namn på skiftlägeskänsliga strängoperatorer, till exempel has_cs och contains_cs, slutar vanligtvis med _cs. Du kan också använda operatorn == skiftlägeskänsliga lika med i stället för =~.

  • Parsa, extrahera inte – Använd parsningsoperatorn eller en parsningsfunktion som parse_json()när det är möjligt. matches regex Undvik strängoperatorn eller funktionen extract(), som båda använder reguljära uttryck. Reservera användningen av reguljära uttryck för mer komplexa scenarier. Läs mer om att parsa funktioner

  • Filtrera tabeller, inte uttryck – Filtrera inte på en beräknad kolumn om du kan filtrera på en tabellkolumn.

  • Inga termer med tre tecken – Undvik att jämföra eller filtrera med termer med tre tecken eller färre. Dessa termer indexeras inte och för att matcha dem krävs fler resurser.

  • Projekt selektivt – Gör dina resultat enklare att förstå genom att bara projicera de kolumner du behöver. Genom att projicera specifika kolumner innan du kör koppling eller liknande åtgärder kan du också förbättra prestandan.

Optimera operatorn join

Kopplingsoperatorn sammanfogar rader från två tabeller genom att matcha värden i angivna kolumner. Använd de här tipsen för att optimera frågor som använder den här operatorn.

  • Mindre tabell till vänster – Operatorn join matchar poster i tabellen till vänster om kopplingsinstrukmentet till poster till höger. Genom att ha den mindre tabellen till vänster måste färre poster matchas, vilket påskyndar frågan.

    I tabellen nedan minskar vi den vänstra tabellen DeviceLogonEvents så att den endast täcker tre specifika enheter innan vi ansluter den med IdentityLogonEvents konto-SID.

    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
    
  • Använd den inre kopplingssmakenStandardkopplingssmaken eller innerunique-join deduplicerar rader i den vänstra tabellen med kopplingsnyckeln innan du returnerar en rad för varje matchning till den högra tabellen. Om den vänstra tabellen har flera rader med samma värde för join nyckeln dedupliceras dessa rader för att lämna en enda slumpmässig rad för varje unikt värde.

    Det här standardbeteendet kan utelämna viktig information från den vänstra tabellen som kan ge användbara insikter. Frågan nedan visar till exempel bara ett e-postmeddelande som innehåller en viss bifogad fil, även om samma bifogade fil har skickats med flera e-postmeddelanden:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    

    För att åtgärda den här begränsningen använder vi den inre kopplingssmaken genom att ange kind=inner för att visa alla rader i den vänstra tabellen med matchande värden till höger:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Koppla poster från ett tidsfönster – När du undersöker säkerhetshändelser letar analytiker efter relaterade händelser som inträffar ungefär samma tidsperiod. Om du tillämpar samma metod när du använder join den kan du också förbättra prestandan genom att minska antalet poster som ska kontrolleras.

    Frågan nedan söker efter inloggningshändelser inom 30 minuter efter att en skadlig fil har tagits emot:

    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)
    
  • Använd tidsfilter på båda sidor – Även om du inte undersöker ett visst tidsfönster kan användning av tidsfilter på både vänster och höger tabeller minska antalet poster för att kontrollera och förbättra join prestanda. Frågan nedan gäller Timestamp > ago(1h) för båda tabellerna så att den endast kopplar poster från den senaste timmen:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Använd tips för prestanda – Använd tips med operatorn join för att instruera serverdelen att distribuera belastningen när du kör resursintensiva åtgärder. Läs mer om kopplingstips

    Shuffle-tipset hjälper till exempel till att förbättra frågeprestanda vid koppling av tabeller med en nyckel med hög kardinalitet – en nyckel med många unika värden – till AccountObjectId exempel i frågan nedan:

    IdentityInfo
    | where JobTitle == "CONSULTANT"
    | join hint.shufflekey = AccountObjectId
    (IdentityDirectoryEvents
        | where Application == "Active Directory"
        | where ActionType == "Private data retrieval")
    on AccountObjectId
    

    Sändningstipset hjälper när den vänstra tabellen är liten (upp till 100 000 poster) och den högra tabellen är extremt stor. Frågan nedan försöker till exempel koppla några e-postmeddelanden som har specifika ämnen med alla meddelanden som innehåller länkar i EmailUrlInfo tabellen:

    EmailEvents
    | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now")
    | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
    

Optimera operatorn summarize

Sammanfattningsoperatorn aggregerar innehållet i en tabell. Använd de här tipsen för att optimera frågor som använder den här operatorn.

  • Hitta distinkta värden – I allmänhet kan du använda summarize för att hitta distinkta värden som kan vara repetitiva. Det kan vara onödigt att använda det för att aggregera kolumner som inte har repetitiva värden.

    Även om ett enskilt e-postmeddelande kan ingå i flera händelser är exemplet nedan inte en effektiv användning av summarize eftersom ett nätverksmeddelande-ID för ett enskilt e-postmeddelande alltid har en unik avsändaradress.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by NetworkMessageId, SenderFromAddress
    

    Operatorn summarize kan enkelt ersättas med project, vilket potentiellt ger samma resultat samtidigt som färre resurser förbrukas:

    EmailEvents
    | where Timestamp > ago(1h)
    | project NetworkMessageId, SenderFromAddress
    

    Följande exempel är en effektivare användning av summarize eftersom det kan finnas flera distinkta instanser av en avsändaradress som skickar e-post till samma mottagaradress. Sådana kombinationer är mindre distinkta och har sannolikt dubbletter.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by SenderFromAddress, RecipientEmailAddress
    
  • Blanda frågan – Även om summarize används bäst i kolumner med repetitiva värden kan samma kolumner också ha hög kardinalitet eller ett stort antal unika värden. Precis som operatorn join kan du även använda shuffle-tipset med summarize för att distribuera bearbetningsbelastningen och eventuellt förbättra prestanda vid användning på kolumner med hög kardinalitet.

    Frågan nedan använder summarize för att räkna distinkta mottagares e-postadress, som kan köras i hundratusentals i stora organisationer. För att förbättra prestandan hint.shufflekeyinnehåller den :

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
    

Frågescenarier

Identifiera unika processer med process-ID:t

Process-ID:n (PID) återanvänds i Windows och återanvänds för nya processer. På egen hand kan de inte fungera som unika identifierare för specifika processer.

Om du vill hämta en unik identifierare för en process på en specifik dator använder du process-ID:t tillsammans med processens skapandetid. När du kopplar eller sammanfattar data runt processer inkluderar du kolumner för datoridentifieraren (antingen DeviceId eller DeviceName), process-ID :t (ProcessId eller InitiatingProcessId) och processens skapandetid (ProcessCreationTime eller InitiatingProcessCreationTime)

I följande exempelfråga hittas processer som har åtkomst till fler än 10 IP-adresser via port 445 (SMB), eventuellt genomsökning efter filresurser.

Exempelfråga:

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

Frågan sammanfattas av båda InitiatingProcessId och InitiatingProcessCreationTime så att den tittar på en enda process, utan att blanda flera processer med samma process-ID.

Köra frågor mot kommandorader

Det finns många sätt att skapa en kommandorad för att utföra en uppgift. En angripare kan till exempel referera till en bildfil utan sökväg, utan filnamnstillägg, med hjälp av miljövariabler eller med citattecken. Angriparen kan också ändra ordningen på parametrar eller lägga till flera citattecken och blanksteg.

Använd följande metoder för att skapa mer hållbara frågor runt kommandorader:

  • Identifiera de kända processerna (till exempel net.exe eller psexec.exe) genom att matcha i filnamnsfälten i stället för att filtrera på själva kommandoraden.
  • Parsa kommandoradsavsnitt med hjälp av funktionen parse_command_line()
  • När du frågar efter kommandoradsargument ska du inte leta efter en exakt matchning på flera orelaterade argument i en viss ordning. Använd i stället reguljära uttryck eller använd flera separata contains-operatorer.
  • Användningsfallsokänsliga matchningar. Använd till exempel =~, , och contains i stället för ==, inoch contains_csin~.
  • Om du vill undvika tekniker för att dölja kommandoraden bör du överväga att ta bort citattecken, ersätta kommatecken med blanksteg och ersätta flera på varandra följande blanksteg med ett enda blanksteg. Det finns mer komplexa fördunklingstekniker som kräver andra metoder, men de här justeringarna kan hjälpa dig att hantera vanliga metoder.

I följande exempel visas olika sätt att konstruera en fråga som söker efter filen net.exe för att stoppa brandväggstjänsten "MpsSvc":

// 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"

Mata in data från externa källor

Om du vill införliva långa listor eller stora tabeller i frågan använder du operatorn externaldata för att mata in data från en angiven URI. Du kan hämta data från filer i TXT, CSV, JSON eller andra format. Exemplet nedan visar hur du kan använda den omfattande listan över SHA-256-hashvärden för skadlig kod som tillhandahålls av MalwareBazaar (abuse.ch) för att kontrollera bifogade filer i e-postmeddelanden:

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

Parsa strängar

Det finns olika funktioner som du kan använda för att effektivt hantera strängar som behöver parsas eller konverteras.

Sträng Funktion Användningsexempel
Kommandorader parse_command_line() Extrahera kommandot och alla argument.
Sökvägar parse_path() Extrahera avsnitten i en fil- eller mappsökväg.
Versionsnummer parse_version() Dekonstruera ett versionsnummer med upp till fyra avsnitt och upp till åtta tecken per avsnitt. Använd tolkade data för att jämföra versionsålder.
IPv4-adresser parse_ipv4() Konvertera en IPv4-adress till ett långt heltal. Om du vill jämföra IPv4-adresser utan att konvertera dem använder du ipv4_compare().
IPv6-adresser parse_ipv6() Konvertera en IPv4- eller IPv6-adress till den kanoniska IPv6-notationen. Om du vill jämföra IPv6-adresser använder du ipv6_compare().

Mer information om alla parsningsfunktioner som stöds finns i Kusto-strängfunktioner.

Obs!

Vissa tabeller i den här artikeln kanske inte är tillgängliga i Microsoft Defender för Endpoint. Aktivera Microsoft Defender XDR för att söka efter hot med hjälp av fler datakällor. Du kan flytta dina avancerade jaktarbetsflöden från Microsoft Defender för Endpoint till Microsoft Defender XDR genom att följa stegen i Migrera avancerade jaktfrågor från Microsoft Defender för Endpoint.

Tips

Vill du veta mer? Engage med Microsofts säkerhetscommunity i vår Tech Community: Microsoft Defender XDR Tech Community.