Поделиться через


Рекомендации по использованию запросов расширенного выслеживания

Область применения:

  • Microsoft Defender XDR

Примените эти рекомендации, чтобы быстрее получать результаты и избежать превышения времени ожидания при выполнении сложных запросов. Дополнительные руководства по повышению производительности запросов см. в статье Рекомендации по использованию запросов Kusto.

Общие сведения о квотах ресурсов ЦП

В зависимости от размера каждый клиент имеет доступ к заданному объему ресурсов ЦП, выделенных для выполнения расширенных запросов охоты. Подробные сведения о различных параметрах использования см. в статье Дополнительные квоты охоты и параметры использования.

После выполнения запроса вы увидите время выполнения и использование ресурсов (низкий, средний, высокий). Высокий указывает, что для выполнения запроса потребовалось больше ресурсов, и его можно улучшить, чтобы более эффективно возвращать результаты.

Сведения о запросе на вкладке **Результаты** на портале Microsoft Defender

Клиенты, которые регулярно выполняют несколько запросов, должны отслеживать потребление и применять рекомендации по оптимизации, приведенные в этой статье, чтобы свести к минимуму сбои в результате превышения квот или параметров использования.

Ознакомьтесь с разделом Оптимизация запросов KQL , чтобы увидеть некоторые из наиболее распространенных способов улучшения запросов.

Общие советы по оптимизации

  • Размер новых запросов. Если вы подозреваете, что запрос вернет большой результирующий набор, сначала оцените его с помощью оператора count. Используйте ограничение или его синоним take , чтобы избежать больших результирующих наборов.

  • Раннее применение фильтров. Применяйте фильтры времени и другие фильтры для сокращения набора данных, особенно перед использованием функций преобразования и анализа, таких как substring(), replace(), trim(), toupper()или parse_json(). В приведенном ниже примере функция синтаксического анализа extractjson() используется после того, как операторы фильтрации сократили количество записей.

    DeviceEvents
    | where Timestamp > ago(1d)
    | where ActionType == "UsbDriveMount"
    | where DeviceName == "user-desktop.domain.com"
    | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
    
  • Содержит биты — чтобы не искать подстроки в словах без необходимости, используйте has оператор вместо contains. Сведения о строковых операторах

  • Поиск в определенных столбцах — поиск в определенном столбце вместо выполнения полнотекстового поиска по всем столбцам. Не используйте * для проверка всех столбцов.

  • С учетом регистра для скорости — поиск с учетом регистра более специфичный и, как правило, более производительный. Имена строковых операторов с учетом регистра, таких как has_cs и contains_cs, обычно заканчиваются на _cs. Кроме того, вместо можно использовать оператор == equals с учетом регистра =~.

  • Синтаксический анализ, не извлекайте. По возможности используйте оператор синтаксического анализа или функцию синтаксического анализа , например parse_json(). matches regex Избегайте оператора string или функции extract(), оба из которых используют регулярное выражение. Зарезервируйте использование регулярных выражений для более сложных сценариев. Дополнительные сведения о функциях синтаксического анализа

  • Фильтрация таблиц, а не выражений. Не фильтруйте по вычисляемого столбцу, если вы можете фильтровать по столбцу таблицы.

  • Нет трехсимвных терминов. Избегайте сравнения или фильтрации терминов с тремя символами или меньше. Эти термины не индексируются, и для их сопоставления потребуется больше ресурсов.

  • Выборочное проецирование— упростите результаты, проецируя только нужные столбцы. Проецирование определенных столбцов перед выполнением операций соединения или аналогичных операций также помогает повысить производительность.

join Оптимизация оператора

Оператор join объединяет строки из двух таблиц путем сопоставления значений в указанных столбцах. Примените эти советы для оптимизации запросов, использующих этот оператор.

  • Меньшая таблица слева. Оператор join сопоставляет записи в таблице слева от оператора join с записями справа. Если таблица меньшего размера слева, необходимо будет сопоставить меньше записей, что ускорит выполнение запроса.

    В таблице ниже мы уменьшаем левую таблицу DeviceLogonEvents , чтобы охватить только три конкретных устройства, прежде чем присоединить ее к IdentityLogonEvents идентификаторам безопасности учетной записи.

    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
    
  • Используйте вкус внутреннего соединения. Вариант соединения по умолчанию или внутреннее соединение дедупликирует строки в левой таблице по ключу соединения перед возвратом строки для каждого совпадения в правой таблице. Если левая таблица содержит несколько строк с одинаковым значением join для ключа, эти строки будут дедуплицированы, чтобы оставить одну случайную строку для каждого уникального значения.

    Это поведение по умолчанию может оставить важные сведения из левой таблицы, которые могут предоставить полезные аналитические сведения. Например, в приведенном ниже запросе будет отображаться только одно сообщение, содержащее определенное вложение, даже если это же вложение было отправлено с помощью нескольких сообщений электронной почты:

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

    Чтобы устранить это ограничение, мы применяем вкус внутреннего соединения , указав kind=inner , чтобы отобразить все строки в левой таблице с соответствующими значениями в правой части:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Объединение записей из временного окна. При изучении событий безопасности аналитики ищут связанные события, которые происходят примерно за тот же период времени. Применение того же подхода при использовании join также способствует повышению производительности, уменьшая количество записей до проверка.

    Приведенный ниже запрос проверяет наличие событий входа в систему в течение 30 минут после получения вредоносного файла:

    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)
    
  • Применяйте фильтры времени с обеих сторон. Даже если вы не изучаете определенный период времени, применение фильтров времени в левой и правой таблицах может сократить количество записей для проверка и повысить join производительность. Приведенный ниже запрос применяется Timestamp > ago(1h) к обеим таблицам, чтобы объединить только записи за последний час:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Используйте подсказки для повышения производительности. Используйте указания с оператором join , чтобы указать серверной части распределять нагрузку при выполнении ресурсоемких операций. Дополнительные сведения о указаниях соединения

    Например, указание перемешиваний помогает повысить производительность запросов при соединении таблиц с помощью ключа с высокой кратностью (ключа с множеством уникальных значений), например в приведенном ниже запросе AccountObjectId :

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

    Широковещательная подсказка помогает, когда левая таблица невелика (до 100 000 записей), а правая таблица очень велика. Например, приведенный ниже запрос пытается объединить несколько сообщений электронной почты с определенными темами со всеми сообщениями, содержащими ссылки в EmailUrlInfo таблице:

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

summarize Оптимизация оператора

Оператор суммирования объединяет содержимое таблицы. Примените эти советы для оптимизации запросов, использующих этот оператор.

  • Поиск отдельных значений — как правило, используется для summarize поиска уникальных значений, которые могут быть повторяющимися. Может быть ненужным использовать его для агрегирования столбцов, которые не имеют повторяющихся значений.

    Хотя одно сообщение электронной почты может быть частью нескольких событий, приведенный ниже пример не является эффективным, так как идентификатор сетевого summarize сообщения для отдельного сообщения всегда поставляется с уникальным адресом отправителя.

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

    Оператор summarize можно легко заменить projectна , получая потенциально те же результаты при использовании меньшего количества ресурсов:

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

    В следующем примере используется более эффективно, summarize так как может быть несколько отдельных экземпляров адреса отправителя, отправляющего сообщение электронной почты на один и тот же адрес получателя. Такие сочетания менее отличаются друг от друга и, скорее всего, имеют дубликаты.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by SenderFromAddress, RecipientEmailAddress
    
  • Перемешивает запрос. Хотя summarize лучше всего использовать в столбцах с повторяющимися значениями, одни и те же столбцы также могут иметь высокую кратность или большое количество уникальных значений. Как и оператор join , вы также можете применить подсказку перетасовки с, summarize чтобы распределить нагрузку на обработку и, возможно, повысить производительность при работе со столбцами с высокой кратностью.

    В приведенном ниже запросе используется summarize для подсчета отдельных адресов электронной почты получателя, которые могут выполняться в сотнях тысяч в крупных организациях. Для повышения производительности он включает в себя hint.shufflekey:

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

Сценарии запросов

Определение уникальных процессов с помощью идентификаторов процессов

Идентификаторы процессов (PID) в Windows перерабатываются и используются для новых процессов. Они не могут служить уникальными идентификаторами для определенных процессов сами по себе.

Чтобы создать уникальный идентификатор для процесса на определенном компьютере, идентификатор процесса нужно использовать вместе со временем создания процесса. При объединении или обобщении данных по процессам рекомендуется включать столбцы для идентификатора компьютера (либо DeviceId, либоDeviceName), идентификатора процесса (ProcessId или InitiatingProcessId) и времени создания процесса (ProcessCreationTime или InitiatingProcessCreationTime)

В приведенном ниже примере запроса обнаружены процессы, имеющие доступ к более чем 10 IP-адресам через порт 445 (SMB) с одновременным возможным сканированием файловых ресурсов.

Пример запроса

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

В запросе указаны одновременно и InitiatingProcessId, и InitiatingProcessCreationTime. Благодаря этому запрос относится к одному единственному процессу, и при этом исключаются многочисленные другие процессы с аналогичным идентификатором процесса.

Командные строки запроса

Создать командную строку для выполнения задачи можно разными способами. Например, злоумышленник может ссылаться на файл изображения без пути, без расширения файла, с помощью переменных среды или с кавычками. Злоумышленник также может изменить порядок параметров или добавить несколько кавычек и пробелов.

Чтобы создать более устойчивые запросы к командным строкам, примените следующие методики:

  • Определите известные процессы (например ,net.exe или psexec.exe) путем сопоставления в полях имени файла вместо фильтрации по самой командной строке.
  • Анализ разделов командной строки с помощью функции parse_command_line()
  • При запросе аргументов командной строки искать точное совпадение для нескольких несвязанных аргументов в определенном порядке не имеет смысла. Вместо этого используются регулярные выражения или несколько отдельных аргументов, содержащих операторы.
  • Совпадения используются без учета регистра. Например, используйте =~, in~и contains вместо ==, inи contains_cs.
  • Чтобы устранить проблемы с методами маскировки командной строки, рассмотрите возможность удаления кавычек, замены запятых пробелами и замены нескольких последовательных пробелов одним пробелом. Существуют более сложные методы маскирования, требующие других подходов, но эти настройки могут помочь в решении распространенных.

В следующих примерах показаны различные способы создания запроса, который ищет файлnet.exe для остановки службы брандмауэра "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"

Прием данных из внешних источников

Чтобы включить длинные списки или большие таблицы в запрос, используйте оператор externaldata для приема данных из указанного URI. Данные из файлов можно получать в формате TXT, CSV, JSON или других форматах. В приведенном ниже примере показано, как использовать обширный список хэшей вредоносных программ SHA-256, предоставляемых MalwareBazaar (abuse.ch), для проверка вложений в сообщениях электронной почты:

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

Синтаксический анализ строк

Существуют различные функции, которые можно использовать для эффективной обработки строк, требующих синтаксического анализа или преобразования.

String Функция Пример использования
Командные строки parse_command_line() Извлеките команду и все аргументы.
Пути parse_path() Извлеките разделы из пути к файлу или папке.
Номера версий parse_version() Деконструкция номера версии с четырьмя разделами и до восьми символов на раздел. Используйте проанализированные данные для сравнения возраста версий.
IPv4-адреса parse_ipv4() Преобразуйте IPv4-адрес в длинное целое число. Чтобы сравнить IPv4-адреса без их преобразования, используйте ipv4_compare().
IPv6-адреса parse_ipv6() Преобразуйте IPv4-адрес или IPv6-адрес в каноническую нотацию IPv6. Чтобы сравнить IPv6-адреса, используйте ipv6_compare().

Чтобы узнать обо всех поддерживаемых функциях синтаксического анализа, ознакомьтесь со строковыми функциями Kusto.

Примечание.

Некоторые таблицы в этой статье могут быть недоступны в Microsoft Defender для конечной точки. Включите Microsoft Defender XDR для поиска угроз с помощью дополнительных источников данных. Вы можете переместить расширенные рабочие процессы охоты с Microsoft Defender для конечной точки на Microsoft Defender XDR, выполнив действия, описанные в разделе Миграция расширенных запросов охоты из Microsoft Defender для конечной точки.

Совет

Хотите узнать больше? Общайтесь с членами сообщества Microsoft Security в нашем техническом сообществе: Microsoft Defender XDR Tech Community.