Pull to refresh

Защита от DDOS атаки подручными средствами. Получение доступа к своему серверу

Information Security *
Sandbox
За последнее время, наш сайт часто подвергается достаточно мощным DDOS атакам, к слову последняя атака была самой крупной за последнее время, размер ботнета по нашим оценкам — около 10 тысяч машин, мощность — 100 Mbits/s.

Атаку заметила даже Лаборатория Касперского, и предложила свою помощь в отражении, за что им спасибо. Правда к тому времени мы самостоятельно нашли решение, которое блокирует атаку. Собственно про это решение и пойдет речь.

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

Атака была типа HTTP Flood. Система на которой у нас работает сайт — Apache под Linux. Мы написали несколько скриптов, которые будут приведены в тексте статьи. В принципе аналогичный подход можно применять и для Windows/IIS.

Попытаюсь рассказать, какие основные шаги мы сделали для отражения атаки, и какие проблемы возникали по ходу:

Получение доступа к своему серверу


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

После того как получилось зайти в систему, и выключить апач (service httpd stop) нужно убрать запуск апача при старте системы. Это даст возможность получить доступ к машине с помощью перезагрузки, если что-либо пойдет не так. Делается это с помощью команды:

# chkconfig httpd off

Решение не идеальное, но для начала пойдет.

Автоматическая блокировка атакуемого сервиса при высокой загрузке системы


Перезагружать сервер, при каждой новой волне атаки, довольно плохое решение, т.к. это все время, которое играет против нас.

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

Собственно был написан универсальный скрипт который делает задуманное. Вызов скрипта следующий:

# blockOnHighLoad.sh turnOn80Port 5 turnOff80Port

Скрипту передаются две команды, и максимальный уровень загрузки системы при котором нам нужно что-либо предпринять. В данном случае при достижении load average значения 5, запускается команда, которая перекрывает файрволом 80-й порт. При возвращении загрузки на нормальный уровень, порт снова открывается.

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

blockOnHighLoad.sh "/sbin/iptables -D INPUT -p tcp -m tcp --dport 80 -j DROP" 5 "/sbin/iptables -A INPUT -p tcp -m tcp --dport 80 -j DROP"

Т.е. мы напрямую блокируем/разблокируем порт с помощью iptables.

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

Так наряду с блокировкой единственного порта, полезно бывает полностью «закрыть» машину, оставив только порт для подключения по SSH. Такое действие целесообразно проводить при «уходе» загрузки в «космос». Вот вторая полезная команда:

blockOnHighLoad.sh "" 50 "blockAllExcept.sh 22"

Тут по достижении значения load average в 50 единиц, мы закрываем все что можно, за исключением SSH (22-й порт). Открытие порта производится в данном случае в ручном режиме.

Теперь осталось это все хозяйство вставить в автозагрузку системы. Плюс нужно запустить выключенный httpd. Для этого мы дописали следующие команды в скрипт инициализации /etc/rc.local:

blockOnHighLoad.sh "/sbin/iptables -D INPUT -p tcp -m tcp --dport 80 -j DROP" 5 "/sbin/iptables -A INPUT -p tcp -m tcp --dport 80 -j DROP" >> /var/log/blockOnHighLoad-5-80.logs  2>&1 &
blockOnHighLoad.sh "" 50 "blockAllExcept.sh 22" >> /dev/null &
service httpd start

Почему не оставить нормальный запуск httpd через системный старт сервисов? Потому что он запустится перед автоблокировщиком, и при «хорошей» атаке сразу положит систему и скрипт инициализации может не выполнится.

Отмечу, что скрипт печатает небольшой диагностический лог. Выводятся сообщения, когда происходит переключение «режима» работы и печатается текущая загрузка и режим. В
примере выше, этот лог сохраняется в файл /var/log/blockOnHighLoad-5-80.logs. Так что можно посмотреть историю.

Что дальше?


После того как мы получили доступ к машине, мы провели анализ атаки, написали скрипт который автоматически банит ботов. Тут стоит отметить, что при количестве блокируемых IP более нескольких сотен, вариант с iptables, не проходит. Потому как iptables крайне не эффективно работает с большими списками. iptables нужно использовать в связке с ipset, который как раз заточен на хранение и поддержку больших списков IP для iptables. Почитать про детали можно в этой статье.

Надеемся что наш опыт поможет в борьбе с атаками.

Спасибо за внимание.

Собственно скрипты:

blockOnHighLoad.sh
#!/bin/bash

state=normal
i=0
while [ 1 ]; do

        up=`uptime`
        loadavg=`echo $up | sed 's/.*average: //' | sed 's/,.\+//' | sed 's/\..\+//'`

        i=$(($i+1))
        if ((  $i > 60 )); then
                echo
                echo -n `date` " "
                i=0
        fi

        if (( $loadavg > $2 ));then
                if [ "$state" == "normal" ];then
                        state=high
                        echo
                        echo `date` HIGH $3
                        $3
                else
                        echo -n ${loadavg}H
                fi
        else
                if [ "$state" == "high" ];then
                        state=normal
                        echo
                        echo `date` Normal $1
                        $1
                else
                        echo -n ${loadavg}.
                fi
        fi
        sleep 1
done


blockAllExcept.sh (Скрипт взят отсюда)
#!/bin/sh
# Minimal emergency firewall (block everything except SSH).
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -F
iptables -X
iptables -A INPUT -p tcp -m tcp --dport $1 -j ACCEPT
iptables -P INPUT DROP
iptables -A OUTPUT -p tcp -m tcp --sport $1 -j ACCEPT
iptables -P OUTPUT DROP
Tags:
Hubs:
Total votes 106: ↑63 and ↓43 +20
Views 75K
Comments Comments 92