HTTP/1.1 Must Die — итоги исследования

Я исследую веб‑безопасность и делюсь практическими разбориками уязвимостей: от CSRF и XXE до Server‑Side XSS, race conditions и обходов CSP. Пишу чек‑листы атак и защиты, редкие кейсы из практики и мысли о мышлении пентестера. Только прикладной AppSec, понятные примеры и то, что реально встречается в проде. Учимся охотиться, пока на нас не охотятся.

http/1.1request smuggling0.cl

На днях вышло исследование от команды Portswigger с кричащим названием "HTTP/1.1 Must Die".

Ресерч посвящен объяснению ключевых проблем версии протокола 1.1 и новой технике HTTP Request Smuggling - 0.CL. Как в целом и все прочие их исследования, статья очень хорошая и я бы рекомендовал прочитать ее полностью. Но если вы не хотите тратить время на ее прочтение (справедливости ради, она достаточно длинная), приведу основные поинты выделенные в статье:

  • ⏩Протокол HTTP/1.1 имеет в себе проблему на уровне дизайна. Так как протокол тектовый (а не бинарный, как, например, HTTP/2) в нем отсутствуют нормальные принципы разраничения одного запроса от другого (если точнее, то их существуют несколько и все они достаточно абстрактны)
  • ⏩Различные имплементации HTTP/1.1 только усугубляют ситуацию
  • ⏩С момента обнаружения эксплуатации этой проблемы (HTTP Request Smuggling) в 2004 году мы постоянно пытаемся запатчить или скрыть какие-то проблемы, но это порождает лишь новые и новые уязвимости и их количество никогда не закончится
  • ⏩Показана новая теника эксплуатации 0.CL. Смысл заключается в том, что мы скрываем заголовок "Content-Length" (или причастные) от Frontend-сервера, но делаем его заметным для Backend-сервера. При этом важно найти гаджет, который позваоляет получить ответ от приложения на неполный запрос (чтобы обойти таймаут соединения). Эксплуатация происходит в три этапа: 1. Очередь запросов отравляется 0.CL; 2. Очередь отравляется заново на этот раз известными техниками CL.0; 3. Происходит похек пользователей
  • ⏩Также показаны способы эксплуатации 0.CL при помощи древнего заголовка Expect - многие парсеры просто не умеют нормально работать с его функциональностью (он предполагает, что приложение будет возвращать несколько блоков заголовков)
  • ⏩Единственный нормальный способ полностью зафикситься - переходить на более новые версии HTTP на upstream-прокси и внутренней инфраструктуре. При этом указано, что в целом ок если HTTP/1.1 используется только на уровне взаимодействия пользователя с прокси (критичность таких атак сильно меньше).

Для того, чтобы потыкать новую технику руками сдалали новую лабу. В целом, лаба мне понравилась, но пришлось несколько раз перечитать статью, чтобы ее решить. Официальный райтап будет показан в прямом эфире 15 августа. Но я могу поделиться своим решением уже сейчас. Помещу его в комментариях. Так что, если вы планировали изучить ее самостоятельно, пока туда не заглядывайте.

#Research #Smuggling

☠️ Hunt Or Be Hunted

Скриншот заголовка исследования Portswigger «HTTP/1.1 must die»: шапка статьи с автором, датой и темой request smuggling.
Заголовок исследования Portswigger «HTTP/1.1 must die» — иллюстрация исходной статьи.

Дискуссия

Критика чистого разума (Hunt Or Be Hunted Chat)
Лабораторная просит нас вызвать alert() в браузере пользователя, который периодически посещает ресурс. 1. Так как Request Smuggling - лишь способ доставки полезной нагрузки в данном случае, нам нужно для начала найти гаджет, через который мы сможем вызвать alert. Поизучав небольшой функционал приложения (или запустив сканер) мы быстро можем обнаружить, что при посещении страниц с постами мы можем поместить XSS-нагрузку в поле User-Agent (предлагаю это сделать самостоятельно или смотрите в финальную нагрузку) 2. Далее, нам нужно найти early-response гаджет, который будет отдавать ответ даже при неполном запросе. Подойдет любой статический файл. 2.5. Начинаем конструировать нагрузку. Тут есть два пути: мы либо пользуемся новым скриптом в Turbo-Inruder, куда вставляем все нужные запросы и просто смотрим как лаба решается сама, либо делаем все руками. Я попробовал оба подхода, тут расскажу про второй, чтобы было понятнее, что происходит внутри. 3. Производим атаку 0.CL подобным запросом (Конструируем сами или просим помощи у расширения HTTP Request Smuggler, который научился такое обнаруживать):
POST /resources/images/blog.svg HTTP/1.1
Host: <lab_host>
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length  : 41


Тут обращаем внимание на пробелы у заголовка Content-Length. Мы хотим сделать его невидимым для Frontend-Сервера, но заметным для Backend. Очень важна цифра, но об этом чуть далее 4. Конструируем второй запрос с нагрузкой, объединяем его в группу с первым и отправляем друг за другом в separate connections:
GET / HTTP/1.1
Content-Length: 260
X: YGET /404 HTTP/1.1
Host: <lab_host>
User-Agent: foo
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive

GET /post?postId=3 HTTP/1.1
User-Agent: <sample_UA>"><script>alert(1)</script>
Content-Type: application/x-www-form-urlencoded
Content-Length: 5

x=y
Теперь пояснение. CL=41 в первом запросе должен покрывать длину запроса до X: Y во втором запросе. Эта часть второго запроса будет "съедаться" предыдущим запросом и будет проходить то, что идет дальше (`GET /404`). Значение CL=260 во втором запросе должно покрывать всю длину последнего запроса (`GET /post?postId=3`), включая CL=5. Если ваши запросы сложатся удачно, следующий запрос пользователя вернет ему ответ с XSS-нагрузкой вместо ожидаемой страницы, какую бы он ни открыл. Можно добавить в группу любой легальный запрос и наблюдать. Важно: при эксплуатации возникает Race Condition внутри upstream proxy, поэтому эту всю конструкцию, что я прописал выше нужно отправлять несколько раз, пока оно все не соберется как надо и не пройдет эксплуатация. Также важный теоретический момент - при нормальной эксплуатации нам нужно понимать полную длину для второго запроса, включая заголовки, которые выставляет Frontend-Сервер (а он их выставляет всегда). В данном случае я накостылил и специально ограничил последний запрос (часть запроса улетает куда-то дальше в очередь и немного ломает все вокруг. Для решения это не сильно имеет значение, но в реальных случаях так делать нельзя, иначе будет хаос в запросах и ответах пока очередь не почистится). В том же приложении есть гаджет, через который вы можете достать все заголовки Frontend-сервера: Когда оставляете комментарий, ставьте значение email в конец запроса так, чтобы Smuggled-нагрузка добавлялась в него. Если все сделть правильно вы получите рефлект всех заголовков Frontend-сервера в ответе на этот запрос и сможете посчитать необходимую длину запроса. Hunt Or Be Hunted
Daniil Filippov
Леша Must Win 😤😤😤 Ни Шагу назад ! Леша вперед !
Присоединиться к обсуждению →

Читайте так же