Доброго времени суток, Хабр! Я ИБтивист Александр, скоро как 10 лет профессионально увлекаюсь информационной безопасностью и применяю свои исследования на практике. Эта статья будет полезна ИТ‑ и ИБ‑специалистам, которые интересуются аспектами логирования (аудита) и мониторинга безопасности в операционных системах семейства Linux. Рассмотрим и сравним классический подход к логированию auditd с осовремененными решениями на базе eBPF — tetragon, kunai, falco и Sysmon for Linux.
Сразу скажу, что в статье не будет прорывных технологий — это чисто техническое сравнение инструментов для решения классических задач по безопасности. Надеюсь, кому‑то поможет подобрать оптимальный стек для решения своих задач, и научиться детектировать самые изощрённые вредоносные активности, или просто поможет с расширением кругозора в части возможностей различных средств мониторинга/аудита.
TL;DR
Falco — прожорливый, но герой (особенно для docker), а остальные решения … лучше оставить решать специфичные для них задачи))
Tetragon — больше инструмент трассировки, его использование в контексте безопасности требует глубокого понимания работы ядра Linux. Нет каких‑то суперконфигураций, поэтому для ИБ в чистом виде инструмент пока тяжеловат.
Kunai — отличное решение сбора объёмной телеметрии, но из‑за слабоватой поддержки я бы отнёс это к решениям для активного триажа, а не регулярной работы.
SysmonForLinux — простое решение, когда хочется структурированного вывода и закрытия самых базовых историй (что запустилось, с кем соединилось), но вряд ли справится со сложными атаками.
Auditd — классическое решение, но работа с ним не приносит радости, особенно при контейнерах. Если его обвязать объёмной детектирующей логикой SIEM/EDR/XDR, то ещё можно подумать. Старичок всё ещё хорош, когда речь идёт о чисто хостовом аудите благодаря нативности.
В целом разработка суперконфигураций аудита приводит к выводу: необходима разработка и поддержка своего собственного EDR:(
Содержание
Типы событий для мониторинга
Представим, что у нас есть потребность в защите конечных станций или серверов под управлением любой операционной системы семейства Linux. Не углубляясь и спускаясь именно на уровень операционки, можно (условно!) выделить следующие классы событий безопасности.
Создание, завершение процесса. В подавляющем большинстве случаев уже по командной строке процесса можно понять, какое действие происходило в системе.
Сетевое соединение. Эта телеметрия важна для отслеживания подключений к C2-серверам и других несанкционированных попыток загрузки или выгрузки файлов.
Создание или удаление файла. Базовая потребность — создание файла конфигурации, удаление важной библиотеки.
Изменения файлов. В Linux всё есть файл, поэтому сюда можно отнести почти все события изменения конфигураций системы. Другой вопрос — определить, что именно поменялось.
Вход и выход пользователя. События необходимы для точной привязки действия к субъекту — кто именно его произвёл.
Манипуляции с процессами и библиотеками. Хотя в целом покрывается перечисленными событиями, мониторинг клонирования процессов или инъекций, подмены библиотек — это достаточно интересный пул активности.
Всё вышеперечисленное, применяемое для работы контейнеров. Здесь речь идёт об определении событий безопасности внутри контейнера.
Список можно расширять и другими важными типами событий: изменение конфигурации аудита, работа с пользователями и группами и тому подобное. Разумеется, в реальной жизни хочется аудита в стиле: «вот тут тебя хакнули» или «вот сейчас к тебе хакер подключился», но мы здесь говорим о базовой логике детектирования. Решать такие кейсы — уже удел отдельных средств защиты.
Подопытные проекты
Рассмотрим наших кандидатов, с помощью которых будем пробовать задетектировать различные события.
auditd
Это классика, номер один. Для работы требует установки отдельных правил (например, популярна конфигурация на несколько сотен правил). Правила оперируют такими сущностями, как системные вызовы, аргументы вызова, пути к контролируемым файлам или каталогам, результат операции (успех/неудача), идентификатор пользователя (больше или меньше порога администраторов), тип обращения к файлу (чтение, запись, исполнение, изменение атрибутов).
Теперь к нюансам. Записанное событие из коробки содержит только цифровые идентификаторы, без их расшифровки. При установке параметра log_format = ENRICHEDв файле /etc/audit/auditd.conf эти же параметры дублируются с указанием актуальных значений.
Событие auditd до включения обогащения — выполнение команды cat ~/.bash_history (log_format = RAW):
type=SYSCALL msg=audit(1781665973.981:278365): arch=c000003e syscall=59 success=yes exit=0 a0=562e34238560 a1=562e3420b930 a2=562e34208370 a3=1d584fbb0d3f7dd8 items=3 ppid=160798 pid=181740 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts9 ses=3 comm="cat" exe="/usr/bin/cat" subj=unconfined key="process_creation" type=EXECVE msg=audit(1781665973.981:278365): argc=2 a0="cat" a1="/root/.bash_history" type=CWD msg=audit(1781665973.981:278365): cwd="/home/alex/ebpf" type=PATH msg=audit(1781665973.981:278365): item=0 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0 type=PATH msg=audit(1781665973.981:278365): item=1 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0 type=PATH msg=audit(1781665973.981:278365): item=2 name="/lib64/ld-linux-x86-64.so.2" inode=716894 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
Событие auditd после включения обогащения — выполнение команды cat ~/.bash_history (log_format = ENRICHED). Отличия — обогащённая значениями телеметрия у SYSCALL (ARCH=x86_64 SYSCALL=execve AUID=“alex” UID=“root” GID=“root” EUID=“root” SUID=“root” FSUID=“root” EGID=“root” SGID=“root” FSGID=“root”):
type=SYSCALL msg=audit(1781665815.253:276728): arch=c000003e syscall=59 success=yes exit=0 a0=562e342097b0 a1=562e34238700 a2=562e34208370 a3=1d584fbb0d3f7dd8 items=3 ppid=160798 pid=181649 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts9 ses=3 comm="cat" exe="/usr/bin/cat" subj=unconfined key="process_creation"ARCH=x86_64 SYSCALL=execve AUID="alex" UID="root" GID="root" EUID="root" SUID="root" FSUID="root" EGID="root" SGID="root" FSGID="root" type=EXECVE msg=audit(1781665815.253:276728): argc=2 a0="cat" a1="/root/.bash_history" type=CWD msg=audit(1781665815.253:276728): cwd="/home/alex/ebpf" type=PATH msg=audit(1781665815.253:276728): item=0 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root" type=PATH msg=audit(1781665815.253:276728): item=1 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root" type=PATH msg=audit(1781665815.253:276728): item=2 name="/lib64/ld-linux-x86-64.so.2" inode=716894 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root"
Второй нюанс: генерирование на одно действие нескольких событий auditd с общим идентификатором, но разными смыслами. Вполне нормально, когда приходит связка событий SYSCALL, PATH, PROCTITLE или SYSCALL, EXECVE, CWD, PATH (их может быть несколько). Для корректной обработки и единого контекста события целесообразно схлопывать его в одно полное.
Примеры событий auditd — запуск команды curl mail.ru 2>/dev/null && cat /etc/passwd (3 и 6 записей в логе на 1 действие соответственно):
type=SYSCALL msg=audit(1781665533.473:274769): arch=c000003e syscall=42 success=yes exit=0 a0=7 a1=7fb26c001e30 a2=10 a3=7fb2725fe230 items=0 ppid=160798 pid=181534 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts9 ses=3 comm="curl" exe="/usr/bin/curl" subj=unconfined key="network_connect_4"ARCH=x86_64 SYSCALL=connect AUID="alex" UID="root" GID="root" EUID="root" SUID="root" FSUID="root" EGID="root" SGID="root" FSGID="root" type=SOCKADDR msg=audit(1781665533.473:274769): saddr=020000505A9CE8040000000000000000SADDR={ saddr_fam=inet laddr=90.156.232.4 lport=80 } type=PROCTITLE msg=audit(1781665533.473:274769): proctitle=6375726C006D61696C2E7275 type=SYSCALL msg=audit(1781665533.589:274785): arch=c000003e syscall=59 success=yes exit=0 a0=562e342386e0 a1=562e342386c0 a2=562e34208370 a3=1d584fbb0d3f7dd8 items=3 ppid=160798 pid=181540 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts9 ses=3 comm="cat" exe="/usr/bin/cat" subj=unconfined key="process_creation"ARCH=x86_64 SYSCALL=execve AUID="alex" UID="root" GID="root" EUID="root" SUID="root" FSUID="root" EGID="root" SGID="root" FSGID="root" type=EXECVE msg=audit(1781665533.589:274785): argc=2 a0="cat" a1="/etc/passwd" type=CWD msg=audit(1781665533.589:274785): cwd="/home/alex/ebpf" type=PATH msg=audit(1781665533.589:274785): item=0 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root" type=PATH msg=audit(1781665533.589:274785): item=1 name="/usr/bin/cat" inode=654353 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root" type=PATH msg=audit(1781665533.589:274785): item=2 name="/lib64/ld-linux-x86-64.so.2" inode=716894 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID="root" OGID="root"
Третий нюанс: особенность формирования событий — это и непечатаемые символы в SYSCALL (при ENRICHED режиме), и HEX в событиях сетевого соединения или командной строки (иногда и в поле key).
Четвертый нюанс: исключения и правила можно писать по сути только на перечисленные выше сущности, которыми оперирует auditd. То есть вы не можете сказать «логируй только cat/etc/passwd» или «только подключения на 53 порт». Или логируем весь cat полностью и отслеживаем обращения к /etc/passwd, или мониторим все сетевые соединения.
Отсюда и пятая проблема — каждое применённое правило располагается в оперативной памяти и потребляет определённые ресурсы на своё существование. Чем больше правил — тем больше нагрузка на вычислительные ресурсы. Именно поэтому очень плохой идеей является просто логировать создание всех процессов и сетевых соединений. С такой нагрузкой без фильтрации, особенно на продуктивном сервере СУБД или контейнеризации, зачастую могут возникать проблемы избыточной нагрузки именно со стороны подсистемы аудита (ну, или ротацию журнала каждую минуту, да).
Чтобы преодолеть все эти нюансы, мы решили сравнить возможности других решений. Основным фактором отбора стало использование технологии eBPF — она позволяет подключаться к ядру напрямую, что с одной стороны добавляет низкоуровневости («меня ты обмануть можешь, но ядро обмануть сложнее:) „), а с другой — производительности.“»
Основное назначение — мониторинг и действия, в том числе для трассировки работы контейнеров. Достаточно подробно рассмотрено здесь, поэтому тут отмечу только то, что он достаточно подробен, чёток и реализован на Go. Однако для написания политик требуется глубочайшее понимание архитектуры ядра Linux. Потому что, в отличие от auditd, вы можете работать не только с syscall’ами, но и с точками трассировки, kernel‑ и userprobes. Сходу написать рабочую конфигурацию аудита для решения всех задач ИБ разом — это как в соло подхватить бревно на Первомай. Доступно, но лишь единицам.
Активно развиваемый инструмент на C++ для мониторинга безопасности. Разработчики и энтузиасты постоянно поддерживают набор правил для определения типовых событий, интересных с точки зрения информационной безопасности. Здесь рассматриваем его как более перспективное решение для аудита, чем tetragon — но обо всём по порядку, да и гипотезу эту надо проверить.
Это интересное создание рассматривается как простой вариант «поставил‑поехал». Он не шибко способен учесть все особенности Linux, но подойдёт для детектирования базированной активности — создания процессов, сетевых соединений, немного информации о трассировке одних приложений другими. Из приятного — структурированный формат, но не следует многого ожидать от решения, изначально разработанного под Windows.
Разработчики рассматривают его как инструмент мониторинга безопасности и threat hunting (проактивного выявления аномалий и угроз) для систем Linux. Написан на Rust. Особенно интересно сравнить его с falco — прочитали его описание и решили добавить к сравнению.
Методика и условия тестирования
Проверять подопытных будем на виртуальной машине на Debian 12 (ядро 6.1.0). Тестировать будем в двух режимах.
В первом просто запускается подопытный из коробки, без каких‑либо дополнительных махинаций. У auditd из коробки вообще нет никаких отдельных правил (вот с астрой ситуация обычно иная), у sysmon и tetragon используются базовые на запуск процессов, falco и kunai уже содержат встроенные правила на самые «сочные» инфобезные события по их мнению.
Во втором режиме сделаем запуск с готовыми наборами правил, политик и детектирующей логики. Используем следующие расширенные политики/наборы правил:
Auditd — конфигурация с параметром ENRICHED в основной конфигурации. Есть конечно аналоги с маппингом на MITRE ATT&CK, но такая конфигурация очень стара.
Falco — адаптированная под запуск на современных системах (и с обновлённым falco) конфигурация от Solar.
Исправленная конфигурация Solar
# Falco configuration/rules files for self-auditing - list: falco_configuration_files items: [/etc/falco/falco_rules.local.yaml, /etc/falco/falco_rules.yaml, /etc/falco/falco.yaml] # Cron-related files and directories # T1053.003: Scheduled Task/Job: Cron - list: cron_related_files_and_dirs items: [/etc/cron.d/, /etc/cron.daily/, /etc/cron.hourly/, /etc/cron.monthly/, /etc/cron.weekly/, /etc/cron.allow, /etc/cron.deny, /etc/crontab, /etc/crontab.d/, /var/spool/cron/, /etc/at.allow, /etc/at.deny, /var/spool/at/, /etc/anacrontab, /var/spool/anacron/] # Systemd-related files # T1053.006: Scheduled Task/Job: Systemd Timers # T1543.002: Create or Modify System Process: Systemd Service - list: systemd_related_dirs items: [/etc/systemd/, /usr/lib/systemd/, /lib/systemd/] # Kernel modules-related config files and dirs # T1547.006: Boot or Logon Autostart Execution: Kernel Modules and Extensions - list: kernel_modules_related_dirs items: [/etc/modprobe.conf, /etc/modprobe.d/, /lib/modules-load.d/, /usr/lib/modules-load.d/, /usr/local/lib/modules-load.d/, /etc/modules-load.d/, /run/modules-load.d/] # Init persist via RC scripts # T1037.004: Boot or Logon Initialization Scripts: RC Scripts - list: rc_scripts_related_files_and_dirs items: [/etc/inittab, /etc/init.d/, /etc/init/, /etc/rc.d/, /etc/rc.local, /etc/rc.common] # Shell configuration modification # T1546.004: Event Triggered Execution: Unix Shell Configuration Modification - list: shell_configuration_related_files_and_dirs items: [/etc/profile.d/, /etc/profile, /etc/shells, /etc/bashrc] # Dynamic linker hijacking # T1574.006: Hijack Execution Flow: Dynamic Linker Hijacking - list: ld_so_related_files_and_dirs items: [/etc/ld.so.conf, /etc/ld.so.conf.d/, /etc/ld.so.preload] # Sudo priv esc # T1548.003: Abuse Elevation Control Mechanism: Sudo and Sudo Caching - list: sudo_related_files_and_dirs items: [/etc/sudoers, /etc/sudoers.d/] # PAM modification # T1556.003: Modify Authentication Process: Pluggable Authentication Modules - list: pam_related_files_and_dirs items: [/etc/pam.d/, /etc/security/limits.conf, /etc/security/pam_env.conf, /etc/security/namespace.conf, /etc/security/namespace.init, /etc/security/pwquality.conf, /etc/pam.conf, /usr/lib/security/, /lib/x86_64-linux-gnu/security/, /usr/lib64/security/] # Time related files - list: time_related_files items: [/etc/localtime] # Login configuration and information files - list: login_conf_files items: [/etc/login.defs, /etc/securetty, /var/log/faillog, /var/log/lastlog, /var/log/tallylog, /var/log/wtmp, /var/log/btmp] # Sysctl configuration files and dirs - list: sysctl_conf_files_and_dirs items: [/etc/sysctl.conf, /etc/sysctl.d/, /usr/lib/sysctl.d/, /run/sysctl.d/] # Network related files - list: network_related_files items: [/etc/hosts, /etc/resolv.conf, /etc/network/interfaces, /etc/sysconfig/network] # Other system files - list: other_system_files items: [/etc/default/grub] # SSH configuration # Remote Services T1021 - list: ssh_configuration_files_and_dirs items: [/etc/ssh/sshd_config, /etc/ssh/sshd_config.d/] # SSH keys # Root SSH keys T1098/004 - list: root_ssh_keys items: [/root/.ssh] # XDG Autostart Entries # T1547.013 - list: xdg_systemwide items: [/etc/xdg/autostart] # Password/shadow files - list: password_shadow_files items: [/etc/group, /etc/passwd, /etc/shadow, /etc/gshadow, /etc/security/opasswd] # Critical or system files and dirs - list: write_create_critical_files_and_dirs items: [falco_configuration_files, cron_related_files_and_dirs, systemd_related_dirs, kernel_modules_related_dirs, rc_scripts_related_files_and_dirs, shell_configuration_related_files_and_dirs, ld_so_related_files_and_dirs, sudo_related_files_and_dirs, pam_related_files_and_dirs, time_related_files, login_conf_files, sysctl_conf_files_and_dirs, ssh_configuration_files_and_dirs, password_shadow_files, root_ssh_keys, xdg_systemwide, network_related_files, other_system_files] # Root directories for monitoring - list: root_dirs items: [/bin/, /sbin/, /usr/bin/, /usr/sbin/, /usr/local/bin/, /usr/local/sbin/] # Non-critical directories - list: non_critical_dirs items: [/tmp/, /var/tmp/, /dev/shm/, /var/log/] # Исправленный макрос bpfuse - macro: bpfuse condition: (evt.type = bpf) - macro: spawned_process condition: (evt.type in (execve, execveat)) - macro: open_write condition: (evt.type in (open, openat, openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0) - macro: user_ssh_dir condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*') - macro: user_bash_config_files condition: > (fd.name glob '/home/*/.bash_profile' or fd.name glob '/home/*/.bash_login' or fd.name glob '/home/*/.profile' or fd.name glob '/home/*/.bashrc' or fd.name glob '/home/*/.bash_logout' or fd.name glob '/root/.bash_profile' or fd.name glob '/root/.bash_login' or fd.name glob '/root/.profile' or fd.name glob '/root/.bashrc' or fd.name glob '/root/.bash_logout') - macro: user_xdg_autostart condition: (fd.name contains '/.config/' and fd.name glob '/home/*/.config/autostart/*') - macro: files_creation_by_extension condition: > (fd.filename endswith .ko or fd.filename endswith .so or fd.filename endswith .sh or fd.filename endswith .out or fd.filename endswith .py or fd.filename endswith .pl or fd.filename endswith .rb or fd.filename endswith .php or fd.filename endswith .js or fd.filename endswith .jsp or fd.filename endswith .jspx or fd.filename endswith .jar or fd.filename endswith .aspx or fd.filename endswith .asp or fd.filename endswith .asmx or fd.filename endswith .html or fd.filename endswith .kirbi or fd.filename glob "*.so.*") - macro: user_systemd condition: fd.name glob '/home/*/.config/systemd/*' - macro: inbound condition: > ((evt.type in (accept, listen, recvfrom, recvmsg)) and (fd.typechar = 4 or fd.typechar = 6) and (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and (evt.rawres >= 0 or evt.res = EINPROGRESS)) - macro: outbound condition: > ((evt.type in (connect, sendto, sendmsg)) and (fd.typechar = 4 or fd.typechar = 6) and (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and (evt.rawres >= 0 or evt.res = EINPROGRESS)) # Макрос для проверки существования процесса (используется в ptrace правиле) - macro: proc_name_exists condition: (proc.name != "") - rule: Process started - generic_user_execution desc: Generic rule for audit processes started by authenticated users condition: spawned_process and user.loginuid != -1 enabled: true output: "Process started (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %container.mounts %thread.cap_effective %k8s.pod.name %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %proc.pexe %proc.pname %proc.pcmdline %proc.env[PATH])" priority: INFO tags: [eventid_1, proc_execution, generic_user_execution, 4rays] - rule: Outbound TCP connection desc: Generic rule for outbound TCP connections condition: outbound and fd.l4proto=tcp enabled: true output: "Outbound TCP connection (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.cip %fd.cport %fd.sip %fd.sport %fd.is_server %fd.type %fd.l4proto)" priority: INFO tags: [eventid_3, network_connection, tcp, outbound, 4rays] - rule: Outbound UDP connection desc: Generic rule for outbound UDP connections condition: outbound and fd.l4proto=udp enabled: true output: "Outbound UDP connection (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.cip %fd.cport %fd.sip %fd.sport %fd.is_server %fd.type %fd.l4proto)" priority: INFO tags: [eventid_3, network_connection, udp, outbound, 4rays] - rule: Inbound TCP connection desc: Generic rule for inbound TCP connections condition: inbound and fd.l4proto=tcp enabled: true output: "Inbound TCP connection (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.cip %fd.cport %fd.sip %fd.sport %fd.is_server %fd.type %fd.l4proto)" priority: INFO tags: [eventid_3, network_connection, tcp, inbound, 4rays] - rule: Inbound UDP connection desc: Generic rule for inbound UDP connections condition: inbound and fd.l4proto=udp enabled: true output: "Inbound UDP connection (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.cip %fd.cport %fd.sip %fd.sport %fd.is_server %fd.type %fd.l4proto)" priority: INFO tags: [eventid_3, network_connection, udp, inbound, 4rays] - rule: Modification of a watched file or file creation in a watched directory desc: Detect critical system files modification or file creation in a watched directory condition: > ((evt.type = creat or open_write) and (fd.name pmatch (write_create_critical_files_and_dirs) or files_creation_by_extension or user_ssh_dir or user_xdg_autostart or user_bash_config_files or user_systemd or fd.directory pmatch (non_critical_dirs) or fd.directory in (root_dirs))) enabled: true output: "File created or modified (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.name %evt.arg.flags %evt.res)" priority: INFO tags: [eventid_11, filesystem, creation, modification, 4rays] - rule: LD_PRELOAD exists desc: Process started with LD_PRELOAD environment variable condition: evt.type in (execve, execveat) and proc.env[LD_PRELOAD] != "" enabled: true output: "Process started with LD_PRELOAD environment variable (%user.name %proc.env[LD_PRELOAD] %proc.pid %proc.exe %proc.exepath %proc.name %proc.cmdline %proc.exeline %proc.ppid %proc.pname %proc.pcmdline %evt.type)" priority: INFO tags: [eventid_1, proc_execution, ld_preload, 4rays] - rule: Create hardlink desc: Hardlink created condition: evt.type in (link, linkat) and (evt.abspath.src endswith wget or evt.abspath.src endswith curl) enabled: true output: "Hardlink created %evt.args %evt.type %proc.name %evt.abspath.src %evt.abspath.dst" priority: INFO tags: [eventid_12, hardlink, 4rays] - rule: Create symlink desc: Symlink created condition: evt.type in (symlink, symlinkat) and (fs.path.target endswith wget or fs.path.target endswith curl) enabled: true output: "Symlink created %fs.path.target %evt.args %evt.type %proc.name %evt.abspath.src %evt.abspath.dst" priority: INFO tags: [eventid_12, symlink, 4rays] - rule: Potential evasion with symlink desc: Potential evasion of wget or curl use via symlinks condition: evt.type in (execve, execveat) and proc.exepath in ("/usr/bin/wget", "/usr/bin/curl") and (proc.name != "wget" and proc.name != "curl") enabled: true output: "Process started (%user.name %proc.pid %proc.exe %proc.exepath %proc.name %proc.cmdline %proc.exeline %proc.ppid %proc.pname %proc.pcmdline %evt.type)" priority: INFO tags: [eventid_1, proc_execution, 4rays] - rule: Self-deleted file desc: A process deleting its own executable condition: evt.type in (unlink, unlinkat) and proc.exepath = val(fs.path.name) enabled: true output: "Detected self-deleted file %evt.type %fs.path.name %proc.name %proc.exepath %proc.pname" priority: INFO tags: [eventid_23, filesystem, deletion, 4rays] - rule: EBPF Module Load desc: EBPF Module Load condition: > bpfuse and evt.arg.cmd = 5 and proc.exe != "falco" enabled: true output: "BPF load (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.name %evt.arg.flags %evt.res %evt.args %evt.info %evt.arg.cmd)" priority: INFO tags: [eventid_34, bpf_prog_load, 4rays] - rule: Created file in memfd desc: Created file in memfd condition: evt.type = "memfd_create" enabled: true output: "Created file in memfd (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %fd.name %fd.num %evt.arg.flags %evt.res)" priority: INFO tags: [eventid_11, filesystem, creation, modification, memfd, 4rays] # Изменен подход - теперь отслеживаем PTRACE_ATTACH как exit событие - rule: Ptrace attached to process desc: Detects an attempt to inject code into a process using ptrace syscall condition: > evt.type=ptrace and evt.arg.request in ("PTRACE_ATTACH", 16) and proc_name_exists enabled: true output: "Detected ptrace PTRACE_ATTACH attempt (%evt.type %user.name %user.uid %user.loginuid %user.loginname %container.id %container.name %container.image %container.image.id %container.privileged %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.duration %proc.sid %proc.tty %proc.cwd %proc.pid %proc.exepath %proc.exe %proc.name %proc.cmdline %proc.ppid %proc.pexepath %evt.arg.request %evt.arg.pid)" tags: [eventid_10, process_injection, 4rays] priority: INFO
Tetragon — взяли за основу адаптированную конфигурацию falco с небольшими доработками и использовали такой «франкештейн» (слава LLM):
политика Tetragon
apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "full-monitoring-policy" spec: kprobes: # ============================================ # 1. MONITORING PROCESS EXECUTION # ============================================ - call: "__x64_sys_execve" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" - call: "__x64_sys_execveat" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 2. MONITORING OUTBOUND TCP CONNECTIONS (РАБОТАЕТ) # ============================================ - call: "__x64_sys_connect" syscall: true args: - index: 1 type: "sockaddr" selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 3. MONITORING PROCESS WITH LD_PRELOAD # ============================================ - call: "__x64_sys_execve" syscall: true args: - index: 1 type: "string" selectors: - matchArgs: - index: 1 operator: "SubString" values: - "LD_PRELOAD=" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 4. MONITORING FILE PERMISSIONS (WRITE) # ============================================ - call: "security_file_permission" syscall: false args: - index: 0 type: "file" - index: 1 type: "int" selectors: - matchArgs: - index: 1 operator: "Equal" values: - "2" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 5. MONITORING CRITICAL SYSTEM FILES # ============================================ - call: "security_inode_create" syscall: false args: - index: 1 type: "string" selectors: - matchArgs: - index: 1 operator: "Prefix" values: - "/etc/cron.d/" - "/etc/cron.daily/" - "/etc/systemd/system/" - "/etc/modprobe.d/" - "/etc/init.d/" - "/etc/rc.d/" - "/etc/profile.d/" - "/etc/ld.so.conf.d/" - "/etc/sudoers.d/" - "/etc/pam.d/" - "/etc/ssh/sshd_config.d/" - "/root/.ssh/" - "/etc/xdg/autostart/" - "/etc/sysctl.d/" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 6. MONITORING SSH KEYS # ============================================ - call: "security_inode_create" syscall: false args: - index: 1 type: "string" selectors: - matchArgs: - index: 1 operator: "SubString" values: - ".ssh/" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 7. MONITORING SHELL CONFIG FILES # ============================================ - call: "security_inode_create" syscall: false args: - index: 1 type: "string" selectors: - matchArgs: - index: 1 operator: "Postfix" values: - ".bashrc" - ".bash_profile" - ".zshrc" - ".profile" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 8. MONITORING SUSPICIOUS EXTENSIONS # ============================================ - call: "security_inode_create" syscall: false args: - index: 1 type: "string" selectors: - matchArgs: - index: 1 operator: "Postfix" values: - ".ko" - ".so" - ".sh" - ".out" - ".py" - ".pl" - ".rb" - ".php" - ".js" - ".jsp" - ".jar" - ".aspx" - ".asp" - ".kirbi" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 9. MONITORING BPF MODULE LOAD # ============================================ - call: "__x64_sys_bpf" syscall: true args: - index: 0 type: "int" selectors: - matchArgs: - index: 0 operator: "Equal" values: - "5" matchBinaries: - operator: NotIn values: - "/usr/bin/falco" - "/usr/bin/tetragon" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 10. MONITORING MEMFD_CREATE # ============================================ - call: "__x64_sys_memfd_create" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 11. MONITORING PTRACE # ============================================ - call: "__x64_sys_ptrace" syscall: true args: - index: 0 type: "int" selectors: - matchArgs: - index: 0 operator: "Equal" values: - "16" matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 12. MONITORING HARD LINKS # ============================================ - call: "__x64_sys_link" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" - call: "__x64_sys_linkat" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 13. MONITORING SYMBOLIC LINKS # ============================================ - call: "__x64_sys_symlink" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" - call: "__x64_sys_symlinkat" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" # ============================================ # 14. MONITORING FILE DELETION # ============================================ - call: "__x64_sys_unlink" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns" - call: "__x64_sys_unlinkat" syscall: true selectors: - matchActions: - action: Post matchNamespaces: - namespace: Pid operator: NotIn values: - "host_ns"
SysmonForLinux — возьмём готовую конфигурацию, с маппингом на MITRE ATT&CK. Хоть она и старая, было интересно её погонять. Понятно, что можно её расширить и адаптировать под современность, но в данном исследовании очень хотел протестировать «как есть».
Kunai — нашёлся целый отдельный и сравнительно поддерживаемый проект, которым я и воспользуюсь. Из плюсов — готовый маппинг на MITRE ATT&CK.
Оценивать будем следующие параметры:
потребление ресурсов (% CPU, оперативной памяти);
общий объём собранной телеметрии за время тестирования (общий объём, EPS);
качество и полнота телеметрии — удалось ли залогировать эмулируемую активность, насколько подробны события, позволяют ли восстановить полный контекст произошедшего с точки зрения SOC‑аналитика.
Осталось определиться с эмулируемой активностью, которую будем ловить. Несмотря на возможность эмулирования через решения BAS или игр через Metasploit (или любой другой C2-сервер), на этапе предварительной оценки решили ограничиться базовыми испытаниями — простым выполнением команд в терминале. Продвинутые техники (подмена библиотек на лету, манипуляции процессами) здесь не рассматриваются — комплексному исследованию конкретных эксплойтов следует посвятить отдельную статью.
1. Создание, завершение процесса
# простое чтение файла cat /etc/shadow # создание пользователя useradd alex5 # запуск и завершение процесса sleep 60 & PID=$!; sleep 5; kill -9 $PID # чтение истории history # чтение диска напрямую dd if=/dev/sda bs=4024 count=5 | hexdump -C # запуск трассировки процесса strace cat /etc/passwd
2. Сетевое соединение
# обращение к внешнему сайту curl ifconfig.co # dns-запрос - удачный nslookup yandex.ru # dns-запрос – неудачный nslookup azaza.comet # открываем прослушивание порта nc -l 11111 & # простой ping ping 77.88.8.8 -c 5
3. Создание/удаление файла
# создание через направление echo “llm power” > barberfile # прямое редактирование нового файла с произвольным текстом nano /home/miracle # удаление файла rm barberfile # суперудаление файла shred -u /home/miracle # создание дампа процесса sudo gcore -o tetragon_dump $(pidof tetragon)
4. Изменения файлов
# замена исполняемого файла mv /usr/bin/md5sum /usr/bin/md6sum && cp /usr/bin/sha256sum /usr/bin/md5sum && printf "%s" "test valuable" | md5sum # классическое дополнение в файл echo ‘# valuable addition’ >> /etc/sudoers # ввод произвольных изменений в файл nano /etc/addusers.conf # изменение атрибутов файла (установка immutable) chattr +i /etc/audit/auditd.conf # изменение атрибутов файла (установка immutable) chattr +x /etc/passwd
5. Вход пользователя
# локальный вход su alex # эмулируем удалённый вход ssh alex@localhost
6. Определение действий в docker‑контейнерах (был поднят подопытный alpine, в котором выполняется команда)
# предварительно # docker run --rm -it alpine # apk --no-cache add curl # запуск команды в контейнере curl https://ifconfig.me
Для сценариев 1–5 команды отката изменений на всякий случай (и для воспроизводимости результатов):
Команды для отката изменений
userdel alex5 mv /usr/bin/md6sum /usr/bin/md5sum chattr -i /etc/audit/auditd.conf chattr -x /etc/passwd fg # Ctrl+C для прихлопа процесса nc # не забыть выйти из su/ssh через exit при необходимости
На этапе оценки потребления ресурсов для генерирования объёма телеметрии запускаем скрипт ifrit.sh. Он предназначен для базового триажа, который сделает логам много грязи, свойственной стадии разведки. Измерения проводятся для процесса в ОС как без нагрузки, так и при запуске скрипта для эмуляции нагрузки. Полученные показатели считаем максимальными. Для мониторинга ресурсов заказал у нейронок следующий скрипт:
Скрипт для мониторинга потребляемых ресурсов
#!/bin/bash # # logger_monitor.sh # # Usage: # ./logger_monitor.sh [duration] [interval] # # Example: # ./logger_monitor.sh 300 1 # DURATION=${1:-300} INTERVAL=${2:-1} PROCESSES=( "auditd" "falco" "tetragon" "sysmon" "kunai" ) declare -A PREV_PROC_CPU declare -A PREV_TOTAL_CPU declare -A PREV_READ declare -A PREV_WRITE declare -A CPU_SUM CPU_MAX CPU_CNT declare -A MEM_SUM MEM_MAX MEM_CNT declare -A READ_SUM READ_MAX READ_CNT declare -A WRITE_SUM WRITE_MAX WRITE_CNT trap graceful_exit INT ################################################# # Find PIDs ################################################# get_pids() { local name="$1" case "$name" in auditd) pgrep -f 'auditd|kauditd' ;; falco) pgrep falco ;; tetragon) pgrep tetragon ;; sysmon) pgrep -f sysmon ;; kunai) pgrep -f kunai ;; *) pgrep -f "$name" ;; esac } ################################################# # Total CPU jiffies ################################################# get_total_cpu() { awk ' /^cpu /{ s=0 for(i=2;i<=NF;i++) s+=$i print s } ' /proc/stat } ################################################# # Process CPU jiffies ################################################# get_proc_cpu() { local pid=$1 awk ' { print $14 + $15 } ' "/proc/$pid/stat" 2>/dev/null } ################################################# # RSS memory (MB) ################################################# get_mem_mb() { local pid=$1 awk ' /^VmRSS:/{ printf "%.2f", $2 / 1024 } ' "/proc/$pid/status" 2>/dev/null } ################################################# # Read/write bytes ################################################# get_io() { local pid=$1 awk ' /^read_bytes:/ {r=$2} /^write_bytes:/ {w=$2} END { if(r=="") r=0 if(w=="") w=0 print r, w } ' "/proc/$pid/io" 2>/dev/null } ################################################# # Update maximum ################################################# update_max() { local current=$1 local previous=$2 awk -v a="$current" -v b="$previous" ' BEGIN { if(a>b) print a else print b } ' } graceful_exit() { echo echo "Ctrl+C detected. Printing results..." echo print_results exit 0 } ################################################# # One sample ################################################# sample_process() { local name=$1 local pids pids=$(get_pids "$name") [[ -z "$pids" ]] && return local proc_cpu=0 local mem=0 local read_b=0 local write_b=0 for pid in $pids do [[ ! -d /proc/$pid ]] && continue p=$(get_proc_cpu "$pid") m=$(get_mem_mb "$pid") rw=$(get_io "$pid") r=$(echo "$rw" | awk '{print $1}') w=$(echo "$rw" | awk '{print $2}') proc_cpu=$((proc_cpu + p)) read_b=$((read_b + r)) write_b=$((write_b + w)) mem=$(awk -v a="$mem" -v b="$m" ' BEGIN { printf "%.2f", a+b } ') done local total_cpu total_cpu=$(get_total_cpu) # # First sample only initializes counters # if [[ -z "${PREV_TOTAL_CPU[$name]}" ]] then PREV_PROC_CPU[$name]=$proc_cpu PREV_TOTAL_CPU[$name]=$total_cpu PREV_READ[$name]=$read_b PREV_WRITE[$name]=$write_b return fi ################################################# # CPU % ################################################# dproc=$((proc_cpu - PREV_PROC_CPU[$name])) dtotal=$((total_cpu - PREV_TOTAL_CPU[$name])) cpu=$(awk -v p="$dproc" -v t="$dtotal" ' BEGIN { if(t>0) printf "%.2f", 100*p/t else print 0 } ') ################################################# # MB/s ################################################# dread=$((read_b - PREV_READ[$name])) dwrite=$((write_b - PREV_WRITE[$name])) read_rate=$(awk -v d="$dread" -v i="$INTERVAL" ' BEGIN { printf "%.2f", d/1048576/i } ') write_rate=$(awk -v d="$dwrite" -v i="$INTERVAL" ' BEGIN { printf "%.2f", d/1048576/i } ') ################################################# # Update statistics ################################################# CPU_SUM[$name]=$(awk -v a="${CPU_SUM[$name]:-0}" -v b="$cpu" ' BEGIN { print a+b } ') CPU_CNT[$name]=$(( ${CPU_CNT[$name]:-0} + 1 )) CPU_MAX[$name]=$(update_max "$cpu" "${CPU_MAX[$name]:-0}") MEM_SUM[$name]=$(awk -v a="${MEM_SUM[$name]:-0}" -v b="$mem" ' BEGIN { print a+b } ') MEM_CNT[$name]=$(( ${MEM_CNT[$name]:-0} + 1 )) MEM_MAX[$name]=$(update_max "$mem" "${MEM_MAX[$name]:-0}") READ_SUM[$name]=$(awk -v a="${READ_SUM[$name]:-0}" -v b="$read_rate" ' BEGIN { print a+b } ') READ_CNT[$name]=$(( ${READ_CNT[$name]:-0} + 1 )) READ_MAX[$name]=$(update_max "$read_rate" "${READ_MAX[$name]:-0}") WRITE_SUM[$name]=$(awk -v a="${WRITE_SUM[$name]:-0}" -v b="$write_rate" ' BEGIN { print a+b } ') WRITE_CNT[$name]=$(( ${WRITE_CNT[$name]:-0} + 1 )) WRITE_MAX[$name]=$(update_max "$write_rate" "${WRITE_MAX[$name]:-0}") ################################################# # Save previous sample ################################################# PREV_PROC_CPU[$name]=$proc_cpu PREV_TOTAL_CPU[$name]=$total_cpu PREV_READ[$name]=$read_b PREV_WRITE[$name]=$write_b } ################################################# # Monitoring loop ################################################# ################################################# # Final table ################################################# print_results() { echo printf "%-15s %-12s %-15s %-15s %-15s\n" \ "Solution" "CPU%" "MEM(MB)" "I/O R(MB/s)" "I/O W(MB/s)" for p in "${PROCESSES[@]}" do cpu_avg=$(awk -v s="${CPU_SUM[$p]:-0}" \ -v c="${CPU_CNT[$p]:-0}" ' BEGIN { if(c>0) printf "%.2f", s/c else print 0 } ') mem_avg=$(awk -v s="${MEM_SUM[$p]:-0}" \ -v c="${MEM_CNT[$p]:-0}" ' BEGIN { if(c>0) printf "%.2f", s/c else print 0 } ') read_avg=$(awk -v s="${READ_SUM[$p]:-0}" \ -v c="${READ_CNT[$p]:-0}" ' BEGIN { if(c>0) printf "%.2f", s/c else print 0 } ') write_avg=$(awk -v s="${WRITE_SUM[$p]:-0}" \ -v c="${WRITE_CNT[$p]:-0}" ' BEGIN { if(c>0) printf "%.2f", s/c else print 0 } ') printf "%-15s %5s/%-5s %7s/%-7s %7s/%-7s %7s/%-7s\n" \ "$p" \ "$cpu_avg" "${CPU_MAX[$p]:-0}" \ "$mem_avg" "${MEM_MAX[$p]:-0}" \ "$read_avg" "${READ_MAX[$p]:-0}" \ "$write_avg" "${WRITE_MAX[$p]:-0}" done } echo "Monitoring for ${DURATION}s" echo "Sampling interval: ${INTERVAL}s" echo loops=$((DURATION / INTERVAL)) for ((i=1;i<=loops;i++)) do for p in "${PROCESSES[@]}" do sample_process "$p" done sleep "$INTERVAL" done print_results
Шпаргалки по включению расширенных политик аудита для каждого испытуемого:
Как включить расширенные политики аудита
## auditd # забираем файл https://github.com/Neo23x0/auditd/blob/master/audit.rules # проверить что формат стоит обогащённый cat /etc/audit/auditd.conf | grep log_format # должно быть log_format = ENRICHED # если стоял RAW, то меняем и перезапускаем сервис через systemctl restart auditd.service # копируем файл правил и применяем правила cp audit.rules /etc/audit/rules.d/ augenrules --load # убеждаемся, что всё работает и применилось auditctl -s auditctl -l ######################################################## ## falco # берём адаптированную конфигурацию на основе https://github.com/4RAYS-by-SOLAR/falco-ruleset/blob/master/falco%20config.yml, просим LMM исправить несовершенство cp ./ solar_falco.yaml /etc/falco/rules.d/ # выставляем формат JSON для наглядности cat /etc/falco/falco.yaml | grep -e "^json_output" # вывод должен быть #json_output: true # прописать конфигурацию в подгружаемую по умолчанию в /etc/falco/falco.yaml #rules_files: # - /etc/falco/falco_rules.yaml # - /etc/falco/falco_rules.local.yaml # - /etc/falco/rules.d # - /etc/falco/rules.d/solar_falco.yaml # перезагружаем сервис systemctl restart falco ######################################################## ## tetragon # берём политику, записываем её в файл mod_policy.yaml и устанавливаем из файла sudo tetra tracingpolicy add mod_policy.yaml # проверка, что политика активна sudo tetra tracingpolicy list # тут может быть выведена ошибка при долгой загрузке, но спустя пару минут политика всё же применяется # вывод примерно следующий, с указанием потребляемой памяти: full-monitoring-policy enabled - 5.21 mb ######################################################## ## sysmon – берём конфигурацию, ей всего-то 5 лет https://github.com/microsoft/MSTIC-Sysmon/blob/main/linux/configs/main.xml # запускаем sysmon sysmon -i -n # применить конфигурацию sysmon -c main.xml # убедиться, что правила загрузились sysmon -c ######################################################## ## kunai – скачиваем репозиторий https://github.com/digisquad-repo/kunai-rules # важно учесть, что там может быть не самый последний бинарник kunai cd ./kunai-rules chmod +x ./start.sh ./start.sh # лог будет записан в файл /var/log/kunai/kunai_server_*.json

