Техника HTTP Request Smuggling позволяет провести различные виды атак - отравление кеша, похищение сессии, межсайтовый скриптинг и что наиболее важно, предоставляет возможность обойти защиту файрвола.
Перевод: ApacheDev.ru
В предыдущей статье мы рассмотрели (и использовали) следующие ошибки при обработке HTTP запросов:
a. Два различных заголовка Content-Length (Пример №1, №4 и №5)
b. Запрос GET с заголовком Content-Length (Пример №3)
c. Ошибка с 48К в IIS/5.0 (Пример №2)
Существует еще несколько ошибок обработки, которые мы обнаружили. В большинстве случаев, пара прокси/кеш/файрвол сервер и веб-сайт может быть атакована, используя один или несколько методов HRS, но обычно для какой-то конкретной пары программ подходит только часть методов.
Ниже мы приведем список ошибок (и техник их использования), используемых для HRS, вместе с парами устройств, которые подвержены им. Заметьте, что это неполные результаты (т.к. для многих техник мы не протестировали все возможные пары программ). Это означает, что, вероятно, существует гораздо больше пар, уязвимых HRS, чем мы приведем ниже.
В данном случае ошибка очевидна - атакующий отсылает запрос с двумя заголовками Content-Length. Если кеш сервер и веб-сервер используют разные заголовки, тогда возможна HRS.
a. Кеш сервер использует последний заголовок Content-Length, а веб-сервер использует первый заголовок (примеры №1, №4 и №5). Следующие из исследованных кеш серверов используют последний заголовок Content-Length:
- Microsoft ISA/2000
- Sun Microsystems SunONE 3.6 SP4
Следующие из исследованных веб-серверов, используют первый заголовок Content-Length:
- Jakarta Tomcat 5.0.19 (Coyote/1.1)
- Tomcat 4.1.24 (Coyote/1.0)
- Sun Microsystems SunONE web server 6.1 SP1
Все 6 комбинаций кеш серверов и веб-серверов были протестированы, и все показали уязвимость к HRS. Особый интерес вызывает комбинация прокси сервера Sun Microsystems SunONE 3.6 SP4 и веб-сервера того же поставщика SunONE web server 6.1 SP1.
b. Как вариант 1а, когда в некоторых случаях атака forward smuggling не проходит, возможна атака backward smuggling. Она проходит в случае использования популярного коммерческого кеш приложения (чье название мы не будем раскрывать, а обозначим за PCCA) и Jakarta Tomcat 5.0.19 (Coyote/1.1). PCCA обрабатывает последний заголовок Content-Length и направляет запросы с телом веб-серверу, используя отдельное соединения, таким образом, делая атаку, описанную в 1а, бесполезной. Единственным путем обойти это препятствие это послать запрос без тела, то есть послать второй заголовок Content-Length со значением "0". Веб-сервер (который использует значение второго заголовка Content-Length) решит, что этот запрос не закончен, и мы получим блокировку. Тем не менее, некоторые серверы, а именно IIS/6.0 и Tomcat пошлют ответ при запросе статичного ресурса (например, /index.html) до того, как получат полностью тело запроса. Это можно использовать для проведения backward smuggling, что мы и собираемся продемонстрировать на примере PCCA и Tomcat. Атакующему необходимо отправить первый запрос (на произвольную статичную страницу) с двумя заголовками "Content-Length". Первый заголовок должен иметь значение длины второго запроса. Второй заголовок первого запроса должен иметь значение "0". Второй запрос это запрос, который определяет ресурс, чей URL будет использован для прикрытия. А третий запрос определяет ресурс, чей контент необходимо получить. В этом случае, контент ресурса, определенного в третьем запросе будет закеширован под URL ресурса, определенном втором запросе.
Вот пример атаки (используются PCCA и Tomcat):
1 GET http://SITE/static_foobar.html HTTP/1.0
2 Content-Length: 71
3 Content-Length: 0
4 Connection: Keep-Alive
5
6 GET http://SITE/page_to_poison.html HTTP/1.0
7 Host: SITE
8 Connection: Keep-Alive
9 GET /poison.html HTTP/1.0
10PCCA использует последний заголовок Content-Length (cтрока 3) и следовательно передает строки 1-5 серверу. Tomcat же обрабатывает запрос, используя первый заголовок Content-Length (строка 2), и поэтому будет ожидать еще 71 байт. Однако так как запрос отправлен на статичную страницу (/static_foobar.html), Tomcat также немедленно возвращает ее обратно PCCA. PCCA отправляет этот ответ атакующему и отсылает следующий запрос, полученный им из входящего потока - в данном случае, строки 6-10 (запрос на /page_to_poison.html). Получив его, Tomcat использует 71 байт (строки 6-8, но не забудьте, что PCCA отбросит http://SITE из 6 строки при отправлении запроса серверу) и поэтому Tomcat видит второй запрос, в виде строк 9-10. Следовательно, Tomcat отправит контент страницы /poison.html. PCCA это рассмотрит полученный ответ, как ответ на страницу page_to_poison.html. И в результате атака проведена успешно.
c. Кеш сервер использует первый заголовок Content-Length, в то время как веб-сервер использует второй заголовок Content-Length. Следующие кеш серверы, которые мы исследовали, используют первый заголовок Content-Length:
- Squid 2.5stable4 (Unix)
- Squid 2.5stable5(порт NT)
- Oracle WebCache 9.0.2
Следующие из исследованных нами веб-серверов, используют последний заголовок Content-Length:
- BEA Systems WebLogic 8.1 SP1
Все три комбинации были проверены и показали свою уязвимость атакам HRS.
Разбираясь с Apache 2.0.45, мы обнаружили, что он довольно интересно реагирует на такое сочетание заголовков. Запрос, полученный с обоими заголовками, принимается как обладающий кодированным по кускам телом. [HTTP/1.1 не позволяет запросу иметь одновременно два заголовка: "Content-Length" и "Content-Encoding:chunked"]. Apache полностью прочитывает тело такого запроса и переделывает его в нормальный (не кодированный по кускам) запрос. По непонятным причинам, Apache не добавляет собственный заголовок Content-Length и не удаляет существующий. В результате запрос направляется с первоначальным заголовком Content-Length, и без заголовка "Transfer-Encoded:chunked" и с телом, включающим все части первоначального запроса. Очевидно, что, используя этот феномен, можно скрыть запрос, отослав заголовок "Content-Length: 0" и закодированное по кускам тело со спрятанным запросом.
Данная атака проходит на Apache 2.0.45 и на следующих веб-серверах:
Необходимо отметить, что поведение Apache в этом случае очень странное, так как запросы, которые он создает по умолчанию, не имеют заголовка Content-Length, и поэтому они могут вызывать проблемы обработки на многих веб-серверах (которые в этом случае принимают значение Content-Length за 0). А когда нормальный запрос с заголовком Transfer-Encoded: chunked посылается через Apache, он доставляется веб-серверу как запрос с нормальным телом, но без Content-Length, что приводит к полному игнорированию тела запроса на большинстве веб-серверов.
[Прим: CR - возврат каретки. SP - пробел]
Эта техника использует ошибку в обработке HTTP запроса. Строка заголовка с единственным символом CR, идущая за последовательностью CRLF обрабатывается некоторыми программами как строка HTTP заголовка, а другими как маркер конца заголовков. [Прим: HTTP/1.1 не позволяет использовать такие строки в HTTP запросах.]
Для использования данной техники атаки, нам необходимо применить еще одну технику. Эта техника использует ошибку под названием "Заголовок SP". Техника "Заголовок SP" применяется, когда разные программы по-разному обрабатывают HTTP заголовки, имеющих пробелы между именем заголовков и символом ":".
Некоторые программы воспринимают "foo SP:" как заголовок с именем "foo", тогда как другие - как заголовок с именем "foo " ("foo" с добавленными пробелом). Атака, которую мы опишем ниже, использует обе эти техники.
Позвольте начать с техники использования запроса с двойным CR в строке заголовка:
PCCA рассматривает этот запрос, как имеющий заголовок "CR", и поэтому не заканчивает блок заголовков. IIS/5.0 же завершает блок заголовков, но при определенных условиях, которые мы обсудим позже.
Техника "Заголовок SP" нужна для того, чтобы обойти стремление PCCA отправлять запросы с телом через новое TCP соединение, а также, чтобы IIS не отклонял запросы с двумя заголовками Content-Length.
Основная идея атаки заключается в следующем:
1 GET http://SITE/foobar.html HTTP/1.0
2 Connection: keep-alive
3 [CR]
4 GET /poison.html?aaaa … aaa [2048 times] HTTP/1.0
5 Content-Length: N
6 Content-Length : 0
7
8 GET http://SITE/page_to_poison.html HTTP/1.0
9
Где N это длина запроса на URL "http://SITE/page_to_poison.html" (строки 8-9), отправленный PCCA как проверенный запрос веб-серверу.
PCCA воспринимают строки 1-7 как первый запрос. Это запрос GET с Content-Length равным 0 (запомните, что PCCA обрабатывает два заголовка Content-Length: один в строке 5, а второй в строке 6. Заголовок строки 6, который содержит технику "заголовок SP" нестандартный, потому что содержит дополнительный пробел после имени заголовка. Тем не менее, PCCA все также обрабатывает эту строку как заголовок Content-Length. Так как PCCA использует последнее значение заголовка, то в данном случае он использует значение 0). Следовательно, PCCA отправляет строки 1-7 веб-серверу как единый HTTP запрос.
Заметьте, что PCCA заменяет CR CRLF на CR CR CRLF. Но это не влияет на нашу атаку. IIS/5.0 обрабатывает полученное как первый запрос (строки 1-3), а остальное, как второй запрос, но уже незавершенный (строки 4-7). Первый запрос заканчивается CRLF CR CR CRLF. Интересное поведение IIS/5.0 заключается в том, что он следующие полученные данные TCP соединения пытается обработать как HTTP заголовок. Таким образом, если в следующих 2048 байтах он найдет символ ":", тогда последовательность CRLF CR CR CRLF не будет рассматриваться как маркер конца заголовков. Вот почему нам необходимо 2048 символов "a". А так как IIS не находит двоеточие, то обрабатывает строки 4-7 как новый HTTP запрос (и конечно отсылает обратно ответ на первый запрос). Строки 4-7 представляются как GET запрос с Content-Length N (строка 6 игнорируется, так как IIS не воспринимает Content-Length с пробелом). И теперь IIS ожидает, когда данный запрос будет завершен.
Возвращаемся к PCCA. Он получил ответ на первый GET запрос и поэтому PCCA направляет следующий запрос (на страницу page_to_poison.html), строки 8-9. Теперь IIS получает недостающие N символов тела второго запроса (на poison.html) и теперь запрос может быть полностью обработан. PCCA получает контент страницы /poison.html на запрос страницы /page_to_poison.html. Отравление кеша успешно завершено.
Эта техника была нами протестирована на PCCA и ISS/5.0, тем не менее, многое из рассказанного может быть применено в атаках на другие пары программ.
Есть еще одна ошибка, которая заключается в том, что некоторые HTTP сущности считают, что запрос GET никогда не имеет тела, даже если присутствует заголовок Content-Length. Кеш сервер в этом случае (DeleGate/8.9.2) считает, что GET запрос с заголовком Content-Length не имеет тела и поэтому он направляет данный запрос (но уже без тела) веб серверу. Некоторые веб сервера обрабатывают запросы на статичные страницы до того момента, как получат запрос полностью. В таких случаях, возможна атака отравления кеша (в случае, если веб сервер ожидает получение всего запроса полностью, то происходит блокировка).
Вот веб-серверы, чье поведение соответствует вышеописанному:
А вот и пример атаки:
1 GET http://SITE/static_foobar.html HTTP/1.1
2 Connection: Keep-Alive
3 Host: SITE
4 Content-Type: application/x-www-form-urlencoded
5 Content-Length: 40
6
7 GET http://SITE/page_to_poison.html HTTP/1.1
8 Foo: GET /poison.html HTTP/1.0
9
DeleGate решит, что данный GET запрос не имеет тела. Поэтому, он передаст строки 1-6 как полный запрос веб-серверу. Веб-сервер отправит обратно /static_footback.html, но будет продолжать ожидать окончания запроса (т.е. получения дополнительных 40 байт от DeleGate). Получив обратно ответ на первый запрос, DeleGate прочитает следующий запрос от клиента, находящихся в строках 7-9, и направит его веб-серверу.
Веб-сервер прочитает первые 40 байт запроса и просто проигнорирует их. Эти первые 40 байт как раз последовательность: "GET /page_to_poison.html HTTP/1.1 CRLF Foo:,", которую прокси отправит веб-серверу в начале второго запроса. Поэтому, веб-сервер проигнорирует их и прочитает второй запрос, как GET /poison.html. И эта страница будет возвращена DeleGate, следовательно DeleGate получит контент страницы /poison.html при запросе страницы /page_to_poison.html.
Данная атака может быть проведена с DeleGate/8.9.2 и всеми веб-серверами указанными выше (IIS/6.0, Tomcat и SunONE).
Эта техника работает с такой редко используемой особенностью HTTP, как "строка продолжения заголовка". В соответствие с HTTP стандартом, строка заголовка, начинающаяся с пробела, является продолжением предыдущей строки заголовка. Тем не менее, некоторые программы не совсем правильно реализуют это в своих обработчиках HTTP, что приводит к ошибкам.
Вот программы, которые рассматривают CRLF SP CRLF как продолжение предыдущего заголовка:
И веб-сервера, которые рассматривают CRLF SP CRLF как флаг конца заголовков:
a. Прямая атака
1 POST /dynamic_foobar.asp HTTP/1.0
2 Connection: Keep-Alive
3 Content-Type: application/x-www-form-urlencoded
4 [SP]
5 GET /malicious_url HTTP/1.0
6
FW-1 отправляет строки 1-6 веб-серверу (IIS/5.0). Файрвол рассмотрит строку 4 как продолжение строки 3, и обработает строку 5 как заголовок HTTP запроса. Так как FW-1 не проверяет HTTP заголовки, он пропустит такие строки, как "cmd.exe" (т.е. проверки на червей, XSS и SQL-инъекции не будут осуществляться).
IIS/5.0 обработает это как два запроса: строки 1-4 как первый запрос (POST запрос с нулевой длиной тела, оканчивающийся последовательностью CRLF SP CRLF) и строки 5-6 как второй запрос (GET запрос, со злонамеренным URL).
b. Вариант атаки, требующий дополнительной вставки:
Squid (мы проверили Squid 2.4stable7, 2.5stable4 и 2.5stable5 для NT) воспринимает CRLF SP CRLF как продолжение заголовка. Однако Squid также проверяет HTTP заголовок на наличие символа ":" (иначе, запрос полностью отвергается). Следовательно, строка, которая сразу же следует за CRLF SP CRLF должна содержать двоеточие. Однако IIS/5.0 проверяет наличие символа ":" после CRLF SP CRLF и если таковой находится, IIS/5.0 считает, что это строка является строкой заголовка (т.е. IIS/5.0 не заканчивает секцию заголовков). Поэтому этот метод добавляет в строку произвольные данные (6000 байт), следующие за символом двоеточие. Тем самым, заставляя Squid рассматривать ее как строку заголовка, а IIS/5.0 рассматривает ее как новый запрос.
Например:
1 GET http://SITE/static_foobar.asp HTTP/1.0
2 Foo: bar
3 [SP]
4 GET /poison.html?AAAAA … [6000 times]… AAAA: HTTP/1.0
5 Connection: Keep-Alive
6
7 GET http://SITE/page_to_poison.html HTTP/1.0
8
Squid отправляет строки 1-6 как простой запрос к веб-серверу (IIS). Запомните, что строка 4 является правильным заголовком для Squid, так как она содержит символ двоеточие. IIS рассматривает данные как два запроса. Строки 1-3 как первый запрос, заканчивающийся последовательностью CRLF SP CRLF (в интерпретации ISS, несколько тысяч байт справа от CRLF SP CRLF не содержат символ двоеточие). Этот запрос обрабатывается IIS и ответ возвращается Squid. Затем, Squid отправляет строки 7-8, но IIS отсылает обратно ответ на строки 4-6. Следовательно, Squid получает контент /poison.html на запрос с URL /page_to_poison.html.
Но есть несколько тонких моментов:
Как ясно из заголовка, IIS/5.0 в некоторых случаях ведет себя очень интересно. Так, запрос с большим (>48KB) телом (например, POST с корректным телом и заголовком Content-Length, показывающим длину этого тела), но без заголовка Content-Type будет воспринят как запрос, чей размер тела равен 48 KB (49152). После 49152 байт тела, IIS/5.0 прервет запрос и начнет обрабатывать новый запрос. Это позволяет очень легко провести атаку, т.к. такое поведение нестандартно и не соответствует RFC.
Мы проведем атаку отравления кеша Squid (2.5stable5 для NT), Apache 2.0.45 и ISA/2000. Мы также обойдем защиту FW-1.
Пример атаки (отравление кеша):
1 POST http://SITE/dynamic_foobar.asp HTTP/1.1
2 Host: SITE
3 Connection: keep-alive
4 Content-Length: 49181
5
6 AAAAA … AAAA[49150 times]
7 GET /poison.html HTTP/1.0
8
9 GET http://SITE/page_to_poison.html HTTP/1.1
10 Host: SITE
11
Кеш сервер отправит строки 1-8 веб-серверу. Заметьте, что Content-Length (строка 4) включает как раз необходимую подстановку в 6 строке (49150 байт) плюс CRLF в конце 6 строки, плюс 7 и 8 строки (каждая с CRLF). IIS/5.0 прочитает строки 1-6 как первый запрос (тело завершится после 49152 байта, и как раз попадет на начало 7 строки), затем перейдет ко второму запросу (строки 7-8). Затем он отправит ответ на первый запрос кеш серверу. После этого кеш сервер отправит второй запрос (/page_to_poison.html), строки 9-11. IIS/5.0 обработает второй запрос (строки 7-8) и отправит обратно контент /poison.html. Кеш сервер получит контент /poison.html на запрос на /page_to_poison.html.
Пример (обход FW-1):
1 POST /dynamic_page1.asp HTTP/1.1
2 Host: SITE
3 Connection: keep-alive
4 Content-Length: 49230
5
6 AAAAA … AAAA[49150 times]
7 POST /dynamic_page2.asp HTTP/1.0
8 Connection: Keep-Alive
9 Content-Length: 35
10
11 POST /dynamic_page3 HTTP/1.0
12 Bla: GET /malicious_url HTTP/1.0
13
14 GET /some_page HTTP/1.0
15
FW-1 отправит первый запрос (строки 1-10) IIS/5.0. IIS/5.0 прочитает строки 1-6 как первый полный запрос. Затем он отправит ответ обратно FW-1. Далее он прочитает строки 7-10 как второй неполный запрос (тело еще не передано).
FW-1 вернет ответ от IIS обратно атакующему и отправит строки 11-13 в качестве второго запроса. Заметьте, что злонамеренные данные в 12 строке являются частью HTTP заголовка (так их воспримет FW-1), и поэтому они не приведут к срабатыванию механизмов обнаружения/защиты у FW-1.
IIS/5.0 получит строку 11 и первые 5 байт 12 строки как тело второго запроса и ответит на второй запрос. FW-1 получит этот ответ и отошлет третий запрос IIS/5.0, строки 11-15. IIS/5.0 отправит ответ на третий запрос (строки 12-13), который является результатом запроса на /malicious_url.
Заключение: Мы увидели, что многие пары программ (прокси/файрвол сервера и веб-сервера) являются уязвимыми. Уязвимыми являются следующие пары:
Это частичный список - многие пары мы не протестировали, а также существуют множество других веб-серверов и кеш серверов, которые мы не изучили. Также очень вероятно, что существуют еще множество подобных техник.
Как сайт может защитить себя от HRS? Установка программного файрвола может помочь, но как мы показали на примере FW-1, защиты некоторых файрволов могут быть обойдены (узнайте у поставщика вашего файрвола, защищают ли его продукты от подобных атак).
Другое решение это использовать те веб-сервера, которые используют очень точные парсеры HTTP, как, например Apache (он подвержен лишь некоторым видам HRS только тогда, когда используется и как веб-сервер, и как кеш сервер).
Другие не очень практичные методы - это использовать SSL соединения (https вместо http), завершать клиентскую сессию после каждого запроса или сделать все страницы некешируемыми (для защиты от отравления кеша).
Так для некоторых продуктов упомянутых выше, мы можем дать специальные патчи и конфигурационную информацию:
Для устранения описанных выше уязвимостей в Squid выпущены патчи, которые распространяются среди поставщиков продуктов Squid. Также смотрите документ "SQUID-2005:4" (http://www.squid-cache.org/Advisories/SQUID-2005_4.txt)
http://www.squid-cache.org/Versions/v2/2.5/bugs/#squid-2.5.STABLE7-header_parsing
http://www.squid-cache.org/Versions/v2/2.5/bugs/#squid-2.5.STABLE7-response_splitting
http://www.squid-cache.org/Versions/v2/2.5/bugs/#squid-2.5.STABLE8-relaxed_header_parser
Рекомендованная версия: Squid-2.5.STABLE9.
Также советуем отключить для Squid устойчивые соединения:
client_persistent_connection off
server_persistent_connection off
Мы считаем, что все изложенное выше будет исправлено либо в R55W либо в новом патче.
Единственным способом полностью обезопаситься от HRS это сделать так, чтобы все HTTP устройства точно придерживались процесса обработки HTTP. К сожалению, это вряд ли произойдет в ближайшем будущем.
[1] A. Klein, "Divide and Conquer - HTTP Response Splitting, Web Cache Poisoning Attacks, and Related Topics." Sanctum White Paper, March 2004. http://www.packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf
[2] 3APA3A, "Bypassing Content Filtering Whitepaper," February 2002 (original paper date. The paper was last revised August 2004). http://www.security.nnov.ru/advisories/content.asp
[3] Rain Forest Puppy, "A look at whisker's anti-IDS tactics," December 1999. http://www.ussrback.com/docs/papers/IDS/whiskerids.html
[4] J. Gettys, R. Fielding, J. Mogul, H. Frystyk, L. Masinter, P. Leach, and T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1." RFC 2616, June 1999. http://www.w3.org/Protocols/rfc2616/rfc2616
[5] J. Grossman, "Cross Site Tracing (XST)." WhiteHat Security White Paper, January 2003. http://www.cgisecurity.net/whitehat-mirror/WH-WhitePaper_XST_ebook.pdf
[6] P. Watkins, "Cross Site Request Forgeries (CSRF)," BugTraq posting, June 2001. http://www.securityfocus.com/archive/1/191390
[7] "CERT Advisory CA-2000-02 Malicious HTML Tags Embedded in Client Web Requests." February 2000. http://www.cert.org/advisories/CA-2000-02.html
[8] A. Klein, "Cross Site Scripting Explained." Sanctum White Paper, May 2002. http://crypto.stanford.edu/cs155/CSS.pdf
5778 К? Пф! У нас градус знаний зашкаливает!