Всем привет!

Однажды, много лет назад, во времена эпидемии вируса Code Red, я работал сисадмином в одном вузе и невежливо ответил на электронное письмо из какого то-банка с требованием немедленно прекратить атаку на их веб сервер, работающий под управлением Apache, через месяц пришло бумажное письмо от их службы безопасности, и завертелось. Так я познакомился с системой Snort, которая некоторое время защищала Интернет от наших студентов. Со временем, большая часть трафика стала https, и решение потеряло смысл, но, есть места, где он снова превращается в http, например после https прокси. Тут то мы и сможем его “подсмотреть”, выявить атаки и заблокировать злоумышленника!

Что бы было интереснее, не будем рассматривать реальный кейс, а воспроизведем все “в домашних условиях”. На единственной системе развернем https прокси, веб приложение за ним, и защитим от атак на основе анализа содержимого http запросов. Потребуется VirtualBox (или любой другой гипервизор) и дистрибутив Linux, например Ubuntu, там Snort имеется в виде пакета. Можно взять мой готовый ova образ:

https://val.bmstu.ru/unix/img/My%20Documents/ubuntu_24.04_01.ova

и выполнить 1-й шаг из этой статьи:

Шаг 2. Разворачиваем http приложение

Можно развернуть что-нибудь поинтереснее, но, сути это не меняет, поэтому обойдемся простым вариантом:

$ sudo -i
# apt update
# apt install docker.io
# docker run --name http-app -d --restart=always httpd
# docker inspect http-app -f {{.NetworkSettings.IPAddress}}
# curl http://172.17.0.2

http приложение готово!

Шаг 2. Создаем сертификат для https прокси

Для “игрушечного” примера подойдет само подписанный сертификат c атрибутами “по умолчанию”

# openssl genrsa -out /etc/ssl/private/http-app.key 2048
# openssl req -new -x509 -days 3650 -key /etc/ssl/private/http-app.key -out /etc/ssl/certs/http-app.crt

На все “вопросы” последней команды достаточно нажать Enter (даже про Common Name, поскольку тестировать все будем по ip адресу)

Шаг 3. Разворачиваем https прокси

В качестве https прокси рассмотрим nginx (можно haproxy, суть не изменится). Поскольку тестировать все будем по ip адресу, настроим перенаправление любых запросов (server_name _) на 443 порт в наше http приложение. X-Forwarded-For в нашем случае нужен потому, что только в этом заголовке будет ip адрес клиента в трафике между https прокси и приложением.

# apt install nginx

# nano /etc/nginx/sites-available/http-app

    server {
        listen 443 ssl;
        server_name  _;
        ssl_certificate /etc/ssl/certs/http-app.crt;
        ssl_certificate_key /etc/ssl/private/http-app.key;

        location / {
            proxy_pass http://172.17.0.2;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

# ln -s /etc/nginx/sites-available/http-app /etc/nginx/sites-enabled/http-app

# systemctl restart nginx.service

Теперь наше http приложение должно быть доступно по URLhttps://ip_адрес_виртульной_машины/ правда, браузер будет ругаться на сертификат и придется “перейти на сайт небезопасно”

Шаг 4. Разворачиваем IDS Snort

# apt install snort

В вопросах инсталлятора про сетевые интерфейсы указываем docker0, в нашем случае именно через него идет трафик между https прокси и http приложением в контейнере. Остальные ответы “по умолчанию”, результат работы инсталлятора записывается в файл:

# less /etc/snort/snort.debian.conf

...
DEBIAN_SNORT_INTERFACE="docker0"
...

В основном файле конфигурации отключаем проверку контрольной суммы пакетов, поскольку эта функциональность актуальна только для “входящего” трафика, а в нашем случае, http трафик будет “выходить” из интерфейса docker0, добавляем опцию анализа заголовка X-Forwarded-For в http трафике и настраиваем вывод сообщений об обнаруженных атаках в syslog с типом сообщений auth и уровнем важности alert:

# nano /etc/snort/snort.conf

...
# Configure IP / TCP checksum mode
config checksum_mode: none
...
preprocessor http_inspect_server: server default \
...
    enable_xff \
    webroot no
...
####################################################################
# Step #6: Configure output plugins
...
output alert_syslog: LOG_AUTH LOG_ALERT
...

# systemctl restart snort.service

Для тестирования будем использовать тот самый злополучный Code Red, для которого, как раз, есть правило “из коробки”:

# less /etc/snort/rules/web-iis.rules

...
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"WEB-IIS CodeRed v2 root.exe access"; flow:to_server,established; uricontent:"/root.exe"; nocase; reference:url,www.cert.org/advisories/CA-2001-19.html; classtype:web-application-attack; sid:1256; rev:8;)
...

Симулировать атаку можно обращаясь к нашему приложению по URLhttps://ip_адрес_виртульной_машины/root.exe

В журнале должны появляться характерные сообщения:

# tail -f /var/log/auth.log

...
2024-05-12T18:06:07.467018+00:00 ubuntu snort[6978]: [1:1256:8] WEB-IIS CodeRed v2 root.exe access [Classification: Web Application Attack] [Priority: 1] {TCP} 172.17.0.1:40890 -> 172.17.0.2:80
...

Но, стоп, в них отсутствует адрес клиента, здесь только адрес прокси и самого приложения!

Оказывается, информация из заголовка X-Forwarded-For попадает в двоичный файл, для анализа которого авторы Snort добавили специальную утилиту:

# u2spewfoo /var/log/snort/snort.alert

...
priority: 1     ip source: 172.17.0.1   ip destination: 172.17.0.2
...
type: 1 datatype: 1     bloblength: 12  Original Client IP: 192.168.1.100
...

Этот файл создается и непрерывно растет с момента запуска Snort, а утилита умеет выводить его только целиком. Для нашей следующей задачи будет удобнее, если полученная таким образом информация будет тоже последовательно писаться в журнал. Пришлось “сочинить” такого “крокодила” (ключи -с +1 команды tail из-за двоичного формата файла, u2spewfoo не умеет читать из STDIN, stdbuf отключает буферизацию ввода и вывода, logger все пишет в syslog от имени приложения snort с типом сообщений auth и уровнем важности info):

# stdbuf -i0 -o0 u2spewfoo <(tail -c +1 -f /var/log/snort/snort.alert) | logger -t snort -p auth.info

В журнале теперь будут появляться такие сообщения (понадобится вторая консоль):

# tail -f /var/log/auth.log

...
2024-05-12T18:16:25.965360+00:00 ubuntu snort: #011type: 1#011datatype: 1#011bloblength: 12#011Original Client IP: 192.168.1.100
...

Можно оформить запуск “крокодила” через systemd

# nano /etc/systemd/system/snort-alert-unified2-syslog.service

[Unit]
Description=Send snort alert_unified2 to syslog
After=snort.service

[Service]
ExecStart=/bin/bash -c '/usr/bin/stdbuf -i0 -o0 /usr/sbin/u2spewfoo <(/usr/bin/tail -c +1 -f /var/log/snort/snort.alert) | /usr/bin/logger -t snort -p auth.info'

[Install]
WantedBy=multi-user.target

# systemctl enable snort-alert-unified2-syslog --now

Шаг 5. Превращаем IDS в IPS

Теперь в журнале поваляются адреса клиентов, сигнатуры пакетов которых попадают в правила Snort (с правилами все печально, Snort принадлежит Cisco, были платные подписки или бесплатно с месячной задержкой, теперь только Community rules, в составе пакета, ну, и, можно писать свои)

Для чтения журнала и блокировки злоумышленников будем использовать fail2ban

Вообще, он ставится командой:

# apt install fail2ban

но, на текущий момент (май 2024) в 24-й ubuntu он не работает (надеюсь, починят) и приходится устанавливать его так:

# wget https://launchpad.net/ubuntu/+source/fail2ban/1.1.0-1/+build/28291332/+files/fail2ban_1.1.0-1_all.deb
# dpkg -i fail2ban_1.1.0-1_all.deb 

В любом случае, конфигурация выглядит так:

# nano /etc/fail2ban/filter.d/snort_filter.conf

[Definition]
failregex = .*Original Client IP: <HOST>.*

# nano /etc/fail2ban/jail.d/snort_jail.conf

[snort]
enabled     = true
bantime     = 300
filter      = snort_filter
maxretry    = 3
logpath     = /var/log/auth.log
action      = iptables-allports

# systemctl restart fail2ban.service

Вот тут я “забанил” сам себя и потерял history команд при подготовке статьи, так что лучше продолжать “атаки” с другого домашнего устройства, например с телефона :)

https://ip_адрес_виртульной_машины/root.exe

В журнале можно наблюдать реакцию fail2ban на “атаки”:

# tail -f /var/log/fail2ban.log

А также снять блокировку:

# fail2ban-client status snort
# fail2ban-client set snort unbanip <IP>

Спасибо что дочитали, надеюсь было интересно, буду рад ответить на вопросы!

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


  1. Shaman_RSHU
    13.05.2024 18:36
    +2

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

    А почему вы под DPI понимаете совсем другую технологию?


    1. valvalva Автор
      13.05.2024 18:36

      Даже, не знаю, как бы решал эту задачу сегодня, ну установил компьютер студента https соединение с банком, а что он делает в этом соединении, cvv2 передает или уязвимость эксплуатирует, как узнать? Поэтому, да, согласен, статья про то. как решение DPI переехало из универа в банк и превратилось в IDS/IPS :)


      1. Shaman_RSHU
        13.05.2024 18:36

        Получается, что IDS/IPS не предотвращает вторжение в периметр, а не даёт вырваться "диким" студентам :)


        1. valvalva Автор
          13.05.2024 18:36

          Студенты у нас были нормальные, вирусы были "дикие" )