Результаты испытаний
Путевая заметка: во втором раунде одновременный запуск всех логгеров с расширенной политикой фризил терминал — я полагаю, из‑за большой нагрузки на ресурсы ВМ, особенно в части вывода телеметрии в файлы. Поэтому там эксперимент воспроизводился сначала для связки tetragon+falco+sysmon, затем уже auditd и kunai.
По потреблению ресурсов
№ п/п |
Решение |
Среднее потребление ресурсов из коробки (средний/ максимальный) |
Среднее потребление ресурсов после адаптации (средний/ максимальный) |
|||||||
|
CPU
|
MEM (MB) |
I/O R (MB) |
I/O W (MB) |
CPU
|
MEM (MB) |
I/O R (MB) |
I/O W (MB) |
|||
1 |
|
7.83 / 18.9 |
36.5 / 44.6 |
55.4 / 108.2 |
5.13 / 7.75 |
31.2 / 66.8 |
395.7 / 442.9 |
74.3 / 18.3 |
6.55 / 3.10 |
|
2 |
|
9.74 / 17.0 |
106.8 / 164.89 |
1.5 / 6.39 |
0 / 0.08 |
32.2 / 46.18 |
180.1 / 212.4 |
0.29 / 38.2 |
0.34 / 1.33 |
|
3 |
|
8.44 / 9.0 |
67.93 / 70.1 |
4.95 / 18.6 |
0.78 / 1.19 |
9.8 / 23.12 |
64.3 / 65.9 |
3.27 / 11.6 |
0.54 / 1.16 |
|
4 |
|
5.2 / 7.83 |
22.0 / 57.9 |
1.3 / 34.2 |
0.01 / 0.02 |
1.95 / 2.72 |
15.5 / 29.45 |
0 / 0.5 |
0 / 0 |
|
5 |
auditd |
0 / 0.28 |
2.09 / 2.24 |
0.13 / 0.21 |
0.01 / 0.02 |
7.2 / 18.2 |
2.25 / 2.33 |
0.01 / 0.01 |
2.92 / 10.16 |
|
Результаты, очевидно, не суперстатичны, так как на живой системе есть и свои выполняемые задачи — это может несколько зашумлять статистику. За ориентир берём тот, который был зафиксирован на стенде. Жирным выделены самые большие «потребляторы». При первом применении политики зачастую наблюдался кратковременный взрывной рост потребления ресурсов (в первую очередь CPU) — спустя непродолжительное время показатели устаканивались.
При штатной работе из коробки без дополнительных правил tetragon, sysmon и auditd почти не потребляют CPU. Поскольку kunai и falco шлют данные в syslog, то у них минимальная работа с диском.
Самыми ресурсопотребляемыми оказываются (в порядке убывания прожорливости):
По CPU: kunai, falco, tetragon.
По памяти: kunai, falco, tetragon.
По диску получилось смешанно, но в целом это kunai, tetragon, falco и auditd в расширенном режиме.
Получается, самым нетребовательным к ресурсам стенда в целом оказываются auditd и sysmon. Но насколько они объёмны по телеметрии и каково её качество?
По количеству телеметрии
Здесь происходил запуск всех логгеров с выводом ивентов в файл, затем запуск ifrit.sh. После окончания его работы логгеры останавливались и рассчитывался средний поток EPS для каждого участника соревнований. EPS считался как усреднённое значение между («всего событий (строк) в логе» / «время в секундах между первой и последней записью лога») и («размер лог‑файла в байтах»/«средний размер 1 события [агрегированного в случае auditd] в байтах»/ «время в секундах между первой и последней записью лога»):
Типовые команды для отбора телеметрии были следующие
# auditd tail -F /var/log/audit/audit.log >> audit_do # tetragon tail -F /var/log/tetragon/tetragon.log >> tetragon_do # kunai ./kunai-amd64 >> kunai_do # при запуске kunai-rules формируется отдельный лог-файл # falco falco >> falco_do # sysmon journalctl -f | grep Sysmon >> sysmon_do # можно выводить более гламурно через journalctl | grep sysmon | sudo /opt/sysmon/sysmonLogView
№ п/п |
Решение |
Размер телеметрии |
~EPS из коробки |
Размер телеметрии при запуске правил/политик |
~EPS с адаптированными политиками |
1 |
|
81 Mb |
76,6 |
42.6 Mb |
26,9 |
2 |
|
86,9 Kb |
0,02 |
22.9 Mb |
8,7 |
3 |
|
1.9 Mb |
2,8
|
11.9 Mb |
18,1 |
4 |
|
17 Mb |
17,6 |
586 Kb |
17,3 |
5 |
auditd |
23 Kb |
0,04 |
8.1 Mb |
9,2 |
По объёму телеметрии явный фаворит kunai, за ним falco и tetragon. Далее будет понятно, почему: если взглянуть на события, то kunai старается предоставить полную телеметрию, у sysmon много уходит на xml‑разметку. Falco и tetragon также достаточно подробны. Первый в силу формата события (указание capabilities и сырого события в JSON занимает умеренный объём), второй в силу количества телеметрии шлёт достаточно много событий.
Уже на данном этапе можно прикинуть, насколько хосты готовы к таким «дружелюбным помощникам». Тут же можно отметить, что самый низкий EPS наблюдается у auditd и falco. Интересно всё же оценить качество телеметрии — переходим к следующему этапу.
По качеству телеметрии
Для экономии места под спойлерами вывожу только основные результаты (никому не интересно читать 60 событий выполнения одной команды, верно, kunai?). Для наглядности под спойлером привожу примеры событий всех инструментов до и после адаптации.
Примеры событий каждого инструмента
auditd
Из коробки |
После адаптации |
useradd alex5 | |
type=ADD_GROUP msg=audit(1781644087.170:3294): pid=172489 uid=0 auid=1000 ses=3 subj=unconfined msg='op=adding group acct=“alex5” exe=“/usr/sbin/useradd” hostname=vbox addr=? terminal=pts/1 res=success'UID=“root” AUID=“alex” |
type=SYSCALL msg=audit(1781865028.171:34726): arch=c000003e syscall=59 success=yes exit=0 a0=55ff5085fa60 a1=55ff5082f8d0 a2=55ff5082f370 a3=776c3e0e6ba73432 items=3 ppid=3080 pid=3287 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=3 comm=“useradd” exe=“/usr/sbin/useradd” subj=unconfined key=“process_creation”ARCH=x86_64 SYSCALL=execve AUID=“alex” UID=“root” GID=“root” EUID=“root” SUID=“root” FSUID=“root” EGID=“root” SGID=“root” FSGID=“root” type=EXECVE msg=audit(1781865028.171:34726): argc=2 a0=“useradd” a1=“alex5” type=CWD msg=audit(1781865028.171:34726): cwd=“/home/alex/ebpf” type=PATH msg=audit(1781865028.171:34726): item=0 name=“/usr/sbin/useradd” inode=669596 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID=“root” OGID=“root” type=PATH msg=audit(1781865028.171:34726): item=1 name=“/usr/sbin/useradd” inode=669596 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID=“root” OGID=“root” type=PATH msg=audit(1781865028.171:34726): item=2 name=“/lib64/ld‑linux‑x86-64.so.2” inode=716894 dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0OUID=“root” OGID=“root” type=PROCTITLE msg=audit(1781865028.171:34726): proctitle=7573657261646400616C657835 |
sysmonforlinux
Из коробки |
После адаптации |
useradd alex5 |
|
|
Jun 16 21:08:07 vbox sysmon[34389]: <Event> <System> <Provider Name=“Linux‑Sysmon” Guid=“{ff032593-a8d3-4f13-b0d6-01fc615a0f97}” /> <EventID>1</EventID> <Version>5</Version> <Level>4</Level> <Task>1</Task> <Opcode>0</Opcode> <Keywords>0×8000000000000000</Keywords> <TimeCreated SystemTime=“2026-06-16T21:08:07.052568000Z” /> <EventRecordID>2103979</EventRecordID> <Correlation /> <Execution ProcessID=“34389” ThreadID=“34389” /> <Channel>Linux‑Sysmon/Operational</Channel> <Computer>vbox</Computer> <Security UserId=“0” /> </System> <EventData> <Data Name=“RuleName”>‑</Data> <Data Name=“UtcTime”>2026-06-16 21:08:07.026</Data> <Data Name=“ProcessGuid”>{8f8f9db4-bb37-6a31-7970-3a2eea550000}</Data> <Data Name=“ProcessId”>172489</Data> <Data Name=“Image”>/usr/sbin/useradd</Data> <Data Name=“FileVersion”>‑</Data> <Data Name=“Description”>‑</Data> <Data Name=“Product”>‑</Data> <Data Name=“Company”>‑</Data> <Data Name=“OriginalFileName”>‑</Data> <Data Name=“CommandLine”>useradd alex5</Data> <Data Name=“CurrentDirectory”>/home/alex</Data> <Data Name=“User”>root</Data> <Data Name=“LogonGuid”>{8f8f9db4-0000-0000-0000-000001000000}</Data> <Data Name=“LogonId”>0</Data> <Data Name=“TerminalSessionId”>3</Data> <Data Name=“IntegrityLevel”>no level</Data> <Data Name=“Hashes”>SHA256=bd6dcd629c9526db40d3a6bf35dabb3aaa2bc74f2a7b1dbb93ce90b1eb6354f9</Data> <Data Name=“ParentProcessGuid”>{00000000–0000-0000-0000-000000000000}</Data> <Data Name=“ParentProcessId”>3190</Data> <Data Name=“ParentImage”>‑</Data> <Data Name=“ParentCommandLine”>‑</Data> <Data Name=“ParentUser”>‑</Data> </EventData> </Event>
|
Jun 19 10:30:28 vbox sysmon[917]: <Event> <System> <Provider Name=“Linux‑Sysmon” Guid=“{ff032593-a8d3-4f13-b0d6-01fc615a0f97}” /> <EventID>1</EventID> <Version>5</Version> <Level>4</Level> <Task>1</Task> <Opcode>0</Opcode> <Keywords>0×8000000000000000</Keywords> <TimeCreated SystemTime=“2026-06-19T10:30:28.202135000Z” /> <EventRecordID>2118596</EventRecordID> <Correlation /> <Execution ProcessID=“917” ThreadID=“917” /> <Channel>Linux‑Sysmon/Operational</Channel> <Computer>vbox</Computer> <Security UserId=“0” /> </System> <EventData> <Data Name=“RuleName”>TechniqueID=T1136.001,TechniqueName=Create Account: Local Account</Data> <Data Name=“UtcTime”>2026-06-19 10:30:28.176</Data> <Data Name=“ProcessGuid”>{8f8f9db4-1a44-6a35-79b0-254ff5550000}</Data> <Data Name=“ProcessId”>3287</Data> <Data Name=“Image”>/usr/sbin/useradd</Data> <Data Name=“FileVersion”>‑</Data> <Data Name=“Description”>‑</Data> <Data Name=“Product”>‑</Data> <Data Name=“Company”>‑</Data> <Data Name=“OriginalFileName”>‑</Data> <Data Name=“CommandLine”>useradd alex5</Data> <Data Name=“CurrentDirectory”>/home/alex/ebpf</Data> <Data Name=“User”>root</Data> <Data Name=“LogonGuid”>{8f8f9db4-0000-0000-0000-000001000000}</Data> <Data Name=“LogonId”>0</Data> <Data Name=“TerminalSessionId”>3</Data> <Data Name=“IntegrityLevel”>no level</Data> <Data Name=“Hashes”>SHA256=bd6dcd629c9526db40d3a6bf35dabb3aaa2bc74f2a7b1dbb93ce90b1eb6354f9</Data> <Data Name=“ParentProcessGuid”>{8f8f9db4-19db-6a35-9d5b‑c136ff550000}</Data> <Data Name=“ParentProcessId”>3080</Data> <Data Name=“ParentImage”>/usr/bin/bash</Data> <Data Name=“ParentCommandLine”>/bin/bash</Data> <Data Name=“ParentUser”>root</Data> </EventData> </Event>
|
falco
Из коробки |
После адаптации |
cat /etc/shadow |
|
|
{ “hostname”: “vbox”, “output”: “15:09:35.736804005: Warning Sensitive file opened for reading by non‑trusted program | file=/etc/shadow gparent=sudo ggparent=sudo gggparent=bash evt_type=openat user=root user_uid=0 user_loginuid=1000 process=cat proc_exepath=/usr/bin/cat parent=bash command=cat /etc/shadow terminal=34824 container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA>”, “output_fields”: { “container.id”: “host”, “container.image.repository”: “”, “container.image.tag”: “”, “container.name”: “host”, “evt.time”: 1781795375736804005, “evt.type”: “openat”, “fd.name”: “/etc/shadow”, “k8s.ns.name”: null, “k8s.pod.name”: null, “proc.aname[2]”: “sudo”, “proc.aname[3]”: “sudo”, “proc.aname[4]”: “bash”, “proc.cmdline”: “cat /etc/shadow”, “proc.exepath”: “/usr/bin/cat”, “proc.name”: “cat”, “proc.pname”: “bash”, “proc.tty”: 34824, “user.loginuid”: 1000, “user.name”: “root”, “user.uid”: 0 }, “priority”: “Warning”, “rule”: “Read sensitive file untrusted”, “source”: “syscall”, “tags”: [ “T1555”, “container”, “filesystem”, “host”, “maturity_stable”, “mitre_credential_access” ], “time”: “2026-06-18T15:09:35.736804005Z” }
|
{ “hostname”: “vbox”, “output”: “18:49:33.464215488: Informational Process started (execve root 0 1000 alex host host false CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_LINUX_IMMUTABLE CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_ADMIN CAP_NET_RAW CAP_IPC_LOCK CAP_IPC_OWNER CAP_SYS_MODULE CAP_SYS_RAWIO CAP_SYS_CHROOT CAP_SYS_PTRACE CAP_SYS_PACCT CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_NICE CAP_SYS_RESOURCE CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_MKNOD CAP_LEASE CAP_AUDIT_WRITE CAP_AUDIT_CONTROL CAP_SETFCAP CAP_MAC_OVERRIDE CAP_MAC_ADMIN CAP_SYSLOG CAP_WAKE_ALARM CAP_BLOCK_SUSPEND CAP_AUDIT_READ CAP_PERFMON CAP_BPF CAP_CHECKPOINT_RESTORE <NA> /usr/bin/sudo /usr/bin/sudo /usr/bin/bash /usr/libexec/gnome‑terminal‑server 3050 3049 2955 2929 4 969 662 3050 34 820 /home/alex/ebpf/ 54 689 /usr/bin/cat cat cat cat /etc/shadow 3051 /usr/bin/bash /bin/bash bash bash /root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin) container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA>”, “output_fields”: { “container.id”: “host”, “container.image”: “”, “container.image.id”: “”, “container.image.repository”: “”, “container.image.tag”: “”, “container.mounts”: “”, “container.name”: “host”, “container.privileged”: false, “evt.time”: 1781808573464215488, “evt.type”: “execve”, “k8s.ns.name”: null, “k8s.pod.name”: null, “proc.aexepath[2]”: “/usr/bin/sudo”, “proc.aexepath[3]”: “/usr/bin/sudo”, “proc.aexepath[4]”: “/usr/bin/bash”, “proc.aexepath[5]”: “/usr/libexec/gnome‑terminal‑server”, “proc.apid[2]”: 3050, “proc.apid[3]”: 3049, “proc.apid[4]”: 2955, “proc.apid[5]”: 2929, “proc.cmdline”: “cat /etc/shadow”, “proc.cwd”: “/home/alex/ebpf/”, “proc.duration”: 4969662, “proc.env[PATH]”: “/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin”, “proc.exe”: “cat”, “proc.exepath”: “/usr/bin/cat”, “proc.name”: “cat”, “proc.pcmdline”: “bash”, “proc.pexe”: “/bin/bash”, “proc.pexepath”: “/usr/bin/bash”, “proc.pid”: 54689, “proc.pname”: “bash”, “proc.ppid”: 3051, “proc.sid”: 3050, “proc.tty”: 34820, “thread.cap_effective”: “CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_LINUX_IMMUTABLE CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_ADMIN CAP_NET_RAW CAP_IPC_LOCK CAP_IPC_OWNER CAP_SYS_MODULE CAP_SYS_RAWIO CAP_SYS_CHROOT CAP_SYS_PTRACE CAP_SYS_PACCT CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_NICE CAP_SYS_RESOURCE CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_MKNOD CAP_LEASE CAP_AUDIT_WRITE CAP_AUDIT_CONTROL CAP_SETFCAP CAP_MAC_OVERRIDE CAP_MAC_ADMIN CAP_SYSLOG CAP_WAKE_ALARM CAP_BLOCK_SUSPEND CAP_AUDIT_READ CAP_PERFMON CAP_BPF CAP_CHECKPOINT_RESTORE”, “user.loginname”: “alex”, “user.loginuid”: 1000, “user.name”: “root”, “user.uid”: 0 }, “priority”: “Informational”, “rule”: “Process started — generic_user_execution”, “source”: “syscall”, “tags”: [ “4rays”, “eventid_1”, “generic_user_execution”, “proc_execution” ], “time”: “2026-06-18T18:49:33.464215488Z” } { “hostname”: “vbox”, “output”: “18:49:33.467328839: Warning Sensitive file opened for reading by non‑trusted program | file=/etc/shadow gparent=sudo ggparent=sudo gggparent=bash evt_type=openat user=root user_uid=0 user_loginuid=1000 process=cat proc_exepath=/usr/bin/cat parent=bash command=cat /etc/shadow terminal=34820 container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA>”, “output_fields”: { “container.id”: “host”, “container.image.repository”: “”, “container.image.tag”: “”, “container.name”: “host”, “evt.time”: 1781808573467328839, “evt.type”: “openat”, “fd.name”: “/etc/shadow”, “k8s.ns.name”: null, “k8s.pod.name”: null, “proc.aname[2]”: “sudo”, “proc.aname[3]”: “sudo”, “proc.aname[4]”: “bash”, “proc.cmdline”: “cat /etc/shadow”, “proc.exepath”: “/usr/bin/cat”, “proc.name”: “cat”, “proc.pname”: “bash”, “proc.tty”: 34820, “user.loginuid”: 1000, “user.name”: “root”, “user.uid”: 0 }, “priority”: “Warning”, “rule”: “Read sensitive file untrusted”, “source”: “syscall”, “tags”: [ “T1555”, “container”, “filesystem”, “host”, “maturity_stable”, “mitre_credential_access” ], “time”: “2026-06-18T18:49:33.467328839Z” }
|
tetragon
Из коробки |
После адаптации |
cat /etc/shadow |
|
|
{ “process_exec”: { “process”: { “exec_id”: “dmJveDozODMxMzA5MzY3MDAyMjoxNzI0ODg=”, “pid”: 172488, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/usr/bin/cat”, “arguments”: “/etc/shadow”, “flags”: “execve clone”, “start_time”: “2026-06-16T21:08:06.956354178Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyNTk1OTI3MjcwMTQ6MzE5MA==”, “tid”: 172488, “in_init_tree”: false }, “parent”: { “exec_id”: “dmJveDoyNTk1OTI3MjcwMTQ6MzE5MA==”, “pid”: 3190, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/bin/bash”, “flags”: “execve clone”, “start_time”: “2026-06-16T10:33:53.455411052Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyNTk1ODk2MTI4MTU6MzE4OQ==”, “tid”: 3190, “in_init_tree”: false } }, “node_name”: “vbox”, “time”: “2026-06-16T21:08:06.956353172Z” } { “process_exit”: { “process”: { “exec_id”: “dmJveDozODMxMzA5MzY3MDAyMjoxNzI0ODg=”, “pid”: 172488, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/usr/bin/cat”, “arguments”: “/etc/shadow”, “flags”: “execve clone”, “start_time”: “2026-06-16T21:08:06.956354178Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyNTk1OTI3MjcwMTQ6MzE5MA==”, “tid”: 172488, “in_init_tree”: false }, “parent”: { “exec_id”: “dmJveDoyNTk1OTI3MjcwMTQ6MzE5MA==”, “pid”: 3190, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/bin/bash”, “flags”: “execve clone”, “start_time”: “2026-06-16T10:33:53.455411052Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyNTk1ODk2MTI4MTU6MzE4OQ==”, “tid”: 3190, “in_init_tree”: false }, “time”: “2026-06-16T21:08:07.017303824Z” }, “node_name”: “vbox”, “time”: “2026-06-16T21:08:07.017302862Z” }
|
{ “process_exec”: { “process”: { “exec_id”: “dmJveDo2ODg4MzMwOTE2MTY0OjU0Njg5”, “pid”: 54689, “uid”: 0, “cwd”: “/home/alex/ebpf”, “binary”: “/usr/bin/cat”, “arguments”: “/etc/shadow”, “flags”: “execve clone”, “start_time”: “2026-06-18T18:49:33.464109764Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyMjM3ODkzOTM4MzI6MzA1MQ==”, “tid”: 54689, “in_init_tree”: false }, “parent”: { “exec_id”: “dmJveDoyMjM3ODkzOTM4MzI6MzA1MQ==”, “pid”: 3051, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/bin/bash”, “flags”: “execve clone”, “start_time”: “2026-06-18T16:58:28.922586153Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyMjM3ODYzODM1MTY6MzA1MA==”, “tid”: 3051, “in_init_tree”: false } }, “node_name”: “vbox”, “time”: “2026-06-18T18:49:33.464108782Z” } { “process_exit”: { “process”: { “exec_id”: “dmJveDo2ODg4MzMwOTE2MTY0OjU0Njg5”, “pid”: 54689, “uid”: 0, “cwd”: “/home/alex/ebpf”, “binary”: “/usr/bin/cat”, “arguments”: “/etc/shadow”, “flags”: “execve clone”, “start_time”: “2026-06-18T18:49:33.464109764Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyMjM3ODkzOTM4MzI6MzA1MQ==”, “tid”: 54689, “in_init_tree”: false }, “parent”: { “exec_id”: “dmJveDoyMjM3ODkzOTM4MzI6MzA1MQ==”, “pid”: 3051, “uid”: 0, “cwd”: “/home/alex”, “binary”: “/bin/bash”, “flags”: “execve clone”, “start_time”: “2026-06-18T16:58:28.922586153Z”, “auid”: 1000, “parent_exec_id”: “dmJveDoyMjM3ODYzODM1MTY6MzA1MA==”, “tid”: 3051, “in_init_tree”: false }, “time”: “2026-06-18T18:49:33.498521958Z” }, “node_name”: “vbox”, “time”: “2026-06-18T18:49:33.498519994Z” }
|
kunai
Из коробки |
После адаптации |
cat /etc/shadow — 4 события |
|
|
{ “data”: { “ancestors”: “/usr/lib/systemd/systemd|/usr/lib/systemd/systemd|/usr/libexec/gnome‑terminal‑server|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo|/usr/bin/bash”, “parent_command_line”: “/bin/bash”, “parent_exe”: “/usr/bin/bash”, “command_line”: “cat /etc/shadow”, “exe”: { “path”: “/usr/bin/cat”, “md5”: “7a4179e324c784b99e98fedee05260f7”, “sha1”: “827602d01c310784544309212d9eda4eb9f89904”, “sha256”: “008f819498fe591f3cc920d543709347d8d14a139bb3482bc2cd8635c1b3162e”, “sha512”: “8546f03b16577453ef84ae532018b8ece28552f4ce55b03d399c14b548992b7037be8f7108c0887e1357500398b724de2efbf265169468e03219a6d3d8583072”, “size”: 44016, “error”: null } }, “info”: { “host”: { “uuid”: “a8071456-38e6-591e-8eea‑e34c038d681c”, “name”: “vbox”, “container”: null }, “event”: { “source”: “kunai”, “id”: 1, “name”: “execve”, “uuid”: “d4087d12-cc85-83ad‑dc31-c5726b2e732f”, “batch”: 779 }, “task”: { “name”: “cat”, “pid”: 172488, “tgid”: 172488, “guuid”: “a34e8575-d822-0000-9546-2b58c8a10200”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400000”, “zombie”: false }, “parent_task”: { “name”: “bash”, “pid”: 3190, “tgid”: 3190, “guuid”: “6cede370-3c00-0000-9546-2b58760c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400100”, “zombie”: false }, “utc_time”: “2026-06-16T21:08:06.984252062Z” } } { “data”: { “ancestors”: “/usr/lib/systemd/systemd|/usr/lib/systemd/systemd|/usr/libexec/gnome‑terminal‑server|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo|/usr/bin/bash”, “command_line”: “cat /etc/shadow”, “exe”: { “path”: “/usr/bin/cat” }, “mapped”: { “path”: “/usr/lib/x86_64-linux‑gnu/libc.so.6”, “md5”: “41ee2cdd791957eab1c3302878c739a0”, “sha1”: “f46446ad3de6fc83681dbdec21c5ac7e9fafd37b”, “sha256”: “bff8750fe719e6000791b88b11747dce8772c37118d0b2348044b70819d13835”, “sha512”: “45d4aa6817d69e82522a58a8152ea97b5bce1d7e9d473bdedd5283fd828c21bbf92cd939bd10c327bc2a42b5c39d212d8bd1577c9ac99c987df4994c8700d7b4”, “size”: 1926232, “error”: null } }, “info”: { “host”: { “uuid”: “a8071456-38e6-591e-8eea‑e34c038d681c”, “name”: “vbox”, “container”: null }, “event”: { “source”: “kunai”, “id”: 41, “name”: “mmap_exec”, “uuid”: “3d139cc5-12cb-4fe0-af77-3da20f4ac25a”, “batch”: 780 }, “task”: { “name”: “cat”, “pid”: 172488, “tgid”: 172488, “guuid”: “a34e8575-d822-0000-9546-2b58c8a10200”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400000”, “zombie”: false }, “parent_task”: { “name”: “bash”, “pid”: 3190, “tgid”: 3190, “guuid”: “6cede370-3c00-0000-9546-2b58760c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400100”, “zombie”: false }, “utc_time”: “2026-06-16T21:08:06.984286631Z” } } { “data”: { “ancestors”: “/usr/lib/systemd/systemd|/usr/lib/systemd/systemd|/usr/libexec/gnome‑terminal‑server|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo|/usr/bin/bash”, “command_line”: “cat /etc/shadow”, “exe”: { “path”: “/usr/bin/cat” }, “path”: “/etc/shadow” }, “info”: { “host”: { “uuid”: “a8071456-38e6-591e-8eea‑e34c038d681c”, “name”: “vbox”, “container”: null }, “event”: { “source”: “kunai”, “id”: 82, “name”: “read_config”, “uuid”: “642138a0-0c68-5fbb-2b29-f024121f8bfa”, “batch”: 781 }, “task”: { “name”: “cat”, “pid”: 172488, “tgid”: 172488, “guuid”: “a34e8575-d822-0000-9546-2b58c8a10200”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400000”, “zombie”: false }, “parent_task”: { “name”: “bash”, “pid”: 3190, “tgid”: 3190, “guuid”: “6cede370-3c00-0000-9546-2b58760c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400100”, “zombie”: false }, “utc_time”: “2026-06-16T21:08:07.087288573Z” } }
{ “data”: { “ancestors”: “/usr/lib/systemd/systemd|/usr/lib/systemd/systemd|/usr/libexec/gnome‑terminal‑server|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo|/usr/bin/bash”, “command_line”: “cat /etc/shadow”, “exe”: { “path”: “/usr/bin/cat” }, “error_code”: 0 }, “info”: { “host”: { “uuid”: “a8071456-38e6-591e-8eea‑e34c038d681c”, “name”: “vbox”, “container”: null }, “event”: { “source”: “kunai”, “id”: 5, “name”: “exit_group”, “uuid”: “9487704a‑c503-2f78-e9cc-8067ae62e946”, “batch”: 785 }, “task”: { “name”: “cat”, “pid”: 172488, “tgid”: 172488, “guuid”: “a34e8575-d822-0000-9546-2b58c8a10200”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400000”, “zombie”: false }, “parent_task”: { “name”: “bash”, “pid”: 3190, “tgid”: 3190, “guuid”: “6cede370-3c00-0000-9546-2b58760c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400100”, “zombie”: false }, “utc_time”: “2026-06-16T21:08:07.091970166Z” } }
|
{ “data”: { “ancestors”: “/usr/lib/systemd/systemd|/usr/lib/systemd/systemd|/usr/libexec/gnome‑terminal‑server|/usr/bin/bash|/usr/bin/sudo|/usr/bin/sudo|/usr/bin/bash”, “parent_command_line”: “/bin/bash”, “parent_exe”: “/usr/bin/bash”, “command_line”: “cat /etc/shadow”, “exe”: { “path”: “/usr/bin/cat”, “magic”: “ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV)”, “md5”: “7a4179e324c784b99e98fedee05260f7”, “sha1”: “827602d01c310784544309212d9eda4eb9f89904”, “sha256”: “008f819498fe591f3cc920d543709347d8d14a139bb3482bc2cd8635c1b3162e”, “sha512”: “8546f03b16577453ef84ae532018b8ece28552f4ce55b03d399c14b548992b7037be8f7108c0887e1357500398b724de2efbf265169468e03219a6d3d8583072”, “size”: 44016, “error”: null } }, “detection”: { “rules”: [ “bin_system_shell.execve.detection”, “bin_daily_cmd.execve.detection” ], “tags”: [ “type_detection”, “event_execve_script”, “event_execve”, “command_and_scripting_interpreter:_unix_shell”, “bin_daily_cmd”, “T1059.004”, “bin_system_shell” ], “attack”: [ “T1059.004” ], “severity”: 6 }, “info”: { “host”: { “uuid”: “c030b40d-0eab-417b‑b33a-22d952357984”, “name”: “vbox”, “container”: null }, “event”: { “source”: “kunai”, “id”: 1, “name”: “execve”, “uuid”: “043624d0-0ebf‑db06-42bf-5940432445a8”, “batch”: 445 }, “task”: { “name”: “cat”, “pid”: 3252, “tgid”: 3252, “guuid”: “81a8f5f5-4e00-0000-c6a6-4f77b40c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400000”, “zombie”: false }, “parent_task”: { “name”: “bash”, “pid”: 3212, “tgid”: 3212, “guuid”: “027273b7-4400-0000-c6a6-4f778c0c0000”, “uid”: 0, “user”: “root”, “gid”: 0, “group”: “root”, “namespaces”: { “mnt”: 4 026 531 841 }, “flags”: “0×400100”, “zombie”: false }, “utc_time”: “2026-06-19T13:49:43.139227876Z” } }
|
Сценарий 1: запуск процессов
Инструмент |
Фиксация запуска процессов (execve) |
Детектирование специфичной активности |
auditd |
Да (после адаптации). Логирует запуск всех бинарей, но командная строка часто в HEX. Родительский процесс указывается в виде PID, но не расшифровывается. |
Да. Отдельно выделяет логические события (например, ADD_GROUP при useradd). Ловит syscall ptrace при strace. |
sysmon |
Да. Фиксирует запуск из коробки, но адаптированная конфигурация теряет значительную часть событий. Структура XML избыточна, много пустых полей. |
Нет. Только факты запуска бинарей. Из плюсов — считает хеши запускаемых файлов (SHA256), немного тегирует MITRE (TechniqueName). |
falco |
Да. Логирует запуск, выводит командную строку в читаемом виде. Строит дерево процессов (ancestors). Телеметрия избыточна за счет вывода всех capabilities. |
Да. Детектирует чтение /etc/shadow (Rule: Read sensitive file untrusted) и использование ptrace при strace (Rule: PTRACE attached to process). Тегирует MITRE. |
tetragon |
Да. Фиксирует как запуск (process_exec), так и завершение (process_exit) процессов. Параметры родителя закодированы в base64. Данные не обогащаются (только UID). |
Да. Единственный кроме kunai, кто четко показал, что процесс sleep 60 завершился принудительно по сигналу SIGKILL. |
kunai |
Да. Суперподробная телеметрия: пишет не только сам запуск, но и загрузку всех библиотек (mmap_exec). Считает 4 вида хешей и определяет формат файла по magic bytes. Строит полную цепочку процессов. |
Да. Отдельным событием пишет сигнал kill с указанием цели. При strace тегирует MITRE T1049, T1082. |
Auditd. Здесь пришлось продраться сквозь HEX’ы, что немного затрудняет анализ. Однако видим, что после применения расширенной конфигурации аудита события появились, и что самое главное — мы видим командную строку (и в SYSCALL, и в EXECVE и в PROCTITLE, если преобразовать из HEX). Команда history не имеет исполняемого файла, а запускается напрямую через интерпретатор bash, поэтому в явном виде в логах мы её не увидим (но честно, хотелось бы). Auditd отдельно выделил логическое событие создания нового пользователя в ADD_GROUP, что облегчает мониторинг.
Sysmon. В данном случае адаптация конфигурации явно не пошла ему на пользу. Часть ранее фиксируемых событий была утеряна. Взамен в новых событиях получили небольшой MITRE‑маппинг. В целом есть куча бесполезных полей (opcode, keywords, guid), многие поля пустые, что особенно обидно при запуске процессов от root — что он в явном виде не указывается. Поэтому в целом хорошо, что телеметрия собираться будет, но к её качеству есть много вопросов. Из полезного — наличие хеша исполняемого файла.
Falco. Есть возможность добавлять информационные сообщения в ивенты (эдакий мини‑сием), в том числе и теги сработок. Телеметрия достаточно полная, хотя сама выполненная команда в сыром output не экранирована, что немного смущает. Возможности (capabilities) съедают значительное место в логе. Вывод в JSON включает в себя, по сути, дублирование сырого события и его JSON‑раскладки по полям.
Tetragon. В первую очередь это средство трассирования, поэтому для него характерно наличие событий как для создания (запуска) процесса, так и для его завершения. Параметры родительских процессов закодированы в base64. При этом данные не обогащаются — то есть мы не видим имя пользователя, а только его цифровой идентификатор. Исполняемый файл и аргументы его запуска разнесены в два поля, что поначалу может несколько путать — хотя иногда это удобнее, чем разнесение каждого аргумента в отдельное поле, как в случае с auditd. Однако формат телеметрии статичен и дополнительных ИБ‑приколов здесь нет. Также не всегда интуитивно понятен результат выполнения завершения процессов (успех/неудача).
Kunai. Это настоящий монстр телеметрии. Тут есть и анализ заголовка исполняемого файла, и расчёт нескольких хешей от него же. Есть обогащение цифровых идентификаторов их символьными значениями (имена и группы пользователей). В расширенном аудите появляется обогащение тегами, в том числе и по техникам и тактикам MITRE. Приятно, что, если событие попадает под несколько правил, теги каждого из них будут добавлены в результирующее событие. Это выгодно отличается от ситуаций, когда 1 событие может вызвать шквал алёртов от 10 независимых корреляшек. Или наоборот (auditd) — когда 1 событие идёт по цепочке и алёрт производится только для первого подходящего правила‑фильтра, а остальные фильтры уже не применяются для этого события. Встроенный и расширенный маппинг на MITRE позволяет в том числе отслеживать логические операции (создание пользователя, например). Также kunai отображает полную цепочку процессов.
Вывод по сценарию 1
В первом сценарии мы наблюдаем следующую картину: все испытуемые в расширенном режиме (кроме «улучшенного» Sysmon — наглядный урок, что не стоит всё подряд из интернета тянуть в свою инфраструктуру) смогли задетектировать запуск процессов. Falco, kunai, sysmon и частично auditd позволяют тегировать события или предоставлять обогащённую информацию о событии, в том числе и с точки зрения информационной безопасности. Конечно, в отдельных случаях (трассировка) объём собираемой телеметрии был избыточен. С точки зрения безопасности важнее получить факт запуска трассировки процесса для дальнейшего анализа, чем каждое событие об этой трассировке, коих могут быть сотни и тысячи.
По объёму телеметрии: у sysmon она была самой неинформативной (но был хеш процесса), у tetragon сухая констатация фактов, falco и kunai дают гиперзначительное количество телеметрии, auditd (если абстрагироваться от необходимости ручной агрегации событий) даёт базовый уровень телеметрии, достаточной для работы.
Тем не менее, tetragon и kunai — единственные инструменты в первом сценарии, которые чётко показали, что процесс «sleep 60» завершился не сам, а был принудительно завершён (signal: SIGKILL).
Сценарий 2: взаимодействие с сетью

