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.

Podrobnosti o dotazu na kartě **Výsledky** na portálu Microsoft Defender

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ísto contains. Informace o řetězcových operátorech

  • Hledá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 a contains_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 nalevojoin 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 vztahuje Timestamp > 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átorem project, 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átoru join můžete také použít nápovědu summarize 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ýkonu hint.shufflekeyzahrnuje :

    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 InitiatingProcessIdInitiatingProcessCreationTime 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 =~, a containscontains_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.
Cesty 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.

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.