Osvědčené postupy pro pokročilé proaktivní dotazy
Platí pro:
- Microsoft Defender XDR
Pomocí těchto doporučení můžete rychleji získat výsledky a vyhnout se vypršení časových limitů při spouštění složitých dotazů. Další pokyny ke zvýšení výkonu dotazů najdete v tématu Osvědčené postupy pro dotazy Kusto.
Vysvětlení kvót prostředků procesoru
V závislosti na velikosti má každý tenant přístup k nastavenému množství prostředků procesoru přidělených pro spouštění pokročilých dotazů proaktivního vyhledávání. Podrobné informace o různých parametrech použití najdete v článku o rozšířených kvótách proaktivního vyhledávání a parametrech využití.
Po spuštění dotazu uvidíte dobu provádění a využití prostředků (nízká, střední, vysoká). Vysoká znamená, že spuštění dotazu trvalo více prostředků a bylo možné ho vylepšit, aby se výsledky vracely efektivněji.
Zákazníci, kteří pravidelně spouštějí více dotazů, by měli sledovat spotřebu a použít pokyny k optimalizaci v tomto článku, aby se minimalizovalo přerušení způsobené překročením kvót nebo parametrů využití.
Podívejte se na optimalizaci dotazů KQL a podívejte se na některé z nejběžnějších způsobů, jak vaše dotazy vylepšit.
Obecné tipy pro optimalizaci
Velikost nových dotazů – Pokud máte podezření, že dotaz vrátí velkou sadu výsledků, nejprve ji vyhodnoťte pomocí operátoru count. Pokud se chcete vyhnout velkým sadám výsledků, použijte limit nebo jeho synonymum
take
.Použít filtry v rané fázi – Použijte filtry času a další filtry ke zmenšení datové sady, zejména před použitím transformačních a parsačních funkcí, jako je substring(), replace(), trim(), toupper() nebo parse_json(). V následujícím příkladu se funkce analýzy extractjson() používá poté, co operátory filtrování snížily počet záznamů.
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "UsbDriveMount" | where DeviceName == "user-desktop.domain.com" | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
Obsahuje beats contains – Pokud se chcete vyhnout zbytečnému vyhledávání podřetězců ve slovech, použijte
has
operátor místocontains
. Informace o řetězcových operátorechHledání v konkrétních sloupcích – Místo fulltextového vyhledávání ve všech sloupcích hledejte v konkrétním sloupci. Nepoužívejte
*
ke kontrole všech sloupců.Pro rychlost se rozlišují velká a malá písmena – hledání je konkrétnější a obecně výkonnější. Názvy řetězcových operátorů, které rozlišují malá a velká písmena, například
has_cs
acontains_cs
, obvykle končí na_cs
. Můžete také použít operátor==
rovná se rozlišovat malá a velká písmena místo operátoru=~
.Parsovat, neextrahovat – Kdykoli je to možné, použijte operátor parsování nebo funkci parsování, jako je parse_json(). Vyhněte se řetězcový
matches regex
operátor nebo funkci extract(), které používají regulární výraz. Vyhraďte si použití regulárního výrazu pro složitější scénáře. Další informace o parsování funkcíFiltrovat tabulky, ne výrazy – Pokud můžete filtrovat podle sloupce tabulky, nefiltrujte počítaný sloupec.
Žádné termíny se třemi znaky – nepoužívejte porovnávání nebo filtrování pomocí termínů se třemi nebo méně znaky. Tyto termíny nejsou indexované a jejich shoda bude vyžadovat více prostředků.
Selektivně promítejte – výsledky můžete snadněji pochopit tím, že promítnete jenom sloupce, které potřebujete. Zvýšení výkonu pomáhá také promítání konkrétních sloupců před spuštěním operací spojení nebo podobných operací.
Optimalizace operátoru join
Operátor join sloučí řádky ze dvou tabulek tak, že porovnává hodnoty v zadaných sloupcích. Tyto tipy použijte k optimalizaci dotazů, které používají tento operátor.
Menší tabulka nalevo –
join
Operátor porovná záznamy v tabulce na levé straně příkazu join se záznamy na pravé straně. Když budete mít menší tabulku na levé straně, bude potřeba spárovat méně záznamů, což urychlí dotaz.V následující tabulce zmenšujeme levou tabulku
DeviceLogonEvents
tak, aby zahrnovala jenom tři konkrétní zařízení, než se k níIdentityLogonEvents
připojíte pomocí identifikátorů SID účtu.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
Použijte příchuť inner-join – Výchozí příchuť spojení nebo innerunique-join deduplikuje řádky v levé tabulce pomocí klávesy join před vrácením řádku pro každou shodu do pravé tabulky. Pokud levá tabulka obsahuje více řádků se stejnou hodnotou klíče
join
, budou tyto řádky odstraněny z duplicit, aby zůstal pro každou jedinečnou hodnotu jeden náhodný řádek.Toto výchozí chování může vynechat důležité informace z levé tabulky, které můžou poskytnout užitečný přehled. Například následující dotaz zobrazí jenom jeden e-mail obsahující konkrétní přílohu, i když byla stejná příloha odeslána pomocí více e-mailových zpráv:
EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Abychom toto omezení vyřešili, použijeme příchuť vnitřního spojení tak, že zadáme
kind=inner
, aby se vpravo zobrazily všechny řádky v levé tabulce s odpovídajícími hodnotami:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Spojit záznamy z časového intervalu – Při zkoumání událostí zabezpečení analytici hledají související události, ke kterým dochází přibližně ve stejném časovém období. Použití stejného přístupu při použití
join
také přináší výkon, protože snižuje počet záznamů ke kontrole.Následující dotaz zkontroluje události přihlášení do 30 minut od přijetí škodlivého souboru:
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)
Použijte filtry času na obou stranách– I když nezkoumáte konkrétní časové okno, použití filtrů času v levé i pravé tabulce může snížit počet záznamů, aby se kontrolovala a zlepšila
join
výkon. Následující dotaz se vztahujeTimestamp > ago(1h)
na obě tabulky, aby spojil pouze záznamy z poslední hodiny:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Použití nápovědy k výkonu – Pomocí tipů s operátorem
join
řizte back-endu, aby při spouštění operací náročných na prostředky distribuuje zatížení. Další informace o tipech pro připojeníNápověda pro náhodné prohazování například pomáhá zlepšit výkon dotazů při spojování tabulek pomocí klíče s vysokou kardinalitou – klíče s mnoha jedinečnými hodnotami – například
AccountObjectId
v následujícím dotazu:IdentityInfo | where JobTitle == "CONSULTANT" | join hint.shufflekey = AccountObjectId (IdentityDirectoryEvents | where Application == "Active Directory" | where ActionType == "Private data retrieval") on AccountObjectId
Nápověda pro vysílání pomáhá, když je levá tabulka malá (až 100 000 záznamů) a pravá tabulka je extrémně velká. Následující dotaz se například pokouší spojit několik e-mailů s konkrétními předměty se všemi zprávami obsahujícími odkazy v
EmailUrlInfo
tabulce:EmailEvents | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now") | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
Optimalizace operátoru summarize
Operátor summarize agreguje obsah tabulky. Tyto tipy použijte k optimalizaci dotazů, které používají tento operátor.
Hledání jedinečných hodnot – obecně se používá
summarize
k vyhledání jedinečných hodnot, které se můžou opakovat. Může být zbytečné ji používat k agregaci sloupců, které nemají opakující se hodnoty.I když může být jeden e-mail součástí více událostí, níže uvedený příklad není efektivní,
summarize
protože ID síťové zprávy pro jednotlivé e-maily vždy obsahuje jedinečnou adresu odesílatele.EmailEvents | where Timestamp > ago(1h) | summarize by NetworkMessageId, SenderFromAddress
Operátor
summarize
lze snadno nahradit operátoremproject
, který může přinést stejné výsledky při spotřebě méně prostředků:EmailEvents | where Timestamp > ago(1h) | project NetworkMessageId, SenderFromAddress
Následující příklad je efektivnější,
summarize
protože může existovat více různých instancí adresy odesílatele, které odesílají e-mail na stejnou adresu příjemce. Tyto kombinace jsou méně odlišné a pravděpodobně budou mít duplikáty.EmailEvents | where Timestamp > ago(1h) | summarize by SenderFromAddress, RecipientEmailAddress
Náhodné prohazování dotazu – I když
summarize
se nejlépe používá ve sloupcích s opakovanými hodnotami, stejné sloupce můžou mít také vysokou kardinalitu nebo velký počet jedinečných hodnot. Podobně jako u operátorujoin
můžete také použít nápovědusummarize
pro náhodné prohazování a distribuovat zatížení zpracování a potenciálně zlepšit výkon při provozu se sloupci s vysokou kardinalitou.Následující dotaz používá
summarize
k určení počtu jedinečných e-mailových adres příjemců, které se můžou spouštět ve stovkách tisíc ve velkých organizacích. Pro zvýšení výkonuhint.shufflekey
zahrnuje :EmailEvents | where Timestamp > ago(1h) | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
Scénáře dotazů
Identifikace jedinečných procesů pomocí ID procesů
ID procesů (PID) se recyklují ve Windows a znovu se používají pro nové procesy. Samy o sobě nemůžou sloužit jako jedinečné identifikátory pro konkrétní procesy.
Pokud chcete získat jedinečný identifikátor procesu na konkrétním počítači, použijte ID procesu společně s časem vytvoření procesu. Při spojování nebo sumarizace dat kolem procesů zahrňte sloupce pro identifikátor počítače (buď DeviceId
nebo DeviceName
), ID procesu (ProcessId
nebo InitiatingProcessId
) a čas vytvoření procesu (ProcessCreationTime
nebo InitiatingProcessCreationTime
).
Následující příklad dotazu najde procesy, které přistupuje k více než 10 IP adresám přes port 445 (SMB), a pravděpodobně vyhledá sdílené složky.
Příklad dotazu:
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
Dotaz shrnuje hodnoty a InitiatingProcessId
InitiatingProcessCreationTime
tak, že se dívá na jeden proces, aniž by se směšil několik procesů se stejným ID procesu.
Příkazové řádky dotazu
Existuje mnoho způsobů, jak vytvořit příkazový řádek k provedení úkolu. Útočník by například mohl odkazovat na soubor obrázku bez cesty, bez přípony souboru, pomocí proměnných prostředí nebo pomocí uvozovek. Útočník by také mohl změnit pořadí parametrů nebo přidat více uvozovek a mezer.
Pokud chcete vytvořit odolnější dotazy kolem příkazového řádku, použijte následující postupy:
- Identifikujte známé procesy (například net.exe nebo psexec.exe) tak, že v polích názvu souboru porovnáváte místo filtrování na samotném příkazovém řádku.
- Parsování oddílů příkazového řádku pomocí funkce parse_command_line()
- Při dotazování na argumenty příkazového řádku nehledat přesnou shodu u více nesouvisejících argumentů v určitém pořadí. Místo toho použijte regulární výrazy nebo více samostatných operátorů obsahuje.
- Použití nerozlišuje malá a velká písmena. Například místo
==
, a použijte=~
, acontains
contains_cs
.in~
in
- Pokud chcete zmírnit techniky obfuskace příkazového řádku, zvažte odebrání uvozovek, nahrazení čárky mezerami a nahrazení více po sobě jdoucích mezer jednou mezerou. Existují složitější techniky obfuskace, které vyžadují jiné přístupy, ale tyto vylepšení vám můžou pomoct vyřešit ty běžné.
Následující příklady ukazují různé způsoby vytvoření dotazu, který hledá soubor net.exe zastavit službu brány firewall 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"
Příjem dat z externích zdrojů
Pokud chcete do dotazu začlenit dlouhé seznamy nebo velké tabulky, použijte operátor externaldata k ingestování dat ze zadaného identifikátoru URI. Data můžete získat ze souborů ve formátu TXT, CSV, JSON nebo jiných formátech. Následující příklad ukazuje, jak můžete využít rozsáhlý seznam hodnot hash SHA-256 malwaru, které poskytuje MalwareBazaar (abuse.ch), ke kontrole příloh e-mailů:
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
Parsování řetězců
Existují různé funkce, které můžete použít k efektivnímu zpracování řetězců, které potřebují parsování nebo převod.
String | Funkce | Příklad použití |
---|---|---|
Příkazové řádky | parse_command_line() | Extrahujte příkaz a všechny argumenty. |
Stezky | parse_path() | Extrahujte oddíly cesty k souboru nebo složce. |
Čísla verzí | parse_version() | Dekonstrukce čísla verze s maximálně čtyřmi oddíly a až osmi znaky na oddíl. Analyzovaná data použijte k porovnání stáří verze. |
IPv4 adresy | parse_ipv4() | Převeďte IPv4 adresu na dlouhé celé číslo. Pokud chcete porovnat adresy IPv4 bez převodu, použijte ipv4_compare(). |
IPv6 adresy | parse_ipv6() | Převeďte adresu IPv4 nebo IPv6 na kanonický zápis IPv6. Pokud chcete porovnat adresy IPv6, použijte ipv6_compare(). |
Informace o všech podporovaných funkcích analýzy najdete v článku o funkcích řetězce Kusto.
Poznámka
Některé tabulky v tomto článku nemusí být v Microsoft Defender for Endpoint dostupné. Zapněte Microsoft Defender XDR pro vyhledávání hrozeb s využitím více zdrojů dat. Pokročilé pracovní postupy proaktivního vyhledávání můžete přesunout z Microsoft Defender for Endpoint do Microsoft Defender XDR pomocí kroků v tématu Migrace dotazů rozšířeného proaktivního vyhledávání z Microsoft Defender for Endpoint.
Související témata
- Dokumentace k dotazovacímu jazyku Kusto
- Kvóty a parametry využití
- Zpracování chyb rozšířeného proaktivního vyhledávání
- Přehled rozšířeného proaktivní vyhledávání
- Výuka jazyku dotazu
Tip
Chcete se dozvědět více? Spojte se s komunitou zabezpečení společnosti Microsoft v naší technické komunitě: Technická komunita Microsoft Defender XDR.