Хабр, всем привет! Когда инфраструктура созрела до состояния персика и уже пора расширять классический SOC или вам всегда было интересно, как работает ВПО, то необходимо переходить к Threat Intelligence! Сегодня мы соберем свою лабу по исследованию вирусни/инструментов и процедур(PoC) в виртуальной среде для Linux-платформ.

Материал пригодиться, для понимания работы ВПО, написания митигации и детекции против них, а так же поиска паттернов в вашей инфраструктуре! На выходе мы получим список из артефактов, которые сможем использовать в работе.

Зачем оно надо и из чего состоит?

Ожидается, что мы сможем трассировать системные вызовы, дампировать сетевой трафик, а так же рассматривать открытый и закрытый исходный код сэмпла в поисках подозрительных паттернов.

Преимущества такой лаборатории видны сразу:

  • существует мало современных SandBox решений, которые могут предоставить детализированный сырой лог сетевой и системной активности для Linux-платформ;

  • обычно SandBox очень легко обходят, уводя в сон вредонос, проверяя локации, раскладки и прочие атрибуты. В данном решении вы сможете увидеть, спит ли сэмпл, а так же использовать свои ресурсы, чтобы увеличить время трассировки в отличии от стандартных 500 секунд песка.

  • user execution очень органичен в стандартных решениях, необходимо использовать скрипты для выполнения дополнительных задач. В данном, ваша лаба полностью управляема вами.

  • возможность имитировать инфраструктуру вокруг сэмпла, часто может пригодиться для исследования инструментов.

Все круто, но не без недостатков:

  • остается возможность прикрутить движки для вывода вердиктов, но лаба предназначена для ручного анализа;

  • нужна другая архитектура для тестирования атак на пространство ядра, так как здесь ядро ОС общее;

  • использование собственных ресурсов, вместо сервисных;

Архитектура представляет контур в рамках узла, где основные инструменты исследования будут развернуты в качестве драйвера pcap, бинарного файла strings, механизма eBPF и нормализатора на Python, а сэмпл будет воспроизводиться в контейнере.

Для изоляции двух сред будем использовать возможности Podman, который создаст нам виртуальное пространство с новыми namespace. Они должны ограничить возможность коммуникации, с другими обьектами. Seccomp запретит заражение пространства ядра, ограничив bpf, pivot_root, unshare, setns. Чтобы процесс не мог выйти из своего namespace или создать новый. Сgroup позволит выдать нужное количество ресурсов, а iptables разграничит по сети.

Для тех, кто не знаком с eBPF подкатом можно прочитать больше!

Алгоритм работы механизма eBPF

eBPF - это инструмент трассирования в Linux, который позволяет к точкам ОС крепить пользовательский код для мониторинга, фильтраций или изменения выполняемых функций. Работает он без системных вызовов поэтому зона его влияния - это функции разных пространств, сетевой трафик, а так же контроль точек LSM. Как упоминали раннее необходимо выбрать механизм и точку трассировки, такие как:

  • uprobe / uretprobe - функции пользовательского пространства

  • kprobe / kretprobe - функции пространства ядра

  • tracepoint - точки для событий между пространствами (syscalls)

  • network filter - точки сетевого трассирования

  • LSM-hooks - хуки системы безопасности (Linux Security Modules)

Конфигурировать механизм можно используя системные вызовы bpf(), а так же же готовые инструменты bpftrace со скриптами bt. Схема работы такова:

Такие решения позволяют реализовать низкоуровневый функционал управления сетевым трафиком, запуском функций СЗИ, а так же становятся целью злоумышленников.

Развертывание и изоляция лаборатории

В рамках развертывания лабы реализована частичная автоматизация, что упрощает работу, но основной массив действий будет описан ниже.

Для работы развернем дистрибутив Kali Linux последней версии и предустановим зависимости:

sudo apt update && apt install seccomp iptables binutils tcpdump wget podman -y
sudo apt-get install -y bpfcc-tools libbpfcc libbpfcc-dev linux-headers-$(uname -r) 
sudo wget https://github.com/bpftrace/bpftrace/releases/download/v0.23.5/bpftrace  
sudo cp ./bpftrace /usr/local/bin/bpftrace
chmod +x /usr/local/bin/bpftrace
wget https://github.com/gojue/ecapture/releases/download/v1.4.1/ecapture\_v1.4.1\_linux\_amd64.deb
sudo apt install ./ecapture_v1.4.1_linux_amd64.deb

Перед тем, как развернуть контейнер выберите стратегию изоляции. ВПО часто останавливается, как только в процессе Discovery увидит, хоть что-то подозрительное. К примеру, если процесс вызовет ptrace (PTRACE_SYSCALL, pid, 0, 0) на себя и получит ответ -1, то этот отказ будет индикатором, того что его уже исследуют. Здесь есть три варианта:

  • Стратегия 1: Блокировка запрещенных системных вызовов

    Контейнер (Namespaces, Cgroups, cap-drop) + Seccomp

    Базовая изоляция и лишение привилегий через гранулирование root, а также черный список системных вызовов, которые будут заблокированы через seccomp. Блокируем ключевые системные вызовы, которые могут быть использованы для эскалации привилегий или побега из контейнера.

  • Стратегия 2: Подмена ответов syscall

    Контейнер (Namespaces, Cgroups) + LD_PRELOAD

    Использовать eBPF-программы/библиотеки для критически важных системных вызовов, которые малварь использует для проверки окружения (uname, stat, openat для чтения /proc/..., sysinfo). Для этих вызовов программа не будет блокировать доступ, а будет подменять возвращаемые данные на "безопасные" и "ожидаемые" малварью.

  • Стратегия 3: Гибрид первой и второй стратегии

    Их комбинация позволит обойти большинство Anti-Debug проверок, LD_PRELOAD заменит ключевые возвращаемые значения, а Seccomp будет запрещать и выдавать кастомные ошибки ("errno": 2 // ENOENT - No such file or directory), вместо классической на отсутствие привилегий.

После выбора стратегии развернем контейнер, в заранее определим сеть, выделяемые ресурсы, где так же укажем профиль seccomp.

Сеть для Podman:

sudo podman network create -d bridge \
  --subnet 172.20.0.0/24 \
  --gateway 172.20.0.1 \
  --ip-range 172.20.0.0/24 \
  malwarenet

Многие ВПО любят проверять наличие systemd, так как в случае его отсутствия высока вероятность, что они запущены в виртуализованной среде. Превентивно выбираем podman вместо Docker из-за поддержки cgroup и возможности использовать systemd. Обязательно, добавим службу, а значит и пересоберем образ через Dockerfile:

FROM fedora:latest

# Установка Apache и systemd
RUN dnf -y install \
    httpd && dnf clean all

# Включение Apache
RUN systemctl enable httpd

# Экспорт порта
EXPOSE 80

# Сигнал остановки для systemd
STOPSIGNAL SIGRTMIN+3

# Запуск systemd
CMD ["/sbin/init"]

Сбилдим его командой:

sudo podman build -t fedora-systemd .

Так же в заранее подготовим конфигурационный файл Seccomp, который позволит снизить площадь атаки на ядро, но и не запретит работу systemd:

Seccomp конфиг
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "names": [
        "acct",
        "add_key",
        "bpf",
        "clock_adjtime",
        "clock_settime",
        "create_module",
        "delete_module",
        "finit_module",
        "get_kernel_syms",
        "get_mempolicy",
        "init_module",
        "ioperm",
        "iopl",
        "kcmp",
        "kexec_file_load",
        "kexec_load",
        "keyctl",
        "lookup_dcookie",
        "mbind",
        "mount",
        "move_pages",
        "name_to_handle_at",
        "nfsservctl",
        "open_by_handle_at",
        "perf_event_open",
        "personality",
        "pivot_root",
        "process_vm_readv",
        "process_vm_writev",
        "ptrace",
        "query_module",
        "quotactl",
        "reboot",
        "request_key",
        "set_mempolicy",
        "setns",
        "settimeofday",
        "stime",
        "swapon",
        "swapoff",
        "sysfs",
        "syslog",
        "_sysctl",
        "umount",
        "umount2",
        "unshare",
        "uselib",
        "userfaultfd",
        "ustat",
        "vhangup"
      ],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}

После чего поднимем контейнер, где ограничим его ресурсы, прикрутим сетку, выберем образ и поставим фильтрацию syscall через seccomp:

Podman-среда
  sudo podman run -d \
  --name malware-analysis-container \
  --network malware-net \
  --ip 172.20.0.100 \
  --security-opt seccomp=./file.json \
  --memory=1024m --cpus=1 \
  --systemd=always \
  localhost/fedora-systemd:latest /sbin/init

Как видим seccomp отлично фильтрует системные вызовы, не давая выполнить опасные функции.

Так как схема соединения в виртуальных сетях NAT, примерно, такова:

То сделаем для нашей среды DMZ. Во избежание атаки на локальные компоненты, вы можете вносить белый список, при расширении вашей лаборатории.

Iptables DMZ
#!/bin/bash

IFACE="eth0" # Основной интерфейс в интернет
NETWORK_INTERFACE=$(sudo podman network inspect malware-net -f '{{(index .Plugins 0).InterfaceName}}')
DOCKER_IFACE=$NETWORK_INTERFACE
CONTAINER_IP="172.20.0.100"

# Сброс правил
iptables -F
iptables -t nat -F
iptables -X
iptables -t nat -X

# Политики по умолчанию
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

# Разрешаем localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# DMZ правила
iptables -A FORWARD -s $CONTAINER_IP -o $IFACE -j ACCEPT
iptables -A FORWARD -d $CONTAINER_IP -i $IFACE -m state --state ESTABLISHED,RELATED -j ACCEPT

# Запрет LAN
iptables -A FORWARD -s $CONTAINER_IP -d 192.168.0.0/16 -j DROP
iptables -A FORWARD -s $CONTAINER_IP -d 10.0.0.0/8 -j DROP
iptables -A FORWARD -s $CONTAINER_IP -d 172.16.0.0/12 -j DROP

echo "DMZ zone for $CONTAINER_IP has been configured!"

В рамках Anti-Debug мер можно использовать eBPF-программу, которая заменит return для популярных discovery запросов на ложные. Однако не все вызовы возвращают ответ в syscall, там где данные превышают лимит - сразу записываются в userspace память процесса. Возникают сложности со смещениями, трудности в хранении больших данных и прочие. Однако можно использовать и его, но в данном кейсе мы рассмотрим механизм LD_PRELOAD.

Указав кастомную библиотеку через переменную LD_PRELOAD, мы сможем перехватить syscall за счет того, что загрузимся раньше системной библиотеки. Создаем файл с форматом file.c:

LD_PRELOAD
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include <dlfcn.h>

int (*orig_uname)(struct utsname *buf);

int uname(struct utsname *buf) {
    if (!orig_uname) {
        orig_uname = dlsym(RTLD_NEXT, "uname");
    }

    int ret = orig_uname(buf);

    strcpy(buf->sysname, "SuperSecureOS");
    strcpy(buf->nodename, "fakehost");
    strcpy(buf->release, "9.9.9");
    strcpy(buf->version, "v999");
    strcpy(buf->machine, "x86_999");
    strcpy(buf->domainname, "local.lan");

    return ret;
}

Сначала мы объявляем указатель на функцию, после перехватываем вызов, вызываем оригинальную функцию и полученный буфер меняем под собственные данные в контексте процесса.

Соберем библиотеку командой сразу в контейнере, чтобы совпадали версии glibc и контейнер не схлопнулся. Перекинем полученную библиотеку на хост и пересоберем образ:

gcc -shared -fPIC -ldl fake_uname.c -o libfakeuname.so

А после прикрепим к нашему контейнеру, заранее прокинув файл в него или можно создать новый образ через Dockerfile:

Podman-среда
  sudo podman run -d \
  --security-opt label=disable \
  --name malware-analysis-container \
  --network malware-net \
  --ip 172.20.0.100 \
  --security-opt seccomp=./file.json \
  --memory=1024m --cpus=1 \
  --env LD_PRELOAD=/lib1/libfakeuname.so \
  --systemd=always \
  localhost/fedora-systemd:latest /sbin/init

Запустим и проверим работоспособность:

Отлично, теперь ее можно использовать для подмены любых syscall, вы можете написать ложные ответы на самые популярные Discovery!

Сбор артефактов из телеметрии

На данном этапе мы проводим сбор артефактов TTP’s, которые содержатся в системных вызовах, сетевом трафике, а так же в хранятся файле. Для исследования будем использовать bpftrace, strings и mitmproxy/tcpdump.

Обратите внимание, так как мы проводим исследование в ограниченной среде, то необходимо захватить для фильтрации только процессы контейнера. Здесь это реализовано через cgroup, которое динамически подставляется в скрипте. Трассируем syscall через шаблон:

Шаблон трассировщика
/\\\* ===================== ПРОЦЕССЫ ===================== \\\*/
tracepoint:syscalls:sys_enter_execve
/ cgroup == $CGROUP_ID /
{
    printf("{\\\\"time\\\\":\\\\"%s\\\\",\\\\"probe\\\\":\\\\"%s\\\\",\\\\"pid\\\\":%d,\\\\"ppid\\\\":%d,\\\\"comm\\\\":\\\\"%s\\\\",\\\\"exec\\\\":\\\\"%s\\\\"}\\\\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           curtask->real_parent->tgid,   // <-- ppid
           comm,
           str(args->filename));
}

Для дампа трафика нам необходимо видеть внутренности не только обычных пакетов, но и зашифрованных SSL.

eCapture - это решение на eBPF, которое позволяет прикрепиться к конкретной пользовательской функции OpenSSL и ей подобным, для сбора трафика перед его шифрованием. Решение отличное, но не совсем подходящее. Очень важно, чтобы библиотека хоста совпадала с версией той, которая в контейнере и ВПО или инструмент использовал туже библиотеку, а не свою принесенную. Такое поведение возможно только в лабораторных условиях, поэтому ее можно использовать для некоторых случаев, когда все требования совпали.

Будем использовать mitmproxy и tcpdump, данные от которых сохраняем в файлы. Выбираем необходимый интерфейс, с которого будет идти трафик из нашего изолированного пространства. А для mitm proxy не забудем указать локальный адрес в bash.rc пользователя:

NETWORK_INTERFACE=$(sudo podman network inspect malware-net -f '{{(index .Plugins 0).InterfaceName}}')
sudo tcpdump -i $NETWORK_ID > ./net.txt &

Не забудем про открытые константы и вызываемые функции в бинарных файлах, а так же shellcode решений, соберем с них артефакты:

sudo strings filename > strings.txt

Объединим все решения выше в один скрипт для трассировки:

Запуск трассирования
#!/bin/bash
if [ $# -eq 0 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

if [ ! -f "$1" ]; then
    echo "File $1 does not exist!"
    exit 1
fi

CONTAINER_PID=$(sudo podman inspect --format '{{.State.Pid}}' $(sudo podman ps -q | head -n 1))
CGROUP_PATH=$(cut -d: -f3 /proc/$CONTAINER_PID/cgroup)
CGROUP_ID=$(stat -c %i /sys/fs/cgroup$CGROUP_PATH)
echo "Tracing container with PID=$CONTAINER_PID, cgroup inode=$CGROUP_ID"


cat <<EOF > ./script.bt
#!/usr/bin/env bpftrace

/* ===================== ПРОЦЕССЫ ===================== */
tracepoint:syscalls:sys_enter_execve
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"ppid\":%d,\"comm\":\"%s\",\"exec\":\"%s\"}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           curtask->real_parent->tgid,   // <-- ppid корректно
           comm,
           str(args->filename));
}

/* ===================== ФАЙЛЫ ===================== */
tracepoint:syscalls:sys_enter_openat
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"pathname\":\"%s\",\"flags\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           str(args->filename),
           args->flags);
}

tracepoint:syscalls:sys_enter_unlinkat
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"target\":\"%s\"}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           str(args->pathname));
}

tracepoint:syscalls:sys_enter_renameat
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"oldpath\":\"%s\",\"newpath\":\"%s\"}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           str(args->oldname),
           str(args->newname));
}

/* ===================== СЕТЬ ===================== */
tracepoint:syscalls:sys_enter_socket
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"domain\":%d,\"type\":%d,\"protocol\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->family,
           args->type,
           args->protocol);
}

tracepoint:syscalls:sys_enter_connect
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"fd\":%d,\"addrlen\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->fd,
           args->addrlen);
}

tracepoint:syscalls:sys_enter_accept
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"fd\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->fd);
}

/* ===================== UID/GID ===================== */
tracepoint:syscalls:sys_enter_setuid
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"uid\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->uid);
}

tracepoint:syscalls:sys_enter_setgid
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"gid\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->gid);
}

tracepoint:syscalls:sys_enter_setfsuid
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"fsuid\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->uid);
}

/* ===================== SECURITY-SENSITIVE ===================== */
tracepoint:syscalls:sys_enter_bpf
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"cmd\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->cmd);
}

tracepoint:syscalls:sys_enter_ptrace
/ cgroup == $CGROUP_ID /
{
    printf("{\"time\":\"%s\",\"probe\":\"%s\",\"pid\":%d,\"comm\":\"%s\",\"request\":%d,\"target\":%d}\n",
           strftime("%Y-%m-%d %H:%M:%S", nsecs),
           probe,
           pid,
           comm,
           args->request,
           args->pid);
}
EOF

echo "Start tracing..."
sudo bpftrace ./script.bt > ./syscall.txt &
echo "Start network dumping..."
NETWORK_INTERFACE=$(sudo podman network inspect malware-net -f '{{(index .Plugins 0).InterfaceName}}')
sudo tcpdump -i $NETWORK_ID > ./net.txt &
echo "Start strings search..."
sudo strings "$1" > ./strings.txt &

Синтаксис script <malware.file> требует вредоносное ПО, которое будет проверено strings и выведены строки, как и телеметрия по остальным трассировщикам.

Напишем нормализатор, который позволит текущие данные привести в единый вид и файл:

Нормализатор
import json
import argparse
from datetime import datetime
import requests

def parse_syscalls(sys_file):
    events = []
    with open(sys_file) as f:
        for line in f:
            try:
                ev = json.loads(line)
                ev["source"] = "syscall"
                events.append(ev)
            except json.JSONDecodeError:
                events.append({"source": "syscall", "raw": line.strip(), "parse_error": True})
    return events

def parse_network(net_file, date=None):
    events = []
    with open(net_file) as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            time_str, rest = line.split(" ", 1)
            if date:
                ts = f"{date} {time_str}"
            else:
                ts = datetime.now().strftime("%Y-%m-%d ") + time_str
            events.append({
                "time": ts,
                "source": "net",
                "raw": line
            })
    return events

def parse_strings(strings_file, date=None):
    events = []
    with open(strings_file) as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            ts = date + " 00:00:00" if date else datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            events.append({
                "time": ts,
                "source": "strings",
                "raw": line
            })
    return events

def merge_events(*lists):
    all_events = []
    for lst in lists:
        all_events.extend(lst)
    # сортировка по времени
    def parse_time(ev):
        try:
            return datetime.strptime(ev["time"], "%Y-%m-%d %H:%M:%S.%f")
        except ValueError:
            try:
                return datetime.strptime(ev["time"], "%Y-%m-%d %H:%M:%S")
            except:
                return datetime.min
    return sorted(all_events, key=parse_time)

def send_to_ai(events, api_key):
    url = "https://gpt.serverspace.ru/v1/chat/completions"
    payload = {
        "model": "openai/gpt-4o",
        "max_tokens": 16384,
        "top_p": 0.1,
        "temperature": 0.6,
        "messages": [
            {
                "role": "user",
                "content": json.dumps(events)
            }
        ]
    }
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    r = requests.post(url, headers=headers, data=json.dumps(payload))
    return r.json()


parser = argparse.ArgumentParser()
parser.add_argument("--sys", required=True, help="Syscall JSONL file")
parser.add_argument("--net", required=True, help="Network log txt file")
parser.add_argument("--strings", required=True, help="Strings output file")
parser.add_argument("--date", help="Date for network/strings events YYYY-MM-DD")
parser.add_argument("--out", help="Output JSONL file")
parser.add_argument("--api_key", help="GPT API key")
args = parser.parse_args()

sys_events = parse_syscalls(args.sys)
net_events = parse_network(args.net, date=args.date)
str_events = parse_strings(args.strings, date=args.date)

all_events = merge_events(sys_events, net_events)

if args.out:
    with open(args.out, "w") as f:
        for ev in all_events:
                f.write(json.dumps(ev) + "\n")
else:
    for ev in all_events:
        print(json.dumps(ev))

if args.api_key:
    result = send_to_ai(all_events, args.api_key)
    print(json.dumps(result, indent=2))

Преимущество подобной лабы в подробном логе, который остается после дампа, но аналитика все равно нужна. Как аналог движков будем использовать GPT-4o, которая подсветит TTP’s и суммаризирует данные.

Синтаксис достаточно прост, необходимо указать пути к вердиктам и API, при запуске скрипта:

python normalize_and_send.py \
  --sys syscalls.jsonl \
  --net net.txt \
  --strings malware_strings.txt \
  --date 2025-09-02 \
  --out merged.jsonl \
  --api_key "YOUR_API_KEY"

После выполнения получим следующий результат:

В файле лежит полноценный лог активности процесса в ОС Linux с timestamp, который отображает потенциальные артефакты системной, сетевой коммуникации, а так же вердикт от AI о вредоносных событиях. Данное решение не является конечным, безусловно, можно докрутить движки, оптимизировать код и автоматизировать развертку. Однако оно отлично подходит для решения операционных задач по исследованию сэмплов на вредоносную активность!

Статья поддерживается командой Serverspace.

Serverspace — провайдер облачных сервисов, предоставляющий в аренду виртуальные серверы с ОС Linux и Windows в 8 дата-центрах: Россия, Беларусь, Казахстан, Нидерланды, Турция, США, Канада и Бразилия. Для построения ИТ-инфраструктуры провайдер также предлагает: создание сетей, шлюзов, бэкапы, сервисы CDN, DNS, объектное хранилище S3.

IT-инфраструктура | Удвоение первого платежа по коду HABR

Комментарии (0)