Систем обнаружения вторжений сейчас существует великое множество. Какие-то из них более известны, какие-то менее. Одни предназначены для маленьких сетей, другие для крупных корпораций. Чаще всего в контексте IDS вспоминают Snort, однако это не единственная свободная система подобного рода. Одну из альтернатив, Bro, мы и опишем.

 

Введение

Bro начал разрабатываться в середине девяностых годов в ICSI, международном институте компьютерных наук, который входит в состав знаменитого Калифорнийского университета в Беркли. Спустя какое-то время проект стал использоваться NCSA — тем самым центром суперкомпьютерных приложений, где был создан предтеча Apache, NCSA HTTPd. С 2010 года NCSA активно участвует в разработке данного проекта. На текущий момент проект имеет версию 2.3.2, что говорит о его зрелости. Еще больше о ней говорит тот факт, что в NCSA Bro используется в качестве ключевой части инфраструктуры безопасности.

Bro имеет следующие особенности:

  • Гибкость. Используется скриптовый язык, позволяющий установить для каждого защищаемого объекта свои правила мониторинга. Кроме того, Bro изначально не заточен на обнаружение каких-либо конкретных атак, отсутствует зависимость от сигнатур.
  • Эффективность. Bro рассчитан на работу в сетях с очень большим объемом трафика и может быть использован в различных больших проектах. В частности, поддерживается распределенная архитектура.
  • Глубокий анализ трафика. Поддерживаются анализаторы для множества протоколов, что позволяет осуществить высокоуровневый семантический анализ даже на уровне приложений. При этом поддерживается запоминание состояния соединения опять же до уровня приложений включительно.

Bro представляет собой фреймворк для создания сетевой IDS/IPS и имеет многоуровневую модульную структуру. Перечислим эти уровни:

  • Механизм захвата пакетов. Де-факто этот механизм практически всегда использует для данных целей libpcap, что позволяет Bro не зависеть от платформы и от нижележащего сетевого уровня.
  • Механизм событий (Event Engine, также его называют Core) преобразует пришедшие последовательности пакетов в первичные события. События эти отражают базовые сведения о сетевой активности — так, каждый HTTP-запрос порождает соответствующее событие, которое описывает адрес, порт, запрашиваемый URI и версию протокола HTTP. Этот механизм, однако, не принимает никаких решений относительно окраса события — то есть на данном уровне неизвестно, вредоносное оно или нет.
  • А вот собственно самый верхний уровень, интерпретатор скриптов (Policy Script Interpreter), этим и занимается — на каждое событие, на которое нужно реагировать, регистрируется его обработчик, соответствующий определенному скрипту. События ставятся в очередь FIFO. Скрипты же определяют действия, использующиеся для обнаружения вредоносного трафика, а также политику, применяемую при его обнаружении, и пишутся на собственном скриптовом языке Bro.

Практически же данная платформа может использоваться отнюдь не только для построения IDS, но и для прочего анализа трафика. Он может, в принципе, заменять (и/или дополнять) Wireshark, выделяя и анализируя только необходимый трафик. Последние его версии включают экспериментальную поддержку ElasticSearch, движка полнотекстового поиска.

IDS доступен как в виде исходного кода, так и в виде бинарных пакетов, последние — только под Mac OS X 10.9, Debian 6 и CentOS 6, на который мы его и установим.

INFO


При соответствующей конфигурации Bro способен обрабатывать трафик объемом 10 Гбит/с и выше.

Установка Bro. Подготовка к использованию

Скачаем Bro:

$ wget https://www.bro.org/downloads/release/Bro-2.3.2-Linux-x86_64.rpm
Скачивание Bro
Скачивание Bro

Повысив привилегии и перейдя в каталог загрузки, устанавливаем его:

# yum install ./Bro-2.3.2-Linux-x86_64.rpm
Установка
Установка

На удивление, никаких зависимостей ставить не понадобилось. После установки нужно добавить путь к исполняемым файлам Bro в общесистемную переменную PATH, для чего в каталоге /etc/profile.d/ файл bro.sh со следующим содержимым:

export PATH=/opt/bro/bin:$PATH

Затем нужно установить базовые параметры конфигурации. Для standalone-конфигурации в файле /opt/bro/etc/node.cfg достаточно указать интерфейс, на котором Bro будет слушать, — в моем случае (тестовая конфигурация) это был eth1:

[bro]
type=standalone
host=localhost
interface=eth1

В файле /opt/bro/etc/networks.cfg указываем сеть или сети, которые нужно будет защищать:

192.168.13.0/24     Private IP space

где Private IP space — необязательный комментарий.

