
Коллекция KQL-запросов
Некоторые советы, приёмы и примеры использования KQL для Microsoft Sentinel.
Kusto Query Language — это язык, используемый в Azure Monitor, Azure Data Explorer и Azure Log Analytics (то, что Microsoft Sentinel использует под капотом). Мне всегда казалась полезной эта визуализация, касающаяся KQL, —

Мы хотим использовать KQL для создания точных и эффективных запросов, чтобы находить угрозы, детектируемые события, закономерности и аномалии в нашем большом наборе данных.
Возьмём приведённый ниже запрос в качестве примера```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | where AppDisplayName == "Microsoft Teams" | project TimeGenerated, Location, IPAddress, UserAgent
Когда мы выполняем такой запрос, первая строка указывает Microsoft Sentinel, в какой таблице искать данные, поэтому в данном случае мы хотим искать в таблице SigninLogs, куда отправляются данные о входе в Azure AD. Список таблиц можно посмотреть [здесь](https://docs.microsoft.com/en-us/azure/sentinel/data-source-schema-reference).
Затем Microsoft Sentinel будет последовательно выполнять ваш запрос, то есть обрабатывать каждую строку одну за другой, пока не дойдёт до конца или не возникнет ошибка. Итак, разберём наш запрос построчно.```kql
SigninLogs
Итак, сначала мы выбрали нашу таблицу SigninLogs.```kql SigninLogs | where TimeGenerated > ago(14d)
Затем мы указываем Sentinel просмотреть данные за последние 14 дней в этой таблице.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Далее мы просим Sentinel находить только логи, где UserPrincipalName равен "[email protected]"```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0"
Затем мы ищем только те журналы, в которых ResultType == 0, что соответствует успешному входу в Azure AD.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| where AppDisplayName == "Microsoft Teams"
Далее мы ищем только входы в Microsoft Teams.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | where AppDisplayName == "Microsoft Teams" | project TimeGenerated, Location, IPAddress, UserAgent
Наша последняя строка использует оператор проекта, чтобы вернуть только 4 поля из наших журналов, поэтому мы увидим только TimeGenerated, Location, IPAddress и UserAgent, возвращённые из данных SigninLogs.
Вот так вы строите запросы, теперь основы.
## Основы
### Основы работы со временем
Microsoft Sentinel и KQL сильно оптимизированы для фильтров по времени, поэтому если вы знаете период данных, которые хотите найти, следует сразу же отфильтровать временной диапазон. Например, получить журналы за последние 14 дней, а затем искать имя пользователя, как в запросе ниже —```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Гораздо эффективнее, чем сначала искать имя пользователя, а затем искать временной период, вот так —```kql SigninLogs | where UserPrincipalName == "[email protected]" | where TimeGenerated > ago(14d)
KQL предоставляет множество вариантов для запроса конкретных временных периодов.```kql
SigninLogs
| where TimeGenerated > ago(14d)
Как и в первом примере, это будет выполнять поиск за последние 14 дней.```kql SigninLogs | where TimeGenerated > ago(14h)
Вы также можете указать часы.```kql
SigninLogs
| where TimeGenerated > ago(14m)
И минуты.
KQL также поддерживает запросы между диапазонами времени -```kql SigninLogs | where TimeGenerated between (ago(14d) .. ago(7d))
Это позволит найти данные SigninLogs за период от 14 до 7 дней назад.```kql
SigninLogs
| where TimeGenerated between (ago(14h) .. ago(7h))
Между 14 и 7 часами назад.```kql SigninLogs | where TimeGenerated between (ago(14m) .. ago(7m))
И в промежутке от 14 минут до 7 минут назад.
### Основы where
Where — это оператор, который вы будете использовать практически в каждом запросе. Так вы указываете Microsoft Sentinel, какие конкретные данные искать. Синтаксис очень важен для оператора where. Если мы используем тот же пример.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Это позволит выполнить поиск по нашей таблице SigninLogs за последние 14 дней на предмет точных совпадений, где наш UserPrincipalName равен [email protected]. В KQL оператор == чувствителен к регистру, поэтому если вы ищете [email protected], а имя пользователя на самом деле [email protected], вы не получите никаких результатов. Нечувствительный к регистру эквивалент — это =```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName = "[email protected]"
Это найдет все совпадения для [email protected] независимо от регистра.
Вместо equals можно также использовать contains.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName contains "reprise_99"
Это найдет все записи журнала, где UserPrincipalName содержит reprise_99: если у вас есть данные [email protected] и [email protected], будут найдены обе. Оператор contains не чувствителен к регистру, но вы можете использовать contains_cs, чтобы сделать его чувствительным к регистру.
Вы можете использовать либо startswith, либо endswith при поиске определенных шаблонов.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName startswith "reprise_99"
SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName endswith "testdomain.com"
Операторы startswith и endswith не чувствительны к регистру, но вы можете использовать startswith_cs или endswith_cs, чтобы сделать их чувствительными к регистру.
Если вы ищете полные слова (длиннее четырёх символов), в KQL вы можете использовать оператор has. Использование 'has' более эффективно, чем 'contains', поскольку данные индексируются для вас.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where AppDisplayName has "Teams"
Это позволит найти любые журналы SigninLogs, где в отображаемом имени приложения есть слово Teams; это может включать "Microsoft Teams" и "Microsoft Teams Web Client" — оба удовлетворяют запросу.
Если вы ищете несколько слов, можно использовать has_any или has_all.```kql SigninLogs | where TimeGenerated > ago(14d) | where AppDisplayName has_any ("Teams","Outlook")
Это вернёт результаты, где отображаемое имя приложения содержит либо "Teams", либо "Outlook"```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where AppDisplayName has_all ("Teams","Outlook")
Это вернёт результаты, в которых отображаемое имя приложения содержит "Teams" и "Outlook".
Если вы не знаете, по каким полям искать, можно также использовать подстановочные знаки — это неэффективно, но может навести вас на правильный путь, чтобы найти то, что вам нужно.```kql SigninLogs | where TimeGenerated > ago(14d) | where * contains "reprise_99"
Это позволит выполнить поиск в таблице SigninLogs по любому полю, содержащему reprise_99.