Привет, Хабр! В прошлой статье мы делали обзор на opensource NTA – Arkime, здесь мы продолжим развивать NTA для безопасника и поговорим об анализе файлов в сетевом трафике. Текущая статья будет своего рода обзором - инструкцией по развитию системы.
“Из коробки” Arkime может в YARA, но в таком случае анализ файлов будет на мощностях нод захвата со всеми вытекающими последствиями (увеличение потока трафика потребует СУЩЕСТВЕННОГО увеличения мощности ноды). Чтобы переложить нагрузку с “больной головы” на “здоровую” мы решили прикрутить Strelka.
Strelka
Итак, что же такое Strelka? Как заявляет комьюнити - это энтерпрайз решение для статического анализа и поиска файлов по YARA правилами (и не только) в режиме реального времени. Архитектурно представляет клиент-серверное приложение (понятное дело, требует установки агентов). Агенты написаны на Go Lang, что предоставляет возможность кроссплатформенной установки. Наглядный usecase - поиск вредоносного файла, который обошел антивирусы, но при этом был обнаружен командой кибербеза. На файл написали YARA сигнатуру и запустили поиск по всем агентам в инфраструктуре. Подобный кейс рассмотрим чуть дальше в статье.
К сожалению, о Strelka мало кто знает и использует в СНГ. Разумеется, в этом нет ничего удивительного - как правило, такие функции возлагают на вышеупомянутые коммерческие решения (антивирусы, EDR и пр.), используют самописное, либо вовсе не задумываются о таких задачах.
Изначально Strelka задумывалась как решение для поиска и анализа файлов по запросу, в том числе и анализ файлов на сетевом потоке IDS/IPS сенсорах, полученных из трафика. Именно такой функционал нам и нужен в рамках развития нашего NTA – Arkime.
Из однозначно приятных дополнительных возможностей - отправка файлов в песок для динамического анализа (из коробки реализована только интеграция с Cuckoo sandbox, но есть возможность прикрутить любой другой, если вы умеете в Python). Используя такую интеграцию можно получить песок для всех передаваемых по сети файлов.
Архитектура Strelka достаточно гибкая и позволяет развернуть как маленькую инсталяцию для тестов, так и распределенную инсталяцию для кровавого энтерпрайза (реализация любого взаимодействия серверов и агентов).
В чем профит?
Возникает вполне логичный вопрос - зачем вовсе анализировать файлы на сетевом потоке, когда есть EDR/антивирус и прочие СЗИ с аналогичным функционалом (даже если 146% покрытие инфраструктуры указанными тулами)?
Отвечу списком, который раскрою подробнее:
Любой вредонос может бесследно исчезнуть на эндпоинтах до его обнаружения, но не из трафика, который захвачен NTA;
Точное понимание откуда, куда и когда передавался файл;
Модифицировался ли файл при передаче от одного узла к другому;
Обнаружение Lateral Movement (в дальнейшем просто latmov);
Передача файла с машины, которая вошла в 0,00001% без покрытия СЗИ (или вовсе не доменной);
Автоматизация и упрощение рутинных задач Detection Engineering’а (удобно скармливать файл стрелке и большинство полезной информации удобно (почти) раскладывается в JSON’e, отправляется в песочницы и куда только захотите).
TL;DR подробнее:
Как известно, антивирусы не являются серебряной пулей от всех болезней, да и способов обхода их обнаружения известно не мало. Хороший специалист красной команды (этичный хакер) сможет легко обойти антивирус даже для избитого и отлично известного mimikatz’a. А серьезные ребята из АРТ группировок вовсе стараются не оставлять своих следов. При передаче нешифрованным каналом своего инструментария (например smb или любимым psexec’ом) и последующим его запуском с моментальным удалением, вряд ли удастся получить образец файла на целевой системе, однако он осядет на NTA и будет проанализирован strelka. Из этого можно будет написать детект и отслеживать дальнейшее перемещение файла по сети не только с помощью агентов/антивируса и пр, но и на сети. Хочу отметить, что речь идет не о первичном обнаружении, а именно о насыщении инцидента важными подробностями, о последующем респонзе и анализе, что в будущем приведет к своевременному первичному обнаружению.
Агенты на устройствах наверняка расскажут о наличии того или иного файла, максимум - подсветят происхождение файла (вспоминаем о Transfer Zone), но едва ли расскажут, откуда появился файл (если только косвенными признаками при анализе логов с хоста, что требует самого ценного ресурса - времени). В случае же анализа Strelk’ой - у вас будет неоспоримое доказательство о дате, источнике передачи точного файла (ведь Strelka считает хэши). Такой подход существенно сокращает время разбора инцидента, а как мы знаем, при активном реагировании, скорость анализа является очень критичным показателем - иногда секунды определят, появится ли новый доменный админ или нет.
Некоторые вирусы могут изменять свою структуру с течением времени или иными факторами (разумеется, для избежания обнаружения), но Strelka анализирует все возможные заголовки файлов, в том числе вычисляет imphash и пишет его в лог (imphash - это хэш подключаемых библиотек для исполняемого файла). Кроме imphash для исполняемых файлов записываются: архитектура, для который был написан бинарь, все метаданные исполняемого файла (например версия, цифровая подпись и пр.), ОС, характеристики подключаемых библиотек (их функции) и многие другие заголовки. По таким признакам возможно написать YARA правило (или правило корреляции - ведь логи Strelka отправляются в SIEM), которое будет отслеживать файл, даже если он постоянно изменяется (вы можете справедливо заметить, что в случае продвинутой малвари для избежания обнаружения могут подключаться дополнительные библиотеки для изменения imphash, однако не всегда будут использоваться дополнительные функции новых библиотек).
Вполне вероятно, что latmov может отслеживаться на логах и самой сети, однако как быть, когда злоумышленники используют легитимные для инфраструктуры инструменты (конечно же, что бы усложнить свое обнаружение защитниками)? Профилирование активности админов может не спасти - ведь с точки зрения логов и сети, активность ничем не отличается от действий администратора. Однако именно тут на помощь приходит Strelka - в случае передачи своих инструментов по сети (а как иначе?), они будут проанализированы. Согласитесь, что администраторы не используют хактулы и малварь при осуществлении ежедневных обязанностей?
Идеальных инфраструктур не существует. Наверняка даже в крупнейших банках мира найдется 1 машина, на которой нет СЗИ или вне домена, или вовсе является нелегитимно подключенным устройством в сеть (например raspberry pi, включенная в RJ-45 розетку сети ip-телефонии). В таком случае СЗИ смогут обнаружить (ЕСЛИ смогут) только факт появления вредоноса на атакуемой машине, но Strelka точно расскажет, откуда прилетел такой файл. В более зрелых, с точки зрения ИБ, инфраструктурах может быть обнаружен факт подключения в сеть неизвестного устройства, что послужит хорошим поводом для начала расследования - при таком раскладе развития событий, Strelka упростит анализ активности нелегитимного устройства.
Резюмируя, все эти пункты можно свести к следующему профиту:
Сокращение времени расследования инцидентов;
Увеличение видимости инфраструктуры для безопасника;
Насыщение контекста инцидентов;
Дополнительные возможности детекта вредоносной активности в сети.
Плюсов не мало, однако есть очень важный нюанс, в случае анализа файлов на трафике, они должны быть переданы НЕшифрованным каналом (например, если файл передавался по RDP или по кастомному шифрованному протоколу - разумеется, он не будет проанализирован). В копилку минусов можно так же отнести отсутствие в логах Strelka айпишников, откуда файл был выдернут, НО! с помощью logstash обогатить такие данные.
Предварительная настройка ноды захвата
!!!Дисклеймер!!!
Мы не претендуем (хотя стремимся к ней) на идеальную конфигурацию Suricata, Zeek, Strelka и тд, а так же на полноту покрытия всех потребностей безопасника. Мы точно знаем, что у нас есть недостатки в конфигах, но мы только начали свой путь и, со временем, мы станем лучше, в том числе, благодаря вашим советам и рекомендациям. “Совет начинающим: начните” (с) Великие цитаты Великих людей =)
Для того, чтобы анализировать файлы, их сначала надо откуда-то получить. В прошлой статье мы упоминали, что Arkime сам по себе не выдергивает файлы из трафика (но индексирует их имена). Кроме самого Arkime, на ноде захвата установлена Suricata. Именно с ее помощью мы хотели получать файлы. Однако, столкнулись с тем, что Suricata не особо адекватно выдирает файлы из трафика из-за особенностей пересбора сессии. Более эффективным вариантом будет Zeek (ранее Bro) с настройки на экстракт файлов.
Для этого стоит начать с установки самого Zeek, но мы надеемся, что с эти проблем у вас не будет =). После стандартной настройки (выбор портов для прослушки и т.д.), необходимо написать скрипт на языке самого Zeek (смесь Lua). Такой скрипт есть "из коробки", но он будет собирать все файлы подряд и изначально выключен.
Сам Zeek устроен достаточно хитро и подразумевает внутри себя целую программируемую платформу, которая может делать все что вы захотите, при наличии прямых рук. Для включения скрипта нам нужно “загрузить” его в основной скрипт запуска Zeek. В случае использования standalone инсталяции, достаточно внести директиву “@load <имя модуля>”
в файл “префикс_корня_zeek/share/zeek/site/local.zeek”
.
С точки зрения загрузчика - пути к модулями начинаются из папки share и share/policy, и идеологически - каждый модуль находится в своей папке. Дефолтный крипт выгрузки всех файлов является частью стандартного модуля FileAnalysis, и находится по пути “frameworks/files/extract-all-files”. Как уже было сказано выше, скрипт будет выдирать все подряд, чего мы хотим избежать. Для этого мы своровали вдохновились более точечной вариацией от Security Onion Solutions, где предварительно создан словарь с mime типами файлов и их расширениями. Так как данный скрипт генерируется при установке самого SOS, мы переопределим свои типы и используем их.
Для того чтобы скрипт стал “модулем”, который мы будем использовать, переместим его в папку (для сохранения истоков - share/policy/securityonion/file-extract/
) и создадим файл “__load__.zeek”
(по аналогии __init__.py в Python модулях), в которой добавим директиву “@load ./extract”
. Соответственно, сам скрипт будет называть extract.zeek и в него мы добавим следующее:
redef FileExtract::prefix = "<путь к папке с вырезанными файлами>";
# Set a limit to the file size
redef FileExtract::default_limit = 9000000; # максимальный размер файл для вырезки
# These are the mimetypes we want to rip off the networks
export {
global _mime_whitelist: table[string] of string = {
[“application/x-dosexec”] = “exe”,
# Здесь можно описывать другие типы
} &default = “”;
# Start grabbing the file from the network if it matches the mimetype
event file_sniff(f: fa_file, meta: fa_metadata) &priority=10 {
local ext = "";
if( meta?$mime_type ) {
if ( meta$mime_type !in _mime_whitelist ) {
return;
}
ext = _mime_whitelist[meta$mime_type];
local fname = fmt("%s-%s.%s", f$source, f$id, ext);
Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=fname]);
}
}
# Wait for file_state_remove before you do anything. This is when it is actually done.
event file_state_remove(f: fa_file)
{
if ( !f$info?$extracted || FileExtract::prefix == "" ) {
return;
}
# Check if any of the following conditions exist:
# - missing MD5
# - total_bytes exists (some protocols aren't populating this field) but is 0
# - missing bytes
# - timed out
if ( !f$info?$md5 || (f?$total_bytes && f$total_bytes == 0) || f$missing_bytes > 0 || f$info$timedout) {
# Delete the file if it didn't pass our requirements check.
local nuke = fmt("rm %s/%s", FileExtract::prefix, f$info$extracted);
when [nuke] ( local nukeit = Exec::run([$cmd=nuke]) )
{
}
return;
}
local orig = f$info$extracted;
local split_orig = split_string(f$info$extracted, /\./);
local extension = split_orig[|split_orig|-1];
local dest = fmt("%scomplete/%s-%s-%s.%s", FileExtract::prefix, f$source, f$id, f$info$md5, extension);
# Copy it to the $prefix/complete folder then delete it. I got some weird results with moving when it came to watchdog in python.
local cmd = fmt("cp %s/%s %s && rm %s/%s", FileExtract::prefix, orig, dest, FileExtract::prefix, orig);
when [cmd] ( local result = Exec::run([$cmd=cmd]) )
{
}
f$info$extracted = dest;
}
Статья не про Zeek, но в вариации SOS также есть проверка на корректность выгрузки в обработке file_state_remove, где файл будет удален, если во время сбора не были выполнены некоторые требования.
Путь курильщика:
Настройка suricata.yaml:
Чтобы научить сурка сохранять файлы, необходимо добавить следующие строки (из документации самого сурка 1 и 2) в suricata.yaml (оформить код-блоком) :
- eve-log:
enabled: yes
filetype: regular
filename: eve.json
…
types:
…
- files:
force-magic: yes
…
- file-store:
version: 2
enabled: yes
dir: /ваша/директория/
stream-depth: 0
…
Правила захвата файлов мы честно решили взять с проекта Security Onion Solutions, вот ссылка на сами правила
-
После изменения конфигов, рестартуем сурикату и проверяем запись файлов по указанной директории в конфиге выше. Должно получиться что-то подобное:
Все эти директории - первые 2 символа в sha-256 хэше, того файла, который был сохранен. (например, если SHA-256 хэш файла начинается с ”00”, то он будет сохранен в директории 00)
Готово, теперь файлы будут выдергиваться в эту директорию.
Перейдем к деплою и настройке Strelka.
Деплоймент Strelka
В рамках этой статьи мы выбрали самый простой варинат деплоя - инсталяция всех компонентов в одном экземпляре с помощью docker compose (1 сервер - 1 агент). В большой инсталяции количество сервисов увеличивается по необходимости, а для роутинга и балансировки gRPC запросов используется Envoy. Проще говоря - больше нагрузка, больше сервисов.
Разберем, что в космической собаке зарыто:
Strelka-frontend принимает входящие запросы от клиентов по gRPC.
Strelka-backend обрабатывает входящие файлы.
Strelka-manager смотрит в Redis.
Coordinator – Redis сервер, координирующий задачи на анализ и данные между frontend и backend.
Gatekeeper – Redis сервер, реализующий временный кэш из событий.
Mmrpc - опциональный сервис на базе проекта MaliciousMacroBot, и включающий сканер ScanMmbot
Перейдем к самой установке (далее мы просто копируем из официального гита Strelka):
-
Копируем репозиторий и заходим в него
git clone https://github.com/target/strelka.git && \
cd strelka
-
Удаляем тестовый файл с yara правилами и качаем правила из Yara-Rules. Стоит отметить, что в Yara-Rules идет в комплекте файл index.yar, который включает в себя все остальные. Его же мы инклюдим в файл configs/python/backend/yara/rules.yara. Вы можете использовать другие правила или использовать свои собственные
rm configs/python/backend/yara/rules.yara && \
git clone https://github.com/Yara-Rules/rules.git configs/python/backend/yara/rules/ && \
echo 'include "./rules/index.yar"' > configs/python/backend/yara/rules.yara
-
Билдим образа докера и поднимаем их, а также собираем один из клиентов - strelka-oneshot.
docker-compose -f build/docker-compose.yaml build && \
docker-compose -f build/docker-compose.yaml up -d && \
go build github.com/target/strelka/src/go/cmd/strelka-oneshot
Берем любой пример вредоносного ПО (в примере от самой Strelka решили выбрать Emotet, находящийся в запароленном архиве) и скармливаем его нашей собаке:
./strelka-oneshot -f samples/Win32.Emotet.zip -l - | jq
Вывод будет представлять собой 2 JSON документа, которые с помощью jq будут адекватно отображены. Так как мы скармливаем архив, Strelka автоматом обрабатывает и вложенные файлы.
Как же именно strelka понимает, что за файл перед ней? С помощью пачки yara правил из файлика taste.yara, а с помощью файла конфигурации backend.yaml, будут выбраны сканеры, которые и проанализируют файлы. Все файлы конфигурации всех сервисов нашей собаки лежат в папке configs/<язык сервиса(go или python)>, как и примеры конфигураций для клиентов.
Интеграция Zeek <> Strelka
Итак, вернемся к нашему кейсу - обработка файлов на потоке. Нам нужен клиент strelka-filestream, который будет смотреть указанные шаблоны наименований файлов и отправлять их как задачи на анализ. Давайте также соберем filestream:
go build github.com/target/strelka/src/go/cmd/strelka-filestream
Обработанные файлы он будет перекладывать в другую папку, чтобы необработанные и обработанные не пересекались:
conn:
server: '<адрес frontend>:57314'
cert: 'ssl при наличии'
timeout:
dial: 5s
file: 1m
throughput:
concurrency: 8
chunk: 32768
delay: 0s
files:
patterns:
- '<папка с файлами от Zeek>/*'
processed: '<любая директория, где будут храниться обработанные файлы>'
delete: false ## Удалить ли файлы, после их отпраки на анализ
gatekeeper: true
response:
report: 5s
delta: 5s
staging: '/path/to/your/staging/directory/' ## Директория, в которую файлы перемещаются ПЕРЕД отправкой
Это настройки, которые мы решили выбрать для себя, но вы всегда можете обратиться к документации, и настроить все под себя. Описание всех конфигураций для всех сервисов - здесь, но документация достаточно скромная (ну, а что мы хотели от opensource).
Также напишем простенький systemd юнит для “правильного” (по мнению бОльшего представительства линуксойдов) запуска strelka-filestream:
[Unit]
Description=Strelka Filestream Binary
After=network.target
[Service] Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/bin/strelka-filestream-linux -c /etc/strelka/filestream.yaml
[Install]
WantedBy=multi-user.target
Вы можете более тонко настроить деплой клиента - сделать отдельного пользователя, настроить права на все используемые нами папки, или даже написать SeLinux политику (почему бы и да?).
Настройка доставки логов для анализа
Самое НЕинтересное - доставка логов в ELK, именно на этом этапе мы потратили больше всего времени. Схема JSON документов по результатам анализа файлов нам показалась невероятно монструозной, поэтому выделили самое интересное и основное ниже (документация на схему JSON отсутствует на момент выхода статьи):
{ “file”: … “scan”: {
“<название сканера или краткое определение>”: { <пачка объектов (с вложенными объектами), с информации от сканера> }
}}
И выглядит все оно конечно просто и понятно, но как только вы сами закинете тестовый Emotet на анализ, вы увидите “немного” больше, чем показывают в примере. Тут по плану должен был быть пример лога, но в нем более 9к строк, поэтому прикладываем только отрывок под скрытый текст.
Часть лога, не смотрите - страшно
"pe": {
"address_of_entry_point": 1075979,
"base_of_code": 4096,
"base_of_data": 1671168,
"checksum": 8268887,
"compile_time": "2023-01-20T18:07:04",
"debug": {
"age": 1,
"guid": "2d00683a-1f90-0e48-8dc7b915b650eec3",
"pdb": "D:\a\1\s\exe\Win32\Public_Release\Sysmon.pdb",
"type": "rsds"
},
"dll_characteristics": [
"DYNAMIC_BASE",
"NX_COMPAT",
"TERMINAL_SERVER_AWARE"
],
"elapsed": 1.431875,
"file_alignment": 512,
"file_info": {
"company_name": "Sysinternals - www.sysinternals.com",
"file_description": "System activity monitor",
"file_version": "14.14",
"fixed": {
"operating_systems": [
"WINDOWS32"
],
"type": {
"primary": "UNKNOWN"
}
},
"internal_name": "System Monitor",
"legal_copyright": "By Mark Russinovich and Thomas Garnier\nCopyright (C) 2014-2023 Microsoft Corporation\nUsing libxml2. libxml2 is Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.",
"product_name": "Sysinternals Sysmon",
"product_version": "14.14",
"var": {
"character_set": "Unicode",
"language": "U.S. English"
}
},
"flags": [
"no_certs_found"
],
"header": {
"machine": {
"id": 332,
"type": "I386"
},
"magic": {
"dos": "DOS",
"image": "32_BIT"
},
"subsystem": "WINDOWS_CUI"
},
"image_base": 4194304,
"image_characteristics": [
"EXECUTABLE_IMAGE",
"32BIT_MACHINE"
],
"image_version": 0,
"imphash": "22c706ca771a1849826506040b11d50f",
"linker_version": 14.34,
"major_image_version": 0,
"major_linker_version": 14,
"major_operating_system_version": 6,
"major_subsystem_version": 6,
"minor_image_version": 0,
"minor_linker_version": 34,
"minor_operating_system_version": 0,
"minor_subsystem_version": 0,
"operating_system_version": 6,
"resources": [
{
"id": 1001,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "3e725ceddba1b9460f6304986e6248da",
"sha1": "8710b02483beac27184dd236209f7d1df864ffe6",
"sha256": "d92b848707513894733fe2da0faf7ba7dcfbbb98380d06e07c87521e7fab6bf0"
},
{
"id": 1002,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "01124a558ec6d65f6471456da07ab4ea",
"sha1": "743e1c0d0495d334c77132640352208f806aeb54",
"sha256": "9681d860e84375b95b3d59006dae41b007bca3824bfe10e0c313b72f68ac178a"
},
{
"id": 1,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "12a95179b2738a3f37060eacad6bd6a3",
"sha1": "1197df76d0a31be7a369b13499f1e73c6b6791d0",
"sha256": "3d63feefaf94dc719aa83afc9b25dc8ca317a686cdb617e39f4c1548b060a8ed"
},
{
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "932524ab57ae83176d5596fff967aa52",
"name": "SYSMONSCHEMA",
"sha1": "691dd0236bc963b1b7528accdba56fee26fce10d",
"sha256": "0387426eeb7ea7705c7524e2d6425a7c6fda9923c25650309ae3a6440a0e9971"
},
{
"id": 1,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "bfc1566bfd06c8135ab5c3a2e321ebe7",
"sha1": "66bd09bd18c6953d6fcea960431dfb6ddd7f9416",
"sha256": "db58c2c74246fa1e1c6c504373332303a511be9072ee5b31796c2a837a922f17",
"type": "MESSAGETABLE"
},
{
"id": 1,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "d901d37a441a56585e2878f8f31fd958",
"sha1": "0c60e3a7cc4a9c5726767611fd774c5826690438",
"sha256": "695d4d08304fd3377908fb02cb58063052223e391b9ed70da067a9bb3725c53e",
"type": "VERSION"
},
{
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "97563aa40292469b3e117adfc66dfcbe",
"name": "SYSMONMAN",
"sha1": "8c4b4d8d95edc34e21b1723f3badcfaf51ea6dd5",
"sha256": "71318e89313c58d15da819be6f89fdc975228cbf82626bfa2c78aa2f28d04ad8",
"type": "HTML"
},
{
"id": 1,
"language": {
"primary": "ENGLISH",
"sub": "ENGLISH_US"
},
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"type": "MANIFEST"
}
],
"rich": {
"clear_data": {
"data": "RGFuUwAAAAAAAAAAAAAAAEt4AwETAAAAS3gFAcUAAABPfP0ABAAAAE98BQFWAAAAT3wEARMAAABPfAMBGQAAAEt4BAEZAAAAuHUDAQEAAAC7dQQBCgAAAEt4AQEjAAAAAAABAFIBAADBfAkBPQAAAMF8/wABAAAAAACXAAIAAADBfAIBAQAAAA==",
"md5": "fa160a389ccba4f1f5e7ea61c15d319a"
},
"info": [
{
"count": 19,
"toolid": 259,
"version": 30795
},
{
"count": 197,
"toolid": 261,
"version": 30795
},
{
"count": 4,
"toolid": 253,
"version": 31823
},
{
"count": 86,
"toolid": 261,
"version": 31823
},
{
"count": 19,
"toolid": 260,
"version": 31823
},
{
"count": 25,
"toolid": 259,
"version": 31823
},
{
"count": 25,
"toolid": 260,
"version": 30795
},
{
"count": 1,
"toolid": 259,
"version": 30136
},
{
"count": 10,
"toolid": 260,
"version": 30139
},
{
"count": 35,
"toolid": 257,
"version": 30795
},
{
"count": 338,
"toolid": 1,
"version": 0
},
{
"count": 61,
"toolid": 265,
"version": 31937
},
{
"count": 1,
"toolid": 255,
"version": 31937
},
{
"count": 2,
"toolid": 151,
"version": 0
},
{
"count": 1,
"toolid": 258,
"version": 31937
}
],
"key": "6220d5e7",
"raw_data": {
"data": "JkG7tGIg1ediINXnYiDV5ylY1uZxINXnKVjQ5qcg1ectXCjnZiDV5y1c0OY0INXnLVzR5nEg1ectXNbmeyDV5ylY0eZ7INXn2lXW5mMg1efZVdHmaCDV5ylY1OZBINXnYiDU5zAh1eejXNzmXyDV56NcKudjINXnYiBC52Ag1eejXNfmYyDV5w==",
"md5": "99df8167e636a4e5d03f3a305f56b445"
}
},
"section_alignment": 4096,
"sections": [
{
"address": {
"physical": 1666038,
"virtual": 4096
},
"characteristics": [
"CNT_CODE",
"MEM_EXECUTE",
"MEM_READ"
],
"entropy": 6.636632502553922,
"md5": "55eac3bed86e853dd7b97ecae392a49c",
"name": ".text",
"sha1": "d86802270020bf19260c1a0f4c4e46551a9f01d6",
"sha256": "7b80a0764abba960a81e444dcc6f5cb3ec8c38c0c4aeb89e518ef40ee0d129d1",
"size": 1666048
},
{
"address": {
"physical": 786576,
"virtual": 1671168
},
"characteristics": [
"CNT_INITIALIZED_DATA",
"MEM_READ"
],
"entropy": 4.3805809880051365,
"md5": "ba57f1bdd64e68192ac99511c004430d",
"name": ".rdata",
"sha1": "ae67fd775a29fe2e812c04d7fae00fed6cd3f10f",
"sha256": "95d49c9c84ddb5bd3fb746000e2be173d51af0a4971d0d2f82a728d769a74778",
"size": 786944
},
{
"address": {
"physical": 29224,
"virtual": 2461696
},
"characteristics": [
"CNT_INITIALIZED_DATA",
"MEM_READ",
"MEM_WRITE"
],
"entropy": 4.474025218651877,
"md5": "31a647892600452887f8681d5007a2e4",
"name": ".data",
"sha1": "7b84cf7ccb58aca03a780b60ee731ef458f311d7",
"sha256": "6331c1d1e5d08db286a879035ffa9f08f999bf87358938ef184ccaf25fe0b626",
"size": 14848
},
{
"address": {
"physical": 5686360,
"virtual": 2494464
},
"characteristics": [
"CNT_INITIALIZED_DATA",
"MEM_READ"
],
"entropy": 1.699918955277781,
"md5": "bf1b231fd7cebcaf9ce86b18fdcba822",
"name": ".rsrc",
"sha1": "1e941fdd57610dd433314447b1076f84bad98594",
"sha256": "fe21359b2a4ce3fde264344efee4f475b752d192ea1039aad72edf19e764f073",
"size": 5686784
},
{
"address": {
"physical": 60920,
"virtual": 8183808
},
"characteristics": [
"CNT_INITIALIZED_DATA",
"MEM_DISCARDABLE",
"MEM_READ"
],
"entropy": 0,
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"name": ".reloc",
"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 60928
}
],
"size_of_code": 1666048,
"size_of_headers": 1024,
"size_of_heap_commit": 4096,
"size_of_heap_reserve": 1048576,
"size_of_image": 8245248,
"size_of_initialized_data": 6564352,
"size_of_stack_commit": 4096,
"size_of_stack_reserve": 1048576,
"size_of_uninitialized_data": 0,
"subsystem_version": 6,
"summary": {
"resource_md5": [
"01124a558ec6d65f6471456da07ab4ea",
"97563aa40292469b3e117adfc66dfcbe",
"12a95179b2738a3f37060eacad6bd6a3",
"3e725ceddba1b9460f6304986e6248da",
"d41d8cd98f00b204e9800998ecf8427e",
"932524ab57ae83176d5596fff967aa52",
"d901d37a441a56585e2878f8f31fd958",
"bfc1566bfd06c8135ab5c3a2e321ebe7"
],
"resource_sha1": [
"1197df76d0a31be7a369b13499f1e73c6b6791d0",
"0c60e3a7cc4a9c5726767611fd774c5826690438",
"691dd0236bc963b1b7528accdba56fee26fce10d",
"8c4b4d8d95edc34e21b1723f3badcfaf51ea6dd5",
"743e1c0d0495d334c77132640352208f806aeb54",
"66bd09bd18c6953d6fcea960431dfb6ddd7f9416",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"8710b02483beac27184dd236209f7d1df864ffe6"
],
"resource_sha256": [
"71318e89313c58d15da819be6f89fdc975228cbf82626bfa2c78aa2f28d04ad8",
"d92b848707513894733fe2da0faf7ba7dcfbbb98380d06e07c87521e7fab6bf0",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"3d63feefaf94dc719aa83afc9b25dc8ca317a686cdb617e39f4c1548b060a8ed",
"db58c2c74246fa1e1c6c504373332303a511be9072ee5b31796c2a837a922f17",
"0387426eeb7ea7705c7524e2d6425a7c6fda9923c25650309ae3a6440a0e9971",
"695d4d08304fd3377908fb02cb58063052223e391b9ed70da067a9bb3725c53e",
"9681d860e84375b95b3d59006dae41b007bca3824bfe10e0c313b72f68ac178a"
],
"section_md5": [
"55eac3bed86e853dd7b97ecae392a49c",
"d41d8cd98f00b204e9800998ecf8427e",
"31a647892600452887f8681d5007a2e4",
"bf1b231fd7cebcaf9ce86b18fdcba822",
"ba57f1bdd64e68192ac99511c004430d"
],
"section_sha1": [
"d86802270020bf19260c1a0f4c4e46551a9f01d6",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"1e941fdd57610dd433314447b1076f84bad98594",
"ae67fd775a29fe2e812c04d7fae00fed6cd3f10f",
"7b84cf7ccb58aca03a780b60ee731ef458f311d7"
],
"section_sha256": [
"6331c1d1e5d08db286a879035ffa9f08f999bf87358938ef184ccaf25fe0b626",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"fe21359b2a4ce3fde264344efee4f475b752d192ea1039aad72edf19e764f073",
"95d49c9c84ddb5bd3fb746000e2be173d51af0a4971d0d2f82a728d769a74778",
"7b80a0764abba960a81e444dcc6f5cb3ec8c38c0c4aeb89e518ef40ee0d129d1"
]
}
Самое страшное здесь - секции с импортируемыми функциями из PE заголовков, полная meta информация по yara правилам и массив из информации об энтропии каждой секции исполняемого файла (не пугайтесь, если вы ничего не поняли - мы тоже ничего не поняли).
Нам очень хотелось слать всю информацию о сработках yara правил в Elasticsearch и в дальнейшем использовать в визуализациях, или даже написать правила в Elastic Security.
Но к нашему сожалению - meta данные по yara правилам - массив из несвязанных объектов, поэтому пришлось воспользоваться Vector и написать парсер на VRL, так как писать все это на Painless Script - полное безумие (попробуйте сами).
Анализ
Результатом обработки файлов является лог-файл на ноде захвата и для его последующего анализа требуется его куда-то забирать и визуализировать. Для этого мы решили использовать filebeat + ELK stack (теоретически, можно складывать в ELK Arkime, к которому прикручена Kibana/opensearch, но мы складываем в отдельную инсталяцию). Пример лога в ELK для тестового EICAR файла (мы используем yara правила от Elastic - можно глянуть тут):
Как можно заметить, самые интересные, с точки зрения безопасности, поля - это сработка YARA, хэши, название файла. Внутри тестового файла отсутствуют какие-либо еще интересные данные.
Пару скринов ниже покажут пример анализа internal monologue:
Однако Strelka вытаскивает еще полезную информацию:
Тип файла (из заголовка)
URL’ы в файлах
Импортируемые функции из библиотек
Послесловие
Strelka не единственная в своем роде - коллеги по цеху из Канады разрабатывают AssemblyLine - концептуально похожий проект со Strelka.
Авторы текста:
Тыщенко Иван, tg:@nerebros
Дьячков Иван, tg: @Roosevelt_Rus