Страшный сон пользователей Apache

Пять дней назад в листе рассылки Full Disclosure появился скрипт, по заявлению автора, убивающий Apache начиная от самых старых версий до самых новых.

И как удалось выяснить на собственной шкуре он реально работает Скрипт 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 даже на приличных серверах.

Разработчики Apache подошли к этой проблеме серьёзно, инициативные лица уже предложили изменения в RFC, закрывающие эту уязвимостью.

На нашем сайтике это выглядело следующим образом:
В геометрической прогрессии начало расти количество гостей, до 5000 шт. после чего Апач сидящий на Virtual CPU 300Mhz x 2, 128Mb memory, 5120Mb disk, вешается насмерть и соответственно ничего не работает. Количество процессов httpd растет и каждый сжирает память вызывая Unable to fork: Cannot allocate memory.

Выводы были самы разные от глюка на хостинге до заговоров конкурентов, нанявших кулхацкеров чтобы они заDDoS`или на наш безобидный сайтик.
Пообщавшись с тех поддержкой Хостер.кг, (кстати отличные ребята в тех поддержке, очень оперативно помогают решать проблемы) пришли к выводу что это вероятнее всего атака, заблочили самого активного гостя, IP оказался какой то африканский.
До логов добраться не было возможности, поэтому перешли на lighthttpd. При попытке включить Апач снова происходило тоже самое, т.е. Сервак в дауне.

Поэтому Всем рекомендую сделать следующее:
Если у вас перед Apache стоит nginx, то можно вообще ничего не делать, даже если файлы, для которых возможны описанные выше запросы, не раздаёт nginx, потому как по-умолчанию, по крайней мере на версии 1.1.0, nginx не передаёт заголовок Range на проксируемый сервер.

Проверить, уязвим ли ваш сервер к этой атаке легко:

curl -I -H «Range: bytes=0-1,0-2» -s www.example.com/robots.txt | grep Partial

Если на такие запросы отвечает Apache и вы видите 206 Partial Content, значит быть беде.

Запретить nginx проксировать опасный заголовок можно директивой:

proxy_set_header Range "";

Если нет nginx?

Если у вас во внешний мир Apache смотрит напрямую… Вы можете полностью заблокировать проблемный заголовок при помощи mod_headers:

# a2enmod headers
RequestHeader unset Range

Если же вам всё-таки нужен этот заголовок, существует решение на основе mod_rewrite и ещё несколько более сложных с mod_headers.

Можно принудительно очищать заголовок Range при помощи mod_header («RequestHeader unset Range») или блокировать длинные последовательности Range через mod_rewrite:

# Вариант 1:
RewriteEngine On
RewriteCond %{HTTP:Range} bytes=0-[0-9]+, [NC,OR]
RewriteCond %{HTTP:Range} bytes=([0-9-],){4,} [NC,OR]
RewriteCond %{HTTP:Range} bytes=[0-9,-]+,0-(,|$) [NC]
RewriteRule .? %{SERVER_NAME}/ [NS,L,F]

# Вариант 2:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^(HEAD|GET) [NC]
RewriteCond %{HTTP:Range} ([0-9]*-[0-9]*)(\s*,\s*[0-9]*-[0-9]*)+
RewriteRule .* — [F]

# Вариант 3:
RewriteEngine On
RewriteCond %{HTTP:Range} bytes=0-.* [NC]
RewriteRule .? %{SERVER_NAME}/ [R=302,L]

Для сайтов на движке Drupal поможет код в .htaccess
RewriteCond %{REQUEST_METHOD} ^(HEAD|GET) [NC]
RewriteCond %{HTTP:Range} ([0-9]*-[0-9]*)(\s*,\s*[0-9]*-[0-9]*)+
RewriteRule .* — [F]

Этот код для Wordpress.
RewriteCond %{HTTP:Range} bytes=0-.* [NC]
RewriteRule .? %{SERVER_NAME}/ [R=302,L]

Интересно, что о теоретической возможности совершения подобной атаки Михаил Залевски (Michal Zalewski), известный польский эксперт в области компьютерной безопасности в настоящее время работающий в Google, сообщал еще 4 года назад, но проблема по каким-то причинам не была воспринята всерьез и исправления не были внесены.

Материал подготовленн по материалам хабраюзера alexkbs

6 комментариев

avatar
Я попробовал по всякому, но сервер не выдаёт указанной ошибки и никаких проблем с апачем не происходит.
avatar
А версия Апача какая?
Возможно запрещен это заголовок (Range)
  • msb
  • 0
avatar
Apache/2.2.19
Никаких специальных настроек не сделано, кроме отключения кучи лишних модулей.
avatar
Стародревний Apache/2.2.8 тоже спокойно отреагировал на указанный заголовок:
HTTP/1.1 200 OK
avatar
Повезло, а нам нет :(
avatar
На одном из серверов была ошибка «HTTP/1.1 206 Partial Content». Правда неадекватного поведения апача я не заметил.
Включил модуль mod_headers и прописал директиву убивающую указанный заголовок. Сообщение об ошибке пропало.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.