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

Optionsbleed далеко не так опасен, как Heartbleed, поскольку работает лишь на серверах с определенными настройками, да и объем утекающих данных невелик. Но этот баг — из тех, что интересны сами по себе: изучив его, ты сможешь находить подобные или избегать их, если ты разработчик.

 

Стенд

Для успешных испытаний нам нужен веб-сервер Apache версии 2.2.34 и ниже или 2.4.27 и ниже, если речь идет о ветке приложения 2.4.х. Чтобы сократить время установки и настройки сервера, я рекомендую взять готовый докер-контейнер, в котором уже воссозданы все условия, необходимые для эксплуатации. Готовый файл, как водится, можешь найти у меня в репозитории.

docker run -p80:80 --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined httpd:2.4.25

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

pkill httpd
gdb httpd
run -X -d /usr/local/apache2/
Подготовка к отладке веб-сервера
Подготовка к отладке веб-сервера

INFO

Optionsbleed назвали по аналогии с Heartbleed, где тоже утекала память процесса и атакующий мог ее читать. Options — от метода, которым производится атака.

 

Детали

Уязвимость существует, когда в директиве Limit используется несуществующий или отключенный метод HTTP. Например, разработчик решил запретить использовать PATCH, однако ошибся и создал такой .htaccess-файл:

<Limit PATC>
</Limit>

В тестовом контейнере этот файл уже создан и находится в папке test веб-рута. Теперь посмотрим, что происходит, когда мы посылаем запрос OPTIONS.

OPTIONS /test/ HTTP/1.1
Host: optionsbleed.visualhack

Каждый раз, когда выполняется обработка входящего запроса, веб-сервер создает для него request_rec. Это сложная структура, которая содержит информацию о нескольких соединениях. Сейчас нас интересует только apr_pool_t — пул памяти запроса.

/include/httpd.h
787: /**
788:  * @brief A structure that represents the current request
789:  */
790: struct request_rec {
791:     /** The pool associated with the request */
792:     apr_pool_t *pool;

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

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

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

При выполнении приведенного выше запроса OPTIONS на самом деле выполняется несколько соединений с сервером. Когда обрабатывается первый коннект, выделяется память для хранения запроса.

/modules/http/http_core.c
135: static int ap_process_http_async_connection(conn_rec *c)
136: {
137:     request_rec *r;
...
146:         if ((r = ap_read_request(c))) {
147:
148:             c->keepalive = AP_CONN_UNKNOWN;
Указатель на память, выделенную для структуры request_rec
Указатель на память, выделенную для структуры request_rec

Сервер просматривает директорию, которая указана в запросе, и, если там есть файл .htaccess, он обрабатывается. За это отвечает функция ap_parse_htaccess.

/server/config.c
2149: AP_CORE_DECLARE(int) ap_parse_htaccess(ap_conf_vector_t **result,
2150:                                        request_rec *r, int override,
2151:                                        int override_opts, apr_table_t *override_list,
2152:                                        const char *d, const char *access_names)
2153: {
...
2223:     /* cache it */
2224:     new = apr_palloc(r->pool, sizeof(struct htaccess_result));

При парсинге директивы Limit сервер размещает ее данные в адресном пространстве запроса. Часть памяти выделяется для хранения метода HTTP, который указан в директиве. В нашем случае это строка PATC.

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

Cтатьи из последних выпусков журнала можно покупать отдельно только через два месяца после публикации. Чтобы читать эту статью, необходимо купить подписку.

Подпишись на журнал «Хакер» по выгодной цене!

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

Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

Tips’n’Tricks из арсенала андроидовода. Самые интересные, полезные и нестандартные трюки с Android

Многие годы мы рассказывали про самые разные способы оптимизировать, модифицировать и твик…