Перейти к содержанию

Описание специальных функций

В случае необходимости дополнительной обработки данных перед процессом нормализации можно воспользоваться функциями и операторами, позволяющими выполнять сложные операции прямо на странице настройки правила нормализации. Эти операции компилируются непосредственно в байт-коде 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))
Подробнее про required: false можно прочитать в начале раздела.

Замена строки (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
Тогда есть возможность получить доступ к этим значениям следующим образом, где protocol_id является полем события:
section.field:
  field: lookup('protos', proto_id)
Если ключ не содержится в словаре, анализ завершится неудачей. Чтобы избежать этого, можно указать возвращаемое значение по умолчанию на случай, если ключ не найден. В примере, если proto_id не является допустимым ключом в protos, будет возвращено значение "Unknown protocol":
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)
Пример использования функции exists в сочетании с функцией cond:
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"
        }
    }
}
Перед использованием этой функции в "Таблицах просмотра" необходимо описать массив «windows_sids». Он должен предоставить записи для случаев: 1. sid равен строке, 2. sid начинается с подстроки, 3. sid начинается с подстроки и заканчивается другой подстрокой, 4. sid начинается с подстроки и не заканчивается другой подстрокой.

Пример 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
Пример результата, если lookup без записей:
"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)
    ]