Avancerede bedste praksisser for jagtforespørgslen
Gælder for:
- Microsoft Defender XDR
Anvend disse anbefalinger for at opnå resultater hurtigere og undgå timeout under kørsel af komplekse forespørgsler. Hvis du vil have mere hjælp til at forbedre ydeevnen af forespørgsler, skal du læse Bedste praksis for Kusto-forespørgsler.
Forstå kvoter for CPU-ressourcer
Afhængigt af størrelsen har hver lejer adgang til en bestemt mængde CPU-ressourcer, der er allokeret til kørsel af avancerede jagtforespørgsler. Du kan finde detaljerede oplysninger om forskellige forbrugsparametre ved at læse om avancerede jagtkvoter og forbrugsparametre.
Når du har kørt din forespørgsel, kan du se udførelsestiden og dens ressourceforbrug (Lav, Mellem, Høj). High angiver, at forespørgslen tog flere ressourcer at køre og kunne forbedres for at returnere resultater mere effektivt.
Kunder, der kører flere forespørgsler regelmæssigt, bør spore forbruget og anvende optimeringsvejledningen i denne artikel for at minimere afbrydelse som følge af overskridelse af kvoter eller forbrugsparametre.
Se Optimering af KQL-forespørgsler for at se nogle af de mest almindelige måder at forbedre dine forespørgsler på.
Generelle optimeringstip
Tilpas størrelsen på nye forespørgsler – Hvis du har mistanke om, at en forespørgsel returnerer et stort resultatsæt, skal du først vurdere det ved hjælp af optællingsoperatoren. Brug grænse eller synonymet
take
for at undgå store resultatsæt.Anvend filtre tidligt – Anvend tidsfiltre og andre filtre for at reducere datasættet, især før du bruger transformations- og fortolkningsfunktioner, f.eks . understreng(), erstat(), trim(), toupper()eller parse_json(). I eksemplet nedenfor bruges fortolkningsfunktionen extractjson(), når filtreringsoperatorer har reduceret antallet af poster.
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "UsbDriveMount" | where DeviceName == "user-desktop.domain.com" | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
Har beats indeholder– Hvis du vil undgå unødvendigt at søge efter understrenge i ord, skal du bruge operatoren
has
i stedet forcontains
. Få mere at vide om strengoperatorerSøg i bestemte kolonner – Søg i en bestemt kolonne i stedet for at køre fuldtekstsøgninger på tværs af alle kolonner. Brug ikke
*
til at kontrollere alle kolonner.Forskel på store og små bogstaver i forbindelse med hastighed – Søgninger med forskel på store og små bogstaver er mere specifikke og generelt mere udførligt. Navne på strengoperatorer med forskel på store og små bogstaver, f.eks
has_cs
. ogcontains_cs
, slutter normalt med_cs
. Du kan også bruge operatoren==
equals i stedet for=~
.Opspar, udtræk ikke – Når det er muligt, skal du bruge fortolkningsoperatoren eller en fortolkningsfunktion, f.eks. parse_json().
matches regex
Undgå strengoperatoren eller funktionen extract(), som begge bruger regulært udtryk. Forbeholder dig brugen af regulære udtryk til mere komplekse scenarier. Læs mere om fortolkningsfunktionerFiltrer ikke tabeller – Filtrer ikke efter en beregnet kolonne, hvis du kan filtrere på en tabelkolonne.
Ingen ord med tre tegn – undgå at sammenligne eller filtrere ved hjælp af ord med tre tegn eller færre. Disse ord er ikke indekseret, og matchning af dem kræver flere ressourcer.
Projekt selektivt – Gør dine resultater nemmere at forstå ved kun at projektere de kolonner, du har brug for. Projektering af bestemte kolonner, før der køres joinforbindelser eller lignende handlinger, hjælper også med at forbedre ydeevnen.
Optimer operatoren join
Joinoperatoren fletter rækker fra to tabeller ved at matche værdier i angivne kolonner. Anvend disse tip til at optimere forespørgsler, der bruger denne operator.
Mindre tabel til venstre – operatoren
join
matcher poster i tabellen i venstre side af joinsætningen med poster til højre. Når den mindre tabel er til venstre, skal der matches færre poster, så forespørgslen bliver hurtigere.I nedenstående tabel reducerer vi den venstre tabel
DeviceLogonEvents
, så den kun dækker tre specifikke enheder, før den forbindes medIdentityLogonEvents
konto-SID'er.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
Brug den indre joinforbindelsessmag – joinforbindelsens standardsmag eller den innerunique-joinforbindelse deduplikerer rækker i den venstre tabel med joinnøglen, før der returneres en række for hver forekomst af den højre tabel. Hvis den venstre tabel indeholder flere rækker med den samme værdi for
join
nøglen, deduplikeres disse rækker for at efterlade en enkelt tilfældig række for hver entydige værdi.Denne standardfunktionsmåde kan udelade vigtige oplysninger fra den venstre tabel, som kan give nyttig indsigt. Forespørgslen nedenfor viser f.eks. kun én mail, der indeholder en bestemt vedhæftet fil, selvom den samme vedhæftede fil blev sendt ved hjælp af flere mails:
EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
For at løse denne begrænsning anvender vi den indre joinforbindelsessmag ved at angive
kind=inner
, at alle rækker i tabellen til venstre skal vises med tilsvarende værdier til højre:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Joinforbind poster fra et tidsvindue – Når analytikere undersøger sikkerhedshændelser, søger de efter relaterede hændelser, der forekommer omkring samme tidsperiode. Anvendelse af den samme tilgang, når du bruger
join
, gavner også ydeevnen ved at reducere antallet af poster, der skal kontrolleres.Forespørgslen nedenfor kontrollerer, om der er logonhændelser inden for 30 minutter efter modtagelse af en skadelig fil:
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)
Anvend tidsfiltre på begge sider – Selvom du ikke undersøger et bestemt tidsvindue, kan anvendelse af tidsfiltre på både venstre og højre tabeller reducere antallet af poster for at kontrollere og forbedre
join
ydeevnen. Forespørgslen nedenfor gælder forTimestamp > ago(1h)
begge tabeller, så den kun joinforbinder poster fra den seneste time:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Brug tip til ydeevne – Brug tip med operatoren
join
til at instruere backend til at distribuere belastningen, når der køres ressourcetunge handlinger. Få mere at vide om jointipShuffle-tip hjælper f.eks. med at forbedre forespørgslens ydeevne, når tabeller forbindes ved hjælp af en nøgle med høj kardinalitet – en nøgle med mange entydige værdier – f.eks
AccountObjectId
. i forespørgslen nedenfor:IdentityInfo | where JobTitle == "CONSULTANT" | join hint.shufflekey = AccountObjectId (IdentityDirectoryEvents | where Application == "Active Directory" | where ActionType == "Private data retrieval") on AccountObjectId
Udsendelsestip hjælper, når den venstre tabel er lille (op til 100.000 poster), og den højre tabel er meget stor. Forespørgslen nedenfor forsøger f.eks. at joinforbinde et par mails, der har specifikke emner med alle meddelelser, der indeholder links i tabellen
EmailUrlInfo
:EmailEvents | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now") | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
Optimer operatoren summarize
Opsummeringsoperatoren samler indholdet af en tabel. Anvend disse tip til at optimere forespørgsler, der bruger denne operator.
Find entydige værdier – Generelt kan du bruge
summarize
til at finde entydige værdier, der kan gentages. Det kan være unødvendigt at bruge det til at aggregere kolonner, der ikke har gentagne værdier.Selvom en enkelt mail kan være en del af flere hændelser, er eksemplet nedenfor ikke en effektiv brug af
summarize
, fordi et netværksmeddelelses-id for en individuel mail altid leveres med en entydig afsenderadresse.EmailEvents | where Timestamp > ago(1h) | summarize by NetworkMessageId, SenderFromAddress
Operatoren
summarize
kan nemt erstattes med , der potentielt giver de samme resultater, samtidig medproject
at der bruges færre ressourcer:EmailEvents | where Timestamp > ago(1h) | project NetworkMessageId, SenderFromAddress
Følgende eksempel er en mere effektiv brug af
summarize
, fordi der kan være flere forskellige forekomster af en afsenderadresse, der sender mail til den samme modtageradresse. Disse kombinationer er mindre entydige og vil sandsynligvis have dubletter.EmailEvents | where Timestamp > ago(1h) | summarize by SenderFromAddress, RecipientEmailAddress
Bland forespørgslen – Selvom
summarize
den bedst bruges i kolonner med gentagne værdier, kan de samme kolonner også have høj kardinalitet eller et stort antal entydige værdier. Ligesom operatorenjoin
kan du også anvende shuffle-tip tilsummarize
at distribuere behandlingsbelastningen og muligvis forbedre ydeevnen, når du arbejder på kolonner med høj kardinalitet.Forespørgslen nedenfor bruger
summarize
til at tælle særskilt modtagermailadresse, som kan køre i hundredtusindvis i store organisationer. For at forbedre ydeevnen omfatterhint.shufflekey
den :EmailEvents | where Timestamp > ago(1h) | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
Forespørgselsscenarier
Identificer entydige processer med proces-id'er
Proces-id'er (PID'er) genbruges i Windows og genbruges til nye processer. De kan ikke alene fungere som entydige identifikatorer for bestemte processer.
Hvis du vil hente et entydigt id for en proces på en bestemt computer, skal du bruge proces-id'et sammen med processens oprettelsestid. Når du joinforbinder eller opsummerer data om processer, skal du inkludere kolonner for computer-id'et (enten DeviceId
eller DeviceName
), proces-id'et (ProcessId
eller InitiatingProcessId
) og tidspunktet for oprettelse af processen (ProcessCreationTime
eller InitiatingProcessCreationTime
)
Følgende eksempelforespørgsel finder processer, der har adgang til mere end 10 IP-adresser via port 445 (SMB), og som muligvis søger efter filshares.
Eksempelforespørgsel:
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
Forespørgslen opsummeres af begge InitiatingProcessId
, så InitiatingProcessCreationTime
den ser på en enkelt proces uden at blande flere processer med det samme proces-id.
Forespørgselskommandolinjer
Der er mange måder at oprette en kommandolinje på for at udføre en opgave. En hacker kan f.eks. referere til en billedfil uden en sti, uden et filtypenavn, ved hjælp af miljøvariabler eller med anførselstegn. Hackeren kan også ændre rækkefølgen af parametre eller tilføje flere anførselstegn og mellemrum.
Hvis du vil oprette mere holdbare forespørgsler omkring kommandolinjer, skal du anvende følgende fremgangsmåder:
- Identificer de kendte processer (f.eks .net.exe eller psexec.exe) ved at matche felterne med filnavne i stedet for at filtrere på selve kommandolinjen.
- Fortolk kommandolinjesektioner ved hjælp af funktionen parse_command_line()
- Når du forespørger efter kommandolinjeargumenter, skal du ikke søge efter et nøjagtigt match på flere ikke-relaterede argumenter i en bestemt rækkefølge. Brug i stedet regulære udtryk, eller brug flere separate indeholder operatorer.
- Brug match, hvor der ikke skelnes mellem store og små bogstaver. Brug f.eks.
=~
,in~
ogcontains
i stedet for==
,in
ogcontains_cs
. - Hvis du vil afhjælpe teknikker til tilsløring af kommandolinjer, bør du overveje at fjerne anførselstegn, erstatte kommaer med mellemrum og erstatte flere efter hinanden følgende mellemrum med et enkelt mellemrum. Der er mere komplekse tilsløringsteknikker, der kræver andre metoder, men disse justeringer kan hjælpe med at løse almindelige.
Følgende eksempler viser forskellige måder at konstruere en forespørgsel, der søger efter filen net.exe til at stoppe firewalltjenesten "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"
Indfødning af data fra eksterne kilder
Hvis du vil inkorporere lange lister eller store tabeller i din forespørgsel, skal du bruge operatoren externaldata til at indtage data fra en angivet URI. Du kan hente data fra filer i TXT-, CSV-, JSON- eller andre formater. Nedenstående eksempel viser, hvordan du kan bruge den omfattende liste over malware SHA-256-hashen leveret af MalwareBazaar (abuse.ch) til at kontrollere vedhæftede filer på mails:
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
Fortolkningsstrenge
Der er forskellige funktioner, som du kan bruge til effektivt at håndtere strenge, der skal analyseres eller konverteres.
String | Funktion | Eksempel på anvendelse |
---|---|---|
Kommandolinjer | parse_command_line() | Udpak kommandoen og alle argumenter. |
Stier | parse_path() | Udpak sektionerne i en fil- eller mappesti. |
Versionsnumre | parse_version() | Dekonstruer et versionsnummer med op til fire sektioner og op til otte tegn pr. sektion. Brug de analyserede data til at sammenligne versionens alder. |
IPv4-adresser | parse_ipv4() | Konvertér en IPv4-adresse til et langt heltal. Hvis du vil sammenligne IPv4-adresser uden at konvertere dem, skal du bruge ipv4_compare(). |
IPv6-adresser | parse_ipv6() | Konvertér en IPv4- eller IPv6-adresse til den kanoniske IPv6-notation. Hvis du vil sammenligne IPv6-adresser, skal du bruge ipv6_compare(). |
Hvis du vil vide mere om alle understøttede fortolkningsfunktioner, kan du læse om Kusto-strengfunktioner.
Bemærk!
Nogle tabeller i denne artikel er muligvis ikke tilgængelige i Microsoft Defender for Endpoint. Slå Microsoft Defender XDR til for at jage efter trusler ved hjælp af flere datakilder. Du kan flytte dine avancerede arbejdsprocesser for jagt fra Microsoft Defender for Endpoint til Microsoft Defender XDR ved at følge trinnene i Overfør avancerede jagtforespørgsler fra Microsoft Defender for Endpoint.
Relaterede emner
- Dokumentation til Kusto-forespørgselssprog
- Kvoter og brugsparametre
- Håndter avancerede jagtfejl
- Oversigt over avanceret jagt
- Få mere at vide om forespørgselssproget
Tip
Vil du vide mere? Kontakt Microsoft Security-community'et i vores Tech Community: Microsoft Defender XDR Tech Community.