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.
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örcontains
. Läs mer om strängoperatorerTitta 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
ochcontains_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 funktionerFiltrera 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 medIdentityLogonEvents
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 kopplingssmaken – Standardkopplingssmaken 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ällerTimestamp > 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 kopplingstipsShuffle-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 medproject
, 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 operatornjoin
kan du även använda shuffle-tipset medsummarize
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 prestandanhint.shufflekey
innehå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
=~
, , ochcontains
i stället för==
,in
ochcontains_cs
in~
. - 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.
Relaterade ämnen
- Dokumentation om Kusto-frågespråk
- Kvoter och användningsparametrar
- Hantera avancerade jaktfel
- Översikt över avancerad jakt
- Lär dig frågespråket
Tips
Vill du veta mer? Interagera med Microsofts säkerhetscommunity i vår Tech Community: Microsoft Defender XDR Tech Community.