Инструмент |
Фиксация запуска сетевых утилит |
Фиксация сетевого соединения (IP/Port) |
auditd |
Да (после адаптации). Фиксирует только факт запуска процесса (например, comm=“curl”). |
Нет. В логах фиксируется сам syscall execve, но IP‑адреса, порта или факта установки соединения в базовом выводе нет. (можно настроить отдельно) |
sysmon |
Да (из коробки). Адаптированная политика, как и в первом сценарии, потеряла часть событий (например, nslookup и nc). |
Нет. Фиксируется только запуск бинаря с параметрами. Сетевой телеметрии в предоставленных логах нет. |
falco |
Да. Логирует запуск утилит. |
Да. Лучший результат в этом сценарии. Показывает реальные сетевые соединения с IP‑адресами, портами и протоколом. |
tetragon |
Да. Пишет запуск и завершение утилит. |
Частично. Поймал неудачное завершение nslookup azaza.comet (status: 1), но IP‑адресов и портов в логах не резолвит. |
kunai |
Да. Логирует запуск утилит, подтягивает библиотеки (например, libcurl.so). |
Нет. Как и sysmon/auditd, ограничился фиксацией execve и mmap_exec. Сетевых соединений в предоставленных логах не зафиксировано. |
Auditd. Зафиксировал командную строку процесса — но запуск именно процесса, а не сетевого взаимодействия (например, события типа SOCKADDR). Это можно скорректировать за счёт адаптации набора правил при необходимости — однако обычно события на sysсall типа socket, listen, accept4, connect, bind сложно отфильтровывать. В события PATH всегда попадает библиотека для запуска бинарей, что в целом только захламляет лог.
Sysmon. Тут тоже появились проблемы с адаптированной политикой и исчезновением сетевых событий. В целом уже по результатам первого сценария было понятно, что sysmon является хорошим инструментов для определения фактов запуска процессов с минимальной телеметрией. Но далее рассматривать его как серьёзное решение не представляется возможным.
Falco. Здесь телеметрия проявила себя в полном объёме, зафиксировав все запуски команд и частично — сетевые коммуникации согласно политике. Честно признаюсь, хотелось бы всё же их как‑то агрегировать, чтобы не заспамливаться событиями, но что есть, то есть. Если исключить именно события запуска процессов, то событий, указывающих на сетевое взаимодействие, будет значительно меньше.
Tetragon. По аналогии с первым сценарием — зафиксирован запуск и окончание процессов. Нет резолвов хостнеймов (у auditd и falco в логах DNS‑имена из команд хотя бы присутствовали).
Kunai. Инструмент отработал как tetragon с кучей дополнительной телеметрии.
Из собранных событий видно, что только falco смог сразу показать наличие сетевого соединения с определением IP‑адресов — получились самые полезные события (10.0.2.15 → 195.208.4.1:53 UDP). Никто не уловил активацию порта в режиме прослушивания (nc). Tetragon единственный, кто смог показать неудачное выполнение nslookup для несуществующего хоста (status 1). Адаптированный kunai лучше всех атрибутировал выполнение по MITRE, хотя и немного подбешивал указанием каждой вызываемом библиотеки. Заметка будущему себе — анализировать именно вызов известных библиотек, а не инструментов и бинарей (условно детектим не запуск редактора файла из списка, а завершённую запись в файл). Что характерно, практически на все команды kunai до адаптации выдавал около 52 событий, после этого — в среднем около 30 событий. Да, это немного, но похоже на честную работу.
Поэтому в этом раунде с учётом адаптированных, но не протюнингованных политик (специально вышколенных под сеть), похоже, победил falco.
Сценарий 3: операции с файлами
Инструмент |
Создание файла (echo >) |
Создание файла (nano)
|
Удаление файла (rm / shred) |
Дамп памяти (gcore) |
MITRE ATT&CK |
auditd |
Да (openat + CREATE) |
Нет — запуск процесса |
Нет (только запуск rm/shred) |
Нет (только запуск sudo) |
Нет |
sysmon |
Нет |
Нет — запуск процесса |
Нет (только запуск rm/shred) |
Нет (только запуск sudo) |
Нет |
falco |
Нет |
Нет — запуск процесса |
Да (Rule: Remove Bulk Data) |
Да (Rule: PTRACE attached) |
Да (T1485, T1055.008) |
tetragon |
Нет |
Нет — запуск процесса |
Нет (только запуск rm/shred) |
Нет (только запуск sudo/gcore) |
Нет |
kunai |
Да (Event: file_create) |
Нет — запуск процесса |
Да (Tags: T1485, T1070.004) |
Нет (запуск sudo + сигнал SIGURG) |
Да (T1485, T1070.004) |
Auditd. Зафиксировал создание файла (через openat системный вызов + PATH с CREATE). Справился с определением выполняемых команд, однако без какой‑либо полезной атрибуции. Единственный выдавал inodes, что может быть востребовано при расследовании инцидентов.
Sysmon. Только запуск процессов.
Falco. В случае с shred выдал желаемый алёрт «Warning Bulk data has been removed from disk», аналогичное поведение для начала создания дампа памяти процесса.
Tetragon. Сухой запуск процессов.
Kunai. Результаты аналогичны прошлому сценарию — супербогатый телеметрией лог, однако здесь уже обилие тегов на событии скорее запутывает. Например, для запуска shred:
"detection": { "rules": [ "bin_system_wiper.execve.detection", "bin_system_shell.execve.detection" ], "tags": [ "indicator_removal:_file_deletion", "command_and_scripting_interpreter:_unix_shell", "bin_system_wiper", "bin_system_shell", "data_destruction", "T1485", "T1059.004", "event_execve_script", "T1070.004", "type_detection", "event_execve" ], "attack": [ "T1070.004", "T1485", "T1059.004" ]
В таком случае аналитику SOC нужно будет резко понять, что же происходит — конкретно здесь можно было бы минимизировать теги. До адаптации на трассировку было 160 событий, после адаптации — всего 101 событие.
Тут доля разочарования была повышена. Только falco выдал что‑то интересное, однако кроме auditd и kunai (до адаптации) никто не зафиксировал создание файла первой башевской командой. То есть технически через eBPF можно настроить сбор событий такого типа, тело в конфигурации и количестве ложных событий. С удалением файла также справились только kunai и falco. С созданием дампа памяти процесса также лучше отработал именно falco. Звучит как простая реклама 90-х, но интересно, как испытуемые себя поведут при определении подмены исполняемого файла в следующем сценарии.
Сценарий 4: изменение файлов и атрибутов
Инструмент |
Подмена бинаря (mv/cp) |
Изменение /etc/sudoers |
Редактирование (nano) |
Изменение атрибутов (chattr) |
Наличие хешей |
MITRE ATT&CK |
auditd |
Да (запуск mv/cp) |
Да (openat + флаги) |
Нет (запуск процесса) |
Да (запуск chattr) |
Нет |
Нет |
sysmon |
Да (запуск mv/cp) |
Нет (не видит echo) |
Нет (запуск процесса)
|
Да (запуск chattr) |
Да |
Нет |
falco |
Да (запуск mv/cp) |
Да (Trigger: File modified) |
Нет (запуск процесса)
|
Да (запуск chattr) |
Нет |
Да (в тегах) |
tetragon |
Да (запуск mv/cp) |
Нет |
Нет (запуск процесса)
|
Да (запуск chattr) |
Нет |
Нет |
kunai |
Да (Trigger: file_rename) |
Да (Trigger: write_config) |
Нет (запуск процесса)
|
Да (Trigger + MITRE T1222) |
Да (MD5, SHA1, 256, 512) |
Да (T1222, T1059) |
Auditd. Фиксируются запуски процессов. Изменения в sudoers — фиксировал только открытие файла с флагом на запись (openat + аргумент a2=441). Изменения атрибутов уловил на уровне выполнения команды.
Sysmon. Тут слишком жалкое зрелище. Но из полезного — при запуске модифицированного md5sum (который на самом деле sha256sum), есть хеш. Понятно, что сравнивать его с эталоном — не проблема sysmon.
Falco. Напрямую не уловил прикола с подменой исполняемого файла. Определил создание/модификацию файла sudoers в том числе через capabilities (O_APPEND/O_WRONGLY).
Tetragon. Выдаёт полную цепочку процессов, то есть в целом можно при анализе логов восстановить последовательность запуска. Какой‑либо дополнительной телеметрии мы тут не видим.
Kunai. До адаптации отловил событие переименования файла md5sum и запись в файл sudoers через bash (write_config). После адаптации стал тегировать nano как редактор (по сути — списочная активность). Однако достаточно шумит при операциях cp/mv. Тут были отдельные правила для chattr, хотя лично я бы установку immutable бита сразу выделял в отдельную аномальную категорию — это простой и эффективный способ как для остановки логов, так и для записи истории команд.
В результате помочь определить подмену исполняемого файла могут sysmon и kunai. Модификацию важного конфигурационного файла sudoers не отследили — да, файл открыт с правами на запись, но была ли совершена модификация файла?

Сценарий 5: вход пользователя
Инструмент |
Фиксация запуска su/ssh |
Событие аутентификации (PAM) |
Факт сетевого подключения (SSH) |
Результат аутентификации |
auditd |
Да |
Да (USER_AUTH, USER_LOGIN) |
Да (socket) |
Да (res=success) |
sysmon |
Да |
Нет |
Нет |
Нет |
falco |
Да |
Нет |
Да (Outbound UDP connection) |
Нет |
tetragon |
Да (видна цепочка) |
Нет |
Нет |
Нет |
kunai |
Да (MITRE T1059.004) |
Нет |
Нет |
Нет |
Auditd. На вход пользователя сразу сыпятся события типа USER_* и CRED_*, что достаточно удобно и не нуждается в дополнительной фильтрации. Из минусов — на простейшее подключение по SSH генерится несколько десятков событий. Единственный, кто выдаёт результат аутентификации (успех/отказ).
Sysmon. События уровня запуска команд, не более того.
Falco. В данном случае вход пользователя — это просто запуск процессов, в случае с SSH было ещё событие удалённого сетевого подключения. Но поскольку события входа пользователя нам не показали — это минус.
Tetragon. Просто логгер, почти как sysmon.
Kunai. В случае повышения привилегий/входа пользователя kunai несколько разочаровал, в адаптированном варианте ничего релевантного по сути. До адаптации зарегистрирован запуск процесса, но без интерпретации.
Из плюсов — все инструменты задетектировали запуск бинарных файлов (su/ssh) и актуальный ID пользователя. Через цепочки процессов можно отследить в полном логе порядок выполнения. В этом сценарии фаворит однозначно auditd.
Сценарий 6: docker
Инструмент |
Фиксация команды |
ID / Имя контейнера |
Образ контейнера |
Сетевое соединение |
MITRE ATT&CK |
auditd |
Да |
Нет (только subj=docker‑default) |
Нет |
Нет |
Нет |
sysmon |
Да |
Нет |
Нет |
Нет |
Нет |
falco |
Да |
Да (ID + Имя) |
Да (alpine:latest) |
Нет |
Да (TA000) |
tetragon |
Да |
Да (ID) |
Нет |
Да (IP:Port) |
Нет |
kunai |
Да |
Да (ID + Имя) |
Нет |
Нет |
Да (T1071.001) |
Здесь мы смотрим, насколько телеметрия позволяет определить совершённое в контейнере действие. В первую очередь ожидаем увидеть, смогли ли наши логгеры понять, что активность относится к определённому контейнеру.
Auditd. Смог поймать выполненную команду от имени «docker‑default». Однако без возможности понять, что это за контейнер — других признаков докера не нашлось. Технически забавная ситуация: «— команда была? — была. — на хосте? — на хосте. — ты залогировал? — залогировал. — где докер? — какой докер?». Вроде активность не пропустил, но критически важный контекст установить тоже не получится. Да и при настройке запуска от пользователя с другим именем, установить истину видимо вообще не получится.
Sysmon. Из коробки отловил выполнение команды, но без каких‑либо признаков контейнеризации.
Falco. Этот перец сразу понял, что запуск был в контейнере — есть краткий id, имя, версия контейнера и репозиторий. Также отработало правило выполнения исполняемого файла в контейнере:
{ "hostname": "vbox", "output": "11:38:58.371305814: Critical Executing binary not part of base image | proc_exe=curl proc_sname=sh gparent=containerd-shim proc_exe_ino_ctime=1781868858903844054 proc_exe_ino_mtime=1778694179000000000 proc_exe_ino_ctime_duration_proc_start=279467163971 proc_cwd=/ container_start_ts=1781868841197274895 evt_type=execve user=root user_uid=0 user_loginuid=-1 process=curl proc_exepath=/usr/bin/curl parent=sh command=curl https://ifconfig.me terminal=34816 exe_flags=EXE_WRITABLE|EXE_UPPER_LAYER container_id=c32fafe0ccad container_name=zen_pascal container_image_repository=alpine container_image_tag=latest k8s_pod_name=<NA> k8s_ns_name=<NA>", "output_fields": { "container.id": "c32fafe0ccad", "container.image.repository": "alpine", "container.image.tag": "latest", "container.name": "zen_pascal", "container.start_ts": 1781868841197274895, "evt.arg.flags": "EXE_WRITABLE|EXE_UPPER_LAYER", "evt.time": 1781869138371305814, "evt.type": "execve", "k8s.ns.name": null, "k8s.pod.name": null, "proc.aname[2]": "containerd-shim", "proc.cmdline": "curl https://ifconfig.me", "proc.cwd": "/", "proc.exe": "curl", "proc.exe_ino.ctime": 1781868858903844054, "proc.exe_ino.ctime_duration_proc_start": 279467163971, "proc.exe_ino.mtime": 1778694179000000000, "proc.exepath": "/usr/bin/curl", "proc.name": "curl", "proc.pname": "sh", "proc.sname": "sh", "proc.tty": 34816, "user.loginuid": -1, "user.name": "root", "user.uid": 0 }, "priority": "Critical", "rule": "Drop and execute new binary in container", "source": "syscall", "tags": ["PCI_DSS_11.5.1", "TA0003", "container", "maturity_stable", "mitre_persistence", "process"], "time": "2026-06-19T11:38:58.371305814Z" }
Tetragon. Тут уже указывается полный id контейнера, на этом всё. Неплохо отработал по сети — был контакт.
Kunai. Указывает краткий id контейнера, нет четких правил, что это произошло в контейнере.
Здесь без вопросов я отдаю предпочтение falco — больше всего релевантной для аналитика SOC телеметрии.
Иные критерии: впечатления от испытаний и инструментов
В первую очередь при работе с eBPF‑решениями следует учитывать поддержку ядер операционной системы. Так, kunai требует 5.4+ версии, и в испытаниях были жалобы на излишнюю новизну моего Debian. Tetragon стартует от версий 4.19, falco 4+, sysmon 4.15. Для auditd такой вопрос, понятно, не ставится.
Второй момент — скорость инициализации (применения политик) и стабильность работы. Auditd и sysmon обновлялись на лету, tetragon мог несколько минут инициировать применение политики. Falco и kunai имеют задержку в несколько секунд при инициализации. В какой‑то момент kunai жаловался на малое количество памяти (а также его запуск иногда фризил систему). Falco становилось грустно, когда он не мог записаться в syslog — видимо, большая конкуренция на стенде его смушала:
Thu Jun 18 17:17:47 2026: "syslog" output timeout, all output channels are blocked
Из всех сервисов sysmon — тот единственный, который постоянно просит, чтобы его перезапускали при установке новых пакетов в систему. А вот falco при инициализации у меня порядочно нагружал СPU (до 15%) с последующим опусканием показателей.
Третий фактор — простота интерпретации фиксируемых событий. Одно событие auditd состоит из нескольких связанных событий (требуется нетривиальная агрегация в одно событие для формирования одного логического действия), есть непечатаемые символы, HEX; сама структура key‑value, которую тоже приходится агрегировать (например, в случае EXECVE). Он также показывает parent PID, но не расшифровывает его, что при расследовании инцидента может быть важным (построение цепочки процессов). Единственный способо обогащения событий auditd — указание key, можно указать связанную технику MITRE. Подбешивают многочисленные упоминания обращений к линковщику при запуске исполняемых файлов. Из форензик‑плюшек я бы отметил указание inode, что может пригодиться при глубоком погружении в расследовании сложного инцидента. Auditd с правилами ротировался как не в себя, каждые 40 секунд примерно в спокойном режиме функционирования ОС.
У sysmon легкочитаемая структура, хотя сам XML занимает место в лог‑файлах. Даёт хеш процесса — легче выявлять подмены исполняемых файлов. В конфигурации можно настроить обогащения событий, присваивая им произвольные теги. Можно сказать, что это простейший начальный мониторинг без надежды поймать серьёзное проникновение.
Falco понравился, в целом позволяет настроить выходной формат события, достаточно подробен. Я бы сказал, что это компромисс между «низкоуровневостью» tetragon, и простотой правил/политик auditd. До kunai по телеметрии ему далековато, но как рабочая лошадка выглядит сносно. Правила по умолчанию уже хоть немного полезны.
Из минусов — ивент формируется на каждое событие, и их агрегация встроенными средствами невозможна. Однако политики и формат событий здесь пишутся очень просто: обычно есть несколько контролируемых списков и формат выходного события с фильтром, можно настроить фильтрацию по IP‑адресам, подсетям, доменам, портам и так далее. Подкупает завязанность на docker. Лично я бы подумал в части указания capabilities над составлением кратких хешей наборов, чем полного перечисления в логах...
Tetragon — это инструмент глубокого мониторинга. Но чтобы эффективно его использовать для целей информационной безопасности, придётся реально углубиться в архитектуру ядра Linux — высокий порог вхождения. Знать все функции, их аргументы, возвращаемые значения довольно проблематично. Все события в едином JSON формате. Собирает очень сырые данные (например, auid 1000 показывает, но то, что это root — догадайся сам, пожалуйста). Командлайн разбивается на бинарь и аргументы, что может быть не очень удобно. С сетью также не резолвит адреса. Не всегда интуитивно понятно, где результат выполнения (успех/неудача). В итоге написать унифицированную политику очень трудозатратно. И обратите внимание, что это чисто мониторинг, обогащение здесь почти нереально прикрутить — даже ивенты одинаковые по составу получились. Под средней нагрузкой лог‑файл ротируется, как не в себя.
Kunai — очень дотошный инструмент. Выглядит, как тот самый любимчик учителя, который перегибает палку адекватности, хотя и упрекнуть его не всегда есть в чём. Однако малая поддержка ядер, прожорливость при инициализации, делают решение очень узкоспециализированным. Репозиторий правил похож на одноразовую историю. Решение хорошее, но пишет очень много. Также при запуске во втором сценарии kunai (из репозитория kunai‑rules) жалуется на слишком новое ядро, поэтому телеметрия будет не фонтан. Также прослеживалась конкуренция за eBPF:
[2026-06-18T17:00:06Z WARN kunai] enter_io_submit_sqe probe is not compatible with current kernel: min=5.1.0 max=5.4.0 current=6.1.0 [2026-06-18T17:00:09Z WARN kunai] io_uring_enter_io_poll_issue probe is not compatible with current kernel: min=6.15.0 max=KernelVersion::MAX current=6.1.0 [2026-06-18T17:00:11Z WARN kunai] syscalls_sys_exit_execveat probe is not compatible with current kernel: min=KernelVersion::MIN max=5.9.0 current=6.1.0 … [2026-06-18T17:11:41Z ERROR kunai] probes::bpf line=112 pid=35807 tgid=35807 comm=falco failed to retrieve BPF program load event … [2026-06-18T17:11:41Z WARN kunai] couldn't retrieve bpf program's metadata for event=e541b274-523d-6529-2750-f4930f9177f5, it probably got unloaded too quickly …
Ещё kunai порой просто отказывался запускаться, когда система была под нагрузкой:
thread 'main' (54656) panicked at kunai/src/bin/main.rs:2515:18: cannot open perf event buffer: MMapError { io_error: Os { code: 12, kind: OutOfMemory, message: "Out of memory" } } note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace # или так 2026-07-02T16:27:45Z ERROR kunai] some events have been lost in the way from kernel read=130996 lost=1620 loss-ratio=1.22% eps=472.87: consider event filtering out and/or increase the number of buffered events in configuration. Filtering hints, most frequent events: mmap_exec=78904, clone=16781, exit_group=16552, execve=11034, prctl=479
В телеметрии идёт не только логический запуск одного бинаря, но и запись всех библиотек, которые при этом дёргаются, что очень избыточно для стандартного мониторинга. Из‑за перегрузки этой форензикой (хеши, анализ заголовка файла), слабой поддержки, избыточности и необходимости вкуривать логику работы инструмент не считаю подходящим выбором для регулярной работы. Также есть вопросы к логированию сетевых событий.
Все испытуемые не смогли напрямую определить чисто bash‑евские приколы (пайпы, history, kill), что в целом было предсказуемо, но с точки зрения L1 SOC по‑своему обидно.
Выводы
Falco имеет, пожалуй, самый низкий порог входа для написания политики. Запросто задаёшь формат события, список контролируемых файлов, и получаешь систему понятного мониторинга, в том числе и в контейнерах. Для статичных систем, где мне наперёд известны все критичные файлы и запущенные службы, а также была SIEM с привязкой вида «что случилось — чем характерно для безопасности», я бы остановился на auditd. Falco имеет смысл применять при тесной работе с контейнерами, а kunai — при реагировании, триаже или в песочницах. Из минусов — eBPF позволяет собирать довольно много телеметрии, но это также требует ресурсов и в идеале — фильтрации/агрегации, что не всегда возможно.
Создание системы мониторинга мечты — практически нереальная задача. eBPF‑решения собирают очень много телеметрии, что для эффективного использования потребует внутренней или внешней агрегации, а также постоянного контроля за правилами/политиками для эффективной фильтрации. По сути это буквально означает разработку собственного EDR‑агента. Я, конечно, понимаю, что некоторые velociraptor постоянно держат наготове, но тут речь идёт о постоянной адаптации правил и поиске баланса — ловить старт каждого процесса (условно 100 EPS) или только избранных процессов (фильтр на 100 процессов, условно 1 EPS). Также классическая проблема мониторинга — был изменён/создан файл в критичной директории, но как оценить его контент (точную правку) и на что это влияет?
Вопрос фактического результата выполнения команд также сильно влияет на понимание контекста события. Например, команда
ping smthбыла запущена — но как она завершилась? Было ли сетевое соединение? Или при обращении к файлу — были ли реальные изменения файла, был ли он прочитан полностью?Универсальной конфигурации политики аудита на все случаи жизни найти не удалось. Всё, что есть общедоступного в сети, в любом случае нужно будет адаптировать под работу в вашей конкретной инфраструктуре — писать исключения, настраивать фильтрацию. В случае покупки какого‑то готового решения обычно львиную часть из этого делает вендор, но тюнинг неизбежен. Более того, проведённые тесты наглядно показали, что супер‑дупер IDDQD решения не существует — или прожорливо, или требует заботы. Оптимальным путём выглядит некоторая синергия решений — например falco для контейнерных историй и запуска, auditd для простых событий, где не требуется обширная форензика, только базовый мониторинг (условно выполнение специфических syscall’ов) без глубокой фильтрации. Но и в таком случае потребуется разработка политик для обоих решений, а потом ещё и агрегация событий, от этого не уйти.
Отдельный вопрос: интерпретация произошедшего события с точки зрения ИБ. В исследовании только falco и kunai обладали функционалом для выдачи информационных сообщений о происходящем. В реальном мире этим обычно занимается SIEM или иное средство внешней экспертизы (UEBA/XDR/EDR/SOC).
К недостаткам исследования можно отнести то, что не затронули osquery и tracee. Первый — мощный инструмент, но меня смущает необходимость постоянного к нему обращения (как к деду, который всё знает, но расскажет только когда ты сам пойдёшь и спросишь). До второго не дошли руки, и я оставил этот проект на перспективу — там интересно будет поковыряться в сигнатурах. Также не оценивал самозащиту и самомониторинг (детект изменений конфигурации, убийства логгера). Не игрался с приблудами для дополнительной фильтрации и агрегации событий auditd (audisp‑filter, плагины и прочее) — потому что не хотел наращивать дополнительные костыли к штатному механизму. И также не углублялся в адаптацию готовых политик, проведение их тюнинга для демонстрации максимальных результатов — чтобы оценить текущие предложения «из коробки». Тем не менее, все указанные недостатки я рассматриваю как плацдарм для дальнейших исследований.
Буду очень рад обмену мнениями и знаниями в комментариях. Эта тема для меня довольно животрепещущая, будет интересно обсудить ваших сынов ошибок трудных и best practice!
gotham_engineer
Хороший, объёмный разбор. Вы подтверждаете про прожорливость Falco и Tetragon на живых системах. Смотрите, хочу вопрос задать Вам как специалисту: 3 месяца в проде крутится связка n8n + Redis 7 в докере на чистой Ubuntu 6.8. Сервер скромный, 4 ГБ оперативки, поэтому каждый мегабайт оверхеда от логгеров критичен. Подумывал затащить eBPF для безопасности контейнеров, но, судя по вашим сценариям, объемы логов при плотном потоке данных просто сожрут дисковый I/O и память.Подскажите, а если вместо eBPF заворачивать логи докер-демона напрямую в systemd-journald с жестким лимитом (к примеру, SystemMaxUse=500M) - насколько это снижает общую нагрузку на хост по сравнению с тем же Falco? Стоит ли переходить на journald в мелком контуре?
ibtivist Автор
На самом деле тут вопрос тот же, что и в статье - не просто обеспечить абстрактную безопасность, а скорее всего есть какие-то сценарии/нежелательные события, которые хотелось бы контролировать. В проде уже сталкивались с безумными ротациями журналов (и 30 секунд и минута) - чаще всего это лечится настройками/политиками, зачастую компромиссами (логируем только один бинарь или исключаем такие-то вещи из аудита вообще, понимая риски).
Отдельная история с затаскиванием логов контейнеров куда-то ещё. Конкретно в данном случае, я бы рассмотрел стратегию делегирования и отправки событий в отдельный SIEM/сервис-парсер. Вторая стратегия - использование такой политики аудита falco, которая позволит отлавливать только нужные события применительно к Вашей инсталляции.
+ условно иногда выгоднее не заниматься попыткой обнаружить всё на свете, а использоваться связки - например через fail2ban мы можем уже меньше думать об удачных/неудачных авторизациях. Настроив аудит Redis и используя кастомные grep'ы мы можем тоже некоторые вещи сделать сами без дополнительных средств)
Отвечая на вопрос, конкретных замеров я не проводил, тоже нужно будет экспериментировать, принимая во внимание вышеописанное.