Наконец, в файле /opt/bro/etc/broctl.cfg указываем интервал ротации логов (в секундах), интервал, по истечении которого они удаляются (в днях), и, при необходимости, email, куда отправлять письма:

MailTo = root@localhost
# <...>
LogRotationInterval = 3600
LogExpireInterval = 30
Конфигурационный файл broctl
Конфигурационный файл broctl

Запустим консоль управления и уже из нее сам Bro с дефолтными скриптами:

# broctl
[BroControl] > install
[BroControl] > start
Первый запуск Bro
Первый запуск Bro

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

Отмечу, что Bro требуется возможность контролировать интерфейс для включения неразборчивого режима, поэтому приложение Bro должно запускаться либо от суперпользователя, либо с установленными капабилити CAP_NET_RAW и CAP_NET_ADMIN.

Из консоли broctl можно выходить, но при этом запущенный Bro не останавливается — для его остановки нужно набрать команду stop. К слову, вместо вызова команд из консоли broctl их можно передавать в качестве аргументов — например, broctl stop.

 

Скриптовый язык Bro. Стандартные скрипты

Как уже писалось, Bro работает на основе скриптов. Событийно ориентированный язык написания скриптов — основное средство расширения и гибкой настройки функционала данной платформы. Проще всего рассматривать Bro в качестве негласной цензуры, докладывающей о событиях определенным скриптам. Скрипты указывают Bro, на что они должны реагировать, Bro же в ответ предоставляет всю имеющуюся информацию о данном соединении, над которым скрипт может произвести некоторые действия. Так, скрипт, генерирующий ssl.log, проходит по всей цепочке сертификатов и, если какой-то из них некорректен, уведомляет об этом. А начинает он обработку тогда, когда клиент (или сервер) выполняет запрос SSL HELLO — при этом, разумеется, скрипт явно определяет свой интерес к соединениям подобного рода.

Типы данных и атрибуты

Стоит рассмотреть некоторые типы данных, используемые при создании скриптов Bro.

  • bool — логический тип, может принимать значения T или F. Что они означают, думаю, в пояснении не нуждаются.
  • time — тип абсолютного времени. На данный момент нет возможности задать его константе напрямую, но можно преобразовать из некоторых других типов, используя такие функции, как double_to_time(), current_time() и network_time(). Допускается сравнение переменных данного типа и их вычитание — в результате последнего получается тип interval.
  • interval — тип относительного времени. Пример его применения есть в основном тексте статьи. Переменные типа могут быть и отрицательными. Допускается сравнение, сложение, вычитание, деление (в результате чего получается тип double) и присваивание данного типа. Также возможно умножение и деление переменных этого типа на переменные арифметических типов — на выходе получаем опять-таки interval.
  • record — де-факто структура. Доступ к членам структуры осуществляется с помощью знака $. Структуры могут быть вложенными.

Помимо типов, в скриптовом языке есть еще и атрибуты, предназначенные для изменения поведения тех или иных типов. Как правило, они ставятся после объявления переменных. Рассмотрим и их тоже.

  • &optional означает, что поле структуры может быть необязательным для заполнения.
  • &default= определяет значение по умолчанию, если таковое не будет указано при инициализации.
  • &redef дает возможность переопределять значения переменных и констант — последнее чаще всего используется для создания политик, так как константы (в контексте Bro), как правило, должны быть переопределяемыми.
  • &persistent делает переменную сохраняемой на диск.
  • &priority= указывает приоритет хука или обработчика событий. Большие значения подразумевают, соответственно, больший приоритет. Приоритет по умолчанию — 0.

Разумеется, это отнюдь не все типы и атрибуты, которые есть в Bro.

В скриптах поддерживаются и инклуды — можно подключать внешние модули. Имеется богатейший набор типов и структур данных, которые могут использоваться в скриптах. Рассмотрим, с чего начинается выполнение скриптов в случае запуска в Standalone-конфигурации.

Начинается оно с обработки local.bro, в котором указаны все остальные скрипты (за исключением обязательных, лежащих в каталоге /opt/bro/share/bro/base/ и выполняемых в любом случае) и который можно редактировать. Посмотрим небольшой кусочек данного файла:

# Скрипт, логирующий остальные загружаемые скрипты
@load misc/loaded-scripts
# Скрипты, задающие стандартные параметры некоторых тонких настроек
@load tuning/defaults
# Скрипт обнаружения сканирования
@load misc/scan
# Логируем некоторую информацию о веб-приложениях, используемых в данной сети
@load misc/app-stats
# <...>
# Обнаруживает софт, применяемый для различных протоколов
@load protocols/ftp/software
@load protocols/smtp/software
@load protocols/ssh/software
@load protocols/http/software
# <...>
# Обнаруживает попытки SQL-инъекций
@load protocols/http/detect-sqli
# Считываем хеши всех файлов, которые проходят через Bro, и отправляем их в сервис Detect-MHR (Malware Hash Registry)
@load framework/files/hash-all-files
@load framework/files/detect-MHR
# <...>

Рассмотрим некоторые из загружаемых скриптов более подробно. Возьмем для примера скрипт /opt/bro/share/bro/policy/protocols/ftp/software.bro:

## Загружаем базовую часть обнаруживателя
@load base/frameworks/software
## Регистрируем модуль в ядре Bro
module FTP;
## Экспортируемые типы и функции
export {
    ## Переопределяем перечисление с целью добавления двух элементов
    redef enum Software::Type += {
        ## Идентификатор для FTP-клиентов в фреймворке обнаружения
        CLIENT,
        ## Аналогичный функционал для серверной стороны пока не реализован
        SERVER,
    };
}

## Срабатываем на событие ftp_request — приоритет обработчика при этом несколько выше обычного
event ftp_request(c: connection, command: string, arg:string) &priority=4
    {
    ## Реагируем на команду CLNT
    if (command == "CLNT")
            {
            ## Функция обнаружения ПО, импортированная из фреймворка. Принимает параметр «идентификатор соединения» и информацию о софте — последняя формируется в виде структуры, доступ к членам которой, к слову, осуществляется через знак доллара — то есть члену структуры info, host, присваивается значение поля orig_h структуры id, которая, в свою очередь, является членом структуры c — connection
            Software::found(c$id, [$unparsed_version=arg, $host=c$id$orig_h, $software_type=CLIENT]);
            }
    }
 

Скриптовый язык Bro. Пример посложнее

В целом здесь все более-менее ясно. Однако это один из самых простых скриптов. Давай разберем еще один скрипт, который реагирует на попытки сканирования (/opt/bro/share/bro/policy/misc/scan.bro).

Настройка опций сканирования во фронтенде для Nmap
Настройка опций сканирования во фронтенде для Nmap

Не буду приводить весь скрипт целиком, лишь ту его часть, которая отвечает за обнаружение сканирования портов:

# <...>
## Загружаем модули предупреждения и статистики
@load base/frameworks/notice
@load base/frameworks/sumstats
# <...>
## Экспортируемые переменные и константы
export {
    # <...>
    ## Для обнаружения неудачных попыток соединения с тем или иным портом эти попытки должны укладываться в заданный интервал времени. Если этот интервал будет избыточным, могут происходить ложные срабатывания. Переопределяемый
    const port_scan_interval = 5min &redef;
    # <...>
    ## Пороговое количество уникальных портов на одном хосте, после превышения которого в заданном интервале времени должно происходить обнаружение сканирования 
    const port_scan_threshold = 15.0 &redef;
    # <...>
}

event bro_init() &priority=5
{
    # <...>
    ## При запуске Bro мы создаем преобразователь данных (Reducer), уменьшающий объем данных, поставляемый в потоке наблюдения (observation stream) под именем scan.port.fail. Reducer в данном случае выбирает исключительно уникальные попытки, причем выбор зависит от порогового количества уникальных портов
    local r2: SumStats::Reducer = [$stream="scan.port.fail", $apply=set(SumStats::UNIQUE), $unique_max=double_to_count(port_scan_threshold+2)];
    ## Создаем также общую статистику с порогом, после превышения которого вызывается колбэк-функция, производящая определенные действия — в данном случае формирующая уведомление о том, что за определенное время ($epoch) порог сканирования уникальных портов был кем-то превышен
    SumStats::create([$name="port-scan", ## Имя общей статистики
        $epoch=port_scan_interval, ## Интервал времени, в течение которого будет собираться статистика. По его истечении она сбрасывается
        $reducers=set(r2), ## Набор преобразователей, которые используются для создания общей статистики
        $threshold_val(key: SumStats::key, result: SumStats::Result) = ## Предоставляет функцию, которая считает некую величину, необходимую для внутреннего измерения пороговой величины
            {
            return result["scan.port.fail"]$unique+0.0;
            },
        $threshold=port_scan_threshold, ## Пороговая величина, по достижении которой вызывается колбэк-функция
        $threshold_crossed(key: SumStats::key, result: SumStats::Result) = ## Наконец, определяем колбэк-функцию, срабатывающую по достижении порога
            {
            local r = result["scan.port.fail"];
            local side = Site::is_local_addr(key$host) ? "local" : "remote"; ## Устанавливаем, откуда идет сканирование — со стороны внешнего мира или же из локальной сети
            local dur = duration_to_mins_secs(r$end-r$begin); ## Длительность попыток сканирования
            local message = fmt("%s scanned at least %d unique ports of host %s in %s", key$host, r$unique, key$str, dur); ## Формируем сообщение для NOTICE
            NOTICE([$note=Port_Scan,
                $src=key$host,
                $dst=to_addr(key$str),
                $sub=side, ## Нужно для политики предупреждений
                $msg=message,
                $identifier=cat(key$host)]); ## Вызываем обертку из фреймворка Notice, которая затем вызывает внутреннюю функцию, что в конечном счете приводит к появлению предупреждения
            }]);
}
## Функция, вызываемая для получения потока наблюдения (observation stream)
function add_sumstats(id: conn_id, reverse: bool)
    {
    local scanner = id$orig_h; ## Хост, с которого производится соединение
    local victim = id$resp_h; ## Хост, к которому подключаются
    local scanned_port = resp_p; ## Порт назначения
    # <...>
    if ( hook Scan::port_scan_policy(scanner, victim, scanned_port) ) ## Если срабатывает данный хук (а он, судя по всему, должен срабатывать практически всегда), мы фиксируем данные в наблюдении. Первый параметр — имя наблюдения, второй — ключ, ну а третий — собственно собираемые данные
        SumStats::observe("scan.port.fail", [$host=scanner, $str=cat(victim)], [$str=cat(scanned_port)]);
    }
# <...>
## Событие, при котором происходит сбор данных с помощью функции, описанной выше
event connection_attempt(c: connection) 
    {
    ## Проверяем, не является ли сканирование обратным (reverse), для чего смотрим, была ли завершена процедура «рукопожатия» (handshake) при установлении соединения
    local is_reverse_scan = F;
    if ( "H" in c$history )
        is_reverse_scan = T;

    ## Вызов функции сбора общей статистики
    add_sumstats(c$id, is_reverse_scan);
    }
# <...>
Сканирование (используется один из стандартных профилей)
Сканирование (используется один из стандартных профилей)

Скрипт, как видим, достаточно сложен и демонстрирует кусочек тех поистине невероятных возможностей, что скрыты в Bro. В скрипте (точнее, той его части, что была показана выше) были использованы как минимум десять типов данных и структур, часть из которых вложенная. Кроме того, было использовано мощнейшее средство Bro — статистический анализ потока данных. Как видим, фреймворк недаром разрабатывался в академических кругах — его вполне можно применять не только для обнаружения вторжений, но и для анализа трафика.

При попытке сканирования появляется предупреждение в логе notice (по умолчанию он находится в каталоге с датой в /var/opt/bro/logs/).

Лог-файл с уведомлением о сканировании
Лог-файл с уведомлением о сканировании

Платформа построена таким образом, что в случае нагрузки часть пакетов отбрасывается — это позволяет избежать перегрузки. Однако по моим тестам (проводились на VirtualBox) в случае использования более-менее современной двухъядерной машины и интенсивного пинг-флуда часть отбрасываемых пакетов составила примерно 0,0085%. Понятно, что в реальных ситуациях нагрузка будет больше и не настолько равномерная, но в целом можно прикинуть количество отбрасываемых пакетов.

Заключение

Платформа Bro предоставляет богатейший набор возможностей для анализа трафика. Помимо обычного анализа заголовков пакетов (о котором даже не имело смысла упоминать — это базовое свойство подобных систем), здесь имеются и регулярные выражения, и сохранение состояния высокоуровневых соединений, и даже статистический анализ (последний явно указывает на академическое происхождение Bro).

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

Есть у Bro и еще минусы, один из которых — недостаток хоть какого-нибудь GUI. В недрах LBNL он, судя по одной из презентаций, имеется — хотя и предоставляет, по сути, лишь удобный доступ к логам. Второй же минус — отсутствие до недавнего времени поддержки баз данных. Все сообщения писались в текстовые файлы, что для промышленных объемов трафика сейчас не очень-то удобно. Однако дело сдвинулось с мертвой точки — в последних версиях Bro появилась экспериментальная поддержка ElasticSearch.

В общем и целом, если ты специалист по ИБ и/или тебе нужно автоматизировать анализ гигантских объемов трафика — о Bro стоит иметь представление хотя бы по той причине, что он свободен и бесплатен, в то время как коммерческие аналоги при сходной (или даже чуть меньшей) функциональности стоят неподъемных денег.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    2 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии