Описание специальных функций
В случае необходимости дополнительной обработки данных перед процессом нормализации можно воспользоваться функциями и операторами, позволяющими выполнять сложные операции прямо на странице настройки правила нормализации. Эти операции компилируются непосредственно в байт-коде Python.
Для корректного распознавания логического выражения используйте перенос | и описывайте выражение с новой строки.
По умолчанию все поля, которые указывают при работе с функциями и операторами в рамках дополнительной обработки данных, являются обязательными. Однако в поступающих данных указанные поля иногда могут не присутствовать. И чтобы не возникало ошибки, можно пометить эти поля как необязательные, отметив это в настройке правила нормализации или добавив в описание функции строку "required: false". В таком случае, поле будет обработано и выведено далее, если оно присутствует во входящих данных.
Строковые функции
Преобразование к нижнему регистру (lower)
Преобразование поля или определенной строки к нижнему регистру.
Использование функции: lower(string), где string — строка, преобразуемая к нижнему регистру.
Пример:
my_section.my_field:
field: lower(hostname)
Преобразование к верхнему регистру (upper)
Преобразование поля или определенной строки к верхнему регистру.
Использование функции: upper(string), где string — строка, преобразуемая к верхнему регистру.
Пример:
my_section.my_field:
field: upper(software_name)
Удаление элементов из строки (strip)
Функция убирает из строки все элементы, указанные перечислением в необязательном первом аргументе. Если указан только один (второй) аргумент, то будут удалены только пробелы.
Использование функции: strip("strip_chars", string), где string — строка, из которой необходимо убрать перечисленные символы strip_chars.
Пример использования функции для удаления пробелов:
section.stripped_field:
field: strip(messy_string)
section.stripped_field_comma:
field: strip(",", messy_comma_string)
section.stripped_field_multiple_possible:
field: strip(",\'.", messy_multiple_possible_string)
Разбиение строки (split)
Функция разделяет строку по указанному разделителю и возвращает её в виде списка.
Использование функции: split(string, separator)[index], где string — строка, которую необходимо преобразовать, separator — разделитель, а index — индекс требуемого элемента (допускается использование отрицательного индекса как в Python). [0], [1] и т.д. — первый, второй и т.д. элементы с начала строки, [-1] — первый элемент с конца строки.
Пример:
section.proto_name:
field: split(http.protocol, '/')[0]
section.other_proto_name:
field: split(http.other_name, ',')[-1]
Проверка по регулярному выражению (match)
Функция возвращает true, если строка соответствует заданному регулярному выражению.
Использование функции: match(‘regular_expression’, string), где string — строка, которую необходимо проверить на соответствие, regular_expression — регулярное выражение.
Пример:
section.is_expected_code:
required: false
field: match('(1..|2..|418)', str(http.status))
Замена строки (replace)
Функция выполняет замену в строке, возвращая новую строку с проведенной заменой.
Использование функции: replace(string, old_value, new_value), где string — строка, в которой необходимо произвести замену, old_value — заменяемое значение, new_value — новое значение.
Пример замены немецкого написания слова "benutzer" на английский "user":
section.user_info:
field: replace(line.full_user_name, 'benutzer', 'user')
Логические операторы
Инфиксные операторы также доступны внутри нормализаторов YAML. В этом разделе доступны почти все операторы Python.
Логические операторы возвращают true или false в зависимости от выражения.
Логическое НЕ (not)
Оператор not возвращает true, если поле не соответствует заданному значению, иначе false.
Пример:
section.is_not_using_firefox:
field: not browser_name == 'Firefox'
Равенство (==)
Оператор == возвращает true, если оба операнда равны, иначе false.
Пример:
section.is_using_firefox:
field: software_name == 'Firefox'
Неравенство (!=)
Оператор != возвращает true, если оба операнда различны, иначе false.
Пример:
section.is_not_1_3:
field: version != 1.3
Больше (>)
Оператор > возвращает true, если один операнд больше другого, иначе false.
Пример:
section.is_newer_than_1_0:
field: version > 1.0
Больше или равно (>=)
Оператор >= возвращает true, если один операнд больше или равен другому, иначе false.
Пример:
section.is_at_least_1_0:
field: version >= 1.0
Меньше
Оператор < возвращает true, если один операнд меньше другого, иначе false.
Пример:
section.is_prior_to_1_0:
field: version < 1.0
Меньше или равно
Оператор <= возвращает true, если один операнд меньше или равен другому, иначе false.
Пример:
section.is_prior_or_1_0:
field: version <= 1.0
Логическое И (and)
Оператор and объединяет условия между собой. Если все выражения оцениваются как true, то возвращается true, если хотя бы одно — false, то возвращается false.
Использование оператора: bool_expr_1 and bool_expr_2, где bool_expr — логическое выражение. Допускается использование более двух логических выражений.
Пример:
section.is_firefox_and_windows:
field: browser_name == 'Firefox' and os_name == 'Windows'
Логическое ИЛИ (or)
Оператор or возвращает значение true, если хотя бы одно из выражений оценивается как true, в ином случае — false.
Использование оператора: bool_expr_1 or bool_expr_2, где bool_expr — логическое выражение. Допускается использование более двух логических выражений.
Пример:
section.is_firefox_or_windows:
field: browser_name == 'Firefox' or os_name == 'Windows'
Проверка наличия элемента (in)
Оператор in проверяет вхождение элемента в массив значений. Функция также работает для проверки вхождения подстроки в строку.
Использование оператора: variable in (value_1, value_2, value_3), где variable — переменная, value — значение.
Пример:
section.is_firefox:
field: |
'Firefox' in http.user_agent
Важно! Используйте перенос | для корректного распознавания логического выражения
Арифметические операторы
Умножение (*)
Оператор * умножает два операнда.
Пример:
section.total_cpu_freq:
field: cpu_number * frequency
Деление (/)
Оператор / делит первый операнд на второй.
Пример:
section.division:
field: first_value / second_value
Сложение (+)
Оператор + суммирует два операнда.
Пример:
section.sum:
field: first_value + second_value
Вычитание (-)
Оператор - вычитает из первого операнда второй операнд.
Пример:
section.difference:
field: first_value - second_value
Условные конструкции
cond
Функция cond работает как оператор if/else. Если указанное в первом аргументе логическое выражение оценивается как true, то выводится второй аргумент; если false - третий.
Использование функции: cond(bool_expr, ‘Значение, если истина’, ‘Значение, если ложь’), где bool_expr — логическое выражение.
Пример:
section.browser_hint:
field: |
cond(browser_name == 'Firefox', 'Firefox detected',
'Other browser detected')
Важно! Используйте перенос | для корректного распознавания логического выражения
Функцию cond можно использовать как переключатель и описывать более сложные случаи.
Использование функции: cond(bool_expr, ‘Значение, если истина’, another_bool_expr, 'Значение, если истина', 'Значение по умолчанию'), где bool_expr — логическое выражение.
Пример:
section.firewall_status: |
cond(type == 'utm', 'Suspicious activity was detected',
action == 'close', 'A connection was closed',
action == 'start', 'A connection was started',
'A connection was allowed')
Важно! Используйте перенос | для корректного распознавания логического выражения
Пример без указания значения по умолчанию:
reason.type: |
cond(action == 'reset', 'flow/reset',
action == 'deny', 'flow/deny')
required: false
Важно! Используйте перенос | для корректного распознавания логического выражения
Если значение по умолчанию отсутствует, поле пропускается. В этом случае необходимо отметить в форме настройки правила нормализации рядом с соответствующим полем, что оно необязательное или добавить в поле ввода "required: false", иначе будет ошибка. Если поле является необязательным, а условие ссылается на входную переменную, которая отсутствует, условие будет считаться ложным. Если условие истинно, но в значении отсутствует поле ввода, это поле будет удалено из вывода.
Подробнее про используемый в примере required: false можно прочитать в начале раздела.
optional
Функция проверяет, присутствуют ли все указанные поля, если нет — возвращает значение по умолчанию (или false).
Пример:
outcome:
field: |
cond(optional(tcp.rst, false), 'failed',
optional(tcp.fin, false), 'success',
'pending')
Важно! Используйте перенос | для корректного распознавания логического выражения
Если выражения относятся к нескольким полям, все они должны присутствовать. Следующий пример вернёт NaN, если a, b или c не присутствуют в проанализированных данных. "NaN" указывается, если данные отсутствуют, не существуют.
Пример:
sum:
field: optional(a + b + c, float('nan'))
Поиск данных
Массивы, которые используются в нескольких нормализаторах, размещаются в lookups.yaml. Это специальный файл, содержащий только глобальные поисковые запросы, доступные в каждом нормализаторе.
Необходимо убедиться, что каждый массив имеет уникальное имя.
lookup
Функция lookup работает как поиск значений по ключу. Значения, содержащиеся в массивах, доступны только с помощью этой функции.
Допустим, в "Таблицах просмотра" определен следующий массив с названием "protos":
lookup:
protos:
0: NotSecureProtocol
1: SecureProtocol
2: VerySecureProtocol
3: Telnet
section.field:
field: lookup('protos', proto_id)
section.field:
field: lookup('protos', proto_id, 'Unknown protocol')
exists
Функция exists проверяет, имеет ли поле полезное значение: не null, не пустую строку и не "-".
Возвращает true или false.
Пример:
section.user_data.is_user_set:
field: exists(line.app.data.user)
section.system.app_name:
field: cond(exists(line.app.name), line.app.name, "unknown application")
Преобразование типа данных
Строковый формат (str)
Преобразование значения поля в строковый формат.
Использование функции: variable::str, где variable — переменная.
Пример:
section.field_that_is_a_string:
field: field_that_is_a_int::str
Формат целого числа (int)
Преобразование значения поля в формат целого числа.
Использование функции: variable::int, где variable — переменная.
Пример:
section.field_that_is_a_int:
field: field_that_is_a_string::int
Формат числа с плавающей точкой (float)
Преобразование значения поля в формат числа с плавающей точкой.
Использование функции: variable::float, где variable — переменная.
Пример:
section.field_that_is_a_float:
field: field_that_is_a_int::float
Функции проверки корректного представления данных
Проверка IP-адреса (is_ip)
Функция для определения, является ли предоставленная строка допустимым адресом IPv4 или IPv6.
Пример:
section.is_valid_ip:
field: is_ip(string)
Проверка имени хоста (is_hostname)
Функция для определения, является ли предоставленная строка допустимым именем хоста. Она не должна быть пустой и содержать точки.
Пример:
section.is_valid_hostname:
field: is_hostname(string)
Проверка доменного имени (is_fqdn)
Функция для определения, является ли предоставленная строка корректным доменным именем. Доменное имя должно содержать хотя бы одну точку, метки между точками не должны быть пустыми. Это не должен быть IP-адрес. Доменное имя может заканчиваться точкой.
Пример:
section.is_valid_fqdn:
field: is_fqdn(string)
Функции для работы со временными отметками
Приведение к ISO 8601 (parse_timestamp)
Функция выполняет перебор всех указанных в качестве аргументов форматов временной отметки и пытается разобрать строку my_ts. Функция перебирает форматы временной отметки до тех пор, пока метка времени не будет проанализирована и возвращена в виде строки в формате ISO 8601.
Форматы должны быть строковыми константами. Допустимые форматы: «iso8601» и все директивы синтаксического анализа, поддерживаемые функцией Python strptime.
Использование функции: parse_timestamp(my_ts, format1[, format2, format3...]), где my_ts — отметка времени, format — формат временной отметки.
Пример:
"@timestamp":
field: parse_timestamp(
date + ' ' + time,
'%m/%d/%Y %I:%M:%S %p',
'%Y/%m/%d',
'iso8601'
)
Важно! Синтаксический анализ временных меток с помощью функции parse_timestamp довольно медленный. Рекомендуется для создания временной метки ISO 8601 в первую очередь использовать простые строковые операции, и, только в случае невозможности этого, использовать функцию parse_timestamp.
Приведение к Unix time (timestamp_to_epoch)
Функция принимает временную метку ISO и преобразует ее в секунды, начиная с временной метки эпохи, в виде числа с плавающей точкой. Если в необработанной строке журнала присутствует tzinfo (информация о смещении времени от времени UTC, о переходе на летнее время и проч), то это значение будет использоваться для локализации отметки времени перед преобразованием.
Использование функции: timestamp_to_epoch(my_ts), где my_ts — отметка времени.
Пример:
section.since_epoch:
field: timestamp_to_epoch(ts)
Приведение к UTC (epoch_to_timestamp)
Функция принимает временную метку эпохи в секундах и преобразует ее во временную метку UTC.
Использование функции: epoch_to_timestamp(my_epoch)
Пример:
section.date:
field: epoch_to_timestamp(epoch)
Функции для дополнительной нормализации
Нормализация User Agent (normalize_http_user_agent)
Функция обращается к строке User agent и производит её дополнительный разбор по следующим полям:
- full — содержимое строки User Agent
- name — название браузера
- os — семейство и версия операционной системы
- device — устройство
- major — мажорная версия браузера
- minor — минорная версия браузера
Использование функции: normalize_http_user_agent(string), где string — строка, которую необходимо преобразовать.
Пример использования функции.
Событие:
{"src_ip":"10.10.10.10", "dst_ip":"20.20.20.20", "cs_user_agent":"Mozilla/5.0 (X11; Linux x86_64;rv:60.0) Gecko/20100101 Firefox/60.0"}
section.user_agent:
field: normalize_http_user_agent(cs_user_agent)
"section": {
"user-agent": {
"device": "Other",
"full": "Mozilla/5.0 (X11; Linux x86_64;rv:60.0) Gecko/20100101 Firefox/60.0",
"major": 60,
"minor": 0,
"name": "Firefox",
"os": "Linux"
}
Нормализация MAC-адреса (normalize_mac_address)
Функция имеет один обязательный аргумент (MAC-адрес) и необязательный — второй — аргумент. Второй аргумент имеет логический тип данных и по умолчанию true. Этот аргумент определяет поведение в случае неверного MAC-адреса (по умолчанию строка журнала отправляется в Index Error).
Поддерживаются следующие форматы:
- AA-BB-CC-DD-EE-FF
- AAA.BBB.CCC.DDD
- AAA:BBB:CCC:DDD
- AAA-BBB-CCC-DDD
- AAABBBCCCDDD
Если MAC-адрес действителен, функция преобразует его в стандартный формат AA:BB:CC:DD:11:22. Например, MAC-адрес формата FF-BA-CD-1D-32-11 функция преобразует в формат FF:BA:CD:1D:32:11.
Если MAC-адрес недействителен, а второй аргумент true (по умолчанию), строка будет отправлена в Index Error. Если второй аргумент False, то будет возвращена пустая строка, а для события event.anomaly.malformed_mac_address будет задана нормализованная строка журнала.
Использование функции: normalize_mac_address(mac_address)
Пример обработки события с действительным MAC-адресом и без указания второго аргумента.
Событие:
{"src_ip":"10.10.10.10", "mac_address":"AA-BB-CC-DD-EE-FF"}
section.client_mac:
field: normalize_mac_address(mac_address)
"section": {
"client_mac": "AA:BB:CC:DD:EE:FF"
}
Пример обработки события с недействительным MAC-адресом и false в качестве второго аргумента.
section.client_mac:
field: normalize_mac_address("AA:BB:CC", false)
{
"event": {
"anomaly": {
"malformed_mac_address": [
"AA:BB:CC"
]
}
},
"section": {
"client_mac": ""
}
}
Нормализация данных по хосту (normalize_host)
Функция предназначена для корректного формирования информации о хосте. Принимает на вход ряд полей и возвращает в виде словаря с тремя ключами: IP, FQDN, Hostname, где IP — массив IP-адресов, FQDN — массив доменных имен, Hostname — массив имен хостов.
Использование функции: normalize_host(field1 [, field2, field3, ... , fieldN]), где field — поле.
Пример:
target.host:
field: normalize_host('127.0.0.1','lt-mail','lt-mail.domain','','10.0.0.2')
"target": {
"host": {
"fqdn": ["lt-mail.domain"],
"hostname": ["lt-mail"],
"ip": ["10.0.0.2","127.0.0.1"]
}
}
Нормализация данных URL (normalize_url)
Функция разбивает URL-адрес на составляющие и возвращает в виде словаря. Второй аргумент является необязательным, в случае его отсутствия значением по умолчанию является пустая строка.
Пример использования функции:
url:
field: normalize_url(field, type)
"url": {
'protocol': 'http',
'host': {'hostname': ['pangeoradar.ru'], 'ip': [], 'fqdn': []},
'path': '/',
'params': '',
'username': '',
'password': '',
'port': 80,
'query': '',
'fragment': '',
'original': 'https://pangeoradar.ru/',
'source-type': 'something',
}
- protocol — протокол
- host — структура {'hostname': [], 'ip': [], 'fqdn': []}, в которую передается Hostname, IP или FQDN
- path — путь
- params — параметры
- username — имя пользователя
- password — пароль
- port — порт
- query — запрос
- fragment — фрагмент страницы
- original — оригинальный URL, переданный в функцию
- source-type — тип источника
Событие:
{"src_ip":"10.10.10.10", "url":"http://user:pass@NetLoc:80/path;parameters?query=argument#fragment"}
field: normalize_url(data['url'], 'url')
"target": {
"http": {
"url": {
"fragment": "fragment",
"host": {"fqdn": [], "hostname": ["netloc"], "ip": []},
"original": "http://user:pass@NetLoc:80/path;parameters?query=argument#fragment",
"params": "parameters",
"password": "pass",
"path": "/path",
"port": 80,
"protocol": "http",
"query": "query=argument",
"source-type": "",
"username": "user"
}
}
}
Нормализация данных Windows SID (normalize_windows_sid)
Функция принимает одно поле (Windows SID) в качестве входных данных и возвращает словарь с тремя ключами: category, subcategory и desc, где category — категория, subcategory — подкатегория и desc — описание.
Пример использования функции:
initiator.user.id_details:
field: normalize_windows_sid(SubjectUserSid)
target.user.id_details:
field: normalize_windows_sid(TargetUserSid)
"initiator" : {
"user" : {
"id_details" : {
"category" : "builtin_system_account",
"subcategory" : "builtin_anonymous_account",
"desc" : "ANONYMOUS LOGON"
}
}
}
Пример lookup, который охватывает все 4 варианта случаев. Будет взято первое совпадение:
lookup:
windows_sids:
- "sid": "S-1-5-7"
"match_type": equal
"category": builtin_system_account
"subcategory": builtin_anonymous_account
"desc": ANONYMOUS LOGON
- "sid": "S-1-5-111-"
"match_type": start
"category": builtin_system_account
"subcategory": builtin_virtual_sshd_account
"desc": TBD
- "sid": "S-1-5-21-"
"match_type": start_end
"ends":
- "end": "-500"
"category": standard_account
"subcategory": builtin_virtual_sshd_account
"desc": TBD
- "end": "-501"
"category": standard_account
"subcategory": builtin_guest_account
"desc": TBD
- "not_end": "$"
"category": standard_account
"subcategory": standard_account
"desc": TBD
"initiator" : {
"user" : {
"id_details" : {
"category" : "undefined_account_type",
"subcategory" : "undefined_account_type",
"desc" : "undefined_account_type"
}
}
}
Дополнительные функции
Tapping
Функция, которая помогает обрабатывать сложные непрогнозируемые данные на этапе предварительной обработки.
Пример использования функции:
tap: |
tcp_flags = line.parsed['tcp']
line.parsed['flow_tags'] = [
f"tcp_{flag}"
for flag in
("syn", "fin", "rst", "psh", "ack", "cwr", "ecn", "urg")
if tcp_flags.get(flag, False)
]