Содержание статьи
Pentest Award
В августе 2023 года прошла церемония награждения Pentest Award — премии для специалистов по тестированию на проникновение, которую учредила компания Awillix. Мы публикуем лучшие работы из каждой номинации. Эта статья — победитель в номинации «Пробив». Читай также райтапы, занявшие второе, третье и четвертое места.
Не буду скрывать, я — фанат программы баг‑баунти VK на HackerOne. Иногда холдинг приобретает новые компании, и программа расширяется, что дает баг‑хантерам неплохой шанс собрать «низко висящие фрукты» — уязвимости, которые могут быть найдены без существенных затрат времени и усилий.
По своему опыту могу сказать, что получить доступ к чему‑то, что до тебя никто не пытался взломать, очень выгодно. На площадке HackerOne есть возможность подписаться на интересующую тебя программу и получать обновления о любых изменениях в правилах. Этим я и воспользовался, чтобы быть одним из первых, кто начнет тестировать недавно добавленный сервис.
В течение 2021 года я не очень активно хантил и не следил за обновлениями в избранной программе. Именно по этой причине я пропустил уведомление о том, что платформа Seedr, которая помогает быстро распространять видео в интернете (сейчас уже не действующая), была добавлена в скоуп.
Моя первая встреча с Seedr состоялась в октябре 2021 года. Я начал тестировать и сразу же обнаружил несколько банальных XSS-уязвимостей, но решил не сообщать о них, так как шанс получить дубликат был слишком высок.
В настоящее время ты можешь просмотреть раскрытые отчеты других баг‑хантеров и заметить, насколько нетипичные для современных приложений уязвимости они нашли в Seedr:
- RCE в .api/nr/report//download;
- SSRF + RCE через fastCGI в POST /api/nr/video;
- XSS Stored on https://seedr.ru;
- OS command injection on seedr.ru.
Подумав, что мой поезд ушел, я решил не тратить силы на этот проект и продолжил прокрастинировать.
Находка, которая привлекла мое внимание
Я вернулся к тестированию Seedr во время декабрьского отпуска в другой стране, где с собой у меня был лишь рюкзак и ноутбук. После некоторого времени пребывания в таких условиях у меня просыпается «баг‑баунти‑голод» и появляется желание найти что‑нибудь интересное. Для разогрева я обычно возвращаюсь к уже знакомым сервисам и стараюсь посмотреть на них свежим взглядом.
На этот раз я уделил больше внимания разведке Seedr, а именно поиску и перечислению поддоменов, сканированию портов, перебору веб‑директорий и так далее. К счастью, я нашел более заманчивые вещи: GitLab, Grafana, несколько хостов API, cron-файлы в веб‑директории, трассировки стека и многое другое. Чем больше точек входа находишь, тем выше шанс отыскать что‑то интересное. Хотя ни одна из находок не оказалась стоящей того, чтобы о ней сообщить, кое‑что все же привлекло мое внимание.
В исходном HTML-коде страницы https://
я заметил следующий комментарий:
https://player.seedr.ru/video?vid=cpapXGq50UY&post_id=57b6ceef64225d5b0f8b456c&config=https%3A%2F%2Fseedr.com%2Fconfig%2F57975d1b64225d607e8b456e.json&hosting=youtube
Готов поспорить, что более опытный читатель уже захотел изменить GET-параметр config
на свой хост для получения входящего HTTP-соединения, что я и сделал. Но после нескольких попыток не получил ни одного отстука и продолжил экспериментировать с другими параметрами.
Я открыл в браузере вот такую ссылку:
https://player.seedr.ru/video?vid=cpapXGq50UY&post_id= 57b6ceef64225d5b0f8b456c&config=https%3A%2F%2Fseedr.com%2Fconfig%2F57975d1b64225d607e8b456e.json&hosting=youtube
Заглянув в код, я заметил, что метатеги заполнены по разметке Open Graph и содержат информацию о видео: название, описание, превью и так далее.
После нескольких тестовых запросов я понял, что GET-параметры post_id
и config
не оказывают существенного влияния на ответ, поэтому упростил URL до https://
.
Предположив, что плеер, скорее всего, поддерживает не только YouTube, я изменил GET-параметр hosting
на coub
и vimeo
.
Итак, похоже, в зависимости от значения GET-параметра hosting
сервер с помощью PHP-функции file_get_contents(
выполняет HTTP-запрос к YouTube, Vimeo или Coub API, загружает метаданные о видео (GET-параметр vid
), обрабатывает их и возвращает HTML-страницу плеера с видео и заполненными по разметке Open Graph метатегами.
GET-параметр vid
является точкой инъекции, так как он позволяет контролировать последнюю часть пути в функции file_get_contents(
с помощью символов обхода пути (/..
) и других полезных символов (?, #, @ и так далее).
Что еще интересно, в случае с Vimeo сервер делает запрос к http://
. И оказывается, что при использовании расширения .
в пути Vimeo возвращает не JSON, а сериализованные данные!
Я предположил, что после функции file_get_contents(
сервер десериализует ответ от Vimeo с помощью функции unserialize(
.
Ого, неужели у нас здесь небезопасная десериализация? Безопасная, пока ответ контролирует Vimeo.
Возможные сценарии
В тот момент у себя в голове я уже видел три возможных сценария атаки:
- Фаззинг функции
file_get_contents(
с целью добиться слепой SSRF, то есть выполнить HTTP-запрос на подконтрольный мне ресурс и в теории добиться небезопасной десериализации.) - Найти контролируемый ответ на
vimeo.
и добиться небезопасной десериализации.com - Найти открытый редирект на
vimeo.
→ SSRF → небезопасная десериализация.com
После нескольких часов различных модификаций GET-параметра vid
и локального фаззинга функции file_get_contents(
я не нашел ничего полезного и параллельно решил поделиться всей имеющейся информацией об этой находке с несколькими надежными товарищами.
Итак, первый сценарий не сработал, поэтому я перешел к следующему — контролируемому ответу на vimeo.
.
Эндпоинт с контролируемым ответом должен отвечать следующим требованиям:
- код ответа HTTP — 200 OK;
- доступен для неавторизованного пользователя;
- контролируемая строка должна находиться в начале тела ответа (PHP успешно десериализует {VALID_SER_STRING}TRASH);
- контролируемая строка должна поддерживать символы { }, "", необходимые для хранения сериализованных объектов.
Ниже представлены некоторые из моих попыток найти требуемое поведение на vimeo.
.
Первая: injection is not a valid method.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {
, ""
.
Вторая: injection is not a valid format.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {
, ""
.
Третья: JavaScript callback.
Недостатки: /
в начале строки, не поддерживаются символы {
, ""
.
Четвертая: экспорт чата прямой трансляции.
Недостатки: дата и имя в начале строки, требуется аутентификация.
К сожалению, второй сценарий также не сработал, поэтому моей последней надеждой оставалось найти открытый редирект на vimeo.
. Ранее я уже встречал опубликованный отчет на HackerOne от 2015 года с открытым редиректом на vimeo.
, поэтому предположил, что есть небольшой шанс найти еще один. На самом деле я одновременно искал открытый редирект еще во время проверки второго сценария, но снова ничего не нашел.
Открытый редирект
Все это время, пока я раскручивал уязвимость, я думал о статье Harsh Jaiswal Vimeo SSRF with code execution potential. Я отчетливо помнил, что для успешной эксплуатации использовалось несколько открытых редиректов на vimeo.
. Уязвимость была найдена еще в 2019 году, поэтому я ожидал, что описываемые в статье открытые редиректы уже исправлены. Но поскольку, вероятно, это был мой единственный шанс, я начал копать в этом направлении.
Из‑за того, что информация на скриншотах была недостаточно скрыта, удалось предположить уязвимый эндпоинт по используемым GET-параметрам. Учитывая это, я немного погуглил и почитал документацию Vimeo API и смог определить, какой именно эндпоинт использовал Harsh в своей цепочке. В любом случае оставалось неясным, какие значения GET-параметров я должен передать.
Я редко прошу кого‑то о помощи, не считая нескольких друзей, но, поскольку я был в тупике, Harsh был моей последней надеждой.
После того как я написал ему и предоставил всю имеющуюся информацию, он поделился со мной рабочей ссылкой с открытым редиректом, которая оказалась такой же, как я и подозревал, но с верными значениями GET-параметров. По этой ссылке я понял, что это не баг на vimeo.
, а фича (действительно, это не шутка).
Итак, теперь у меня есть работающий открытый редирект на vimeo.
, осталось только его применить.
Отлично, я наконец‑то словил HTTP-запрос на свой хост. Прежде чем перейти к десериализации, я решил немного поиграть с SSRF. Результаты смотри на скриншотах.
Из‑за того, что возвращаемое значение из функции file_get_contents(
передается сразу в функцию unserialize(
, у меня не получилась полная SSRF, чтобы читать успешные ответы от внутренних сервисов. Но по крайней мере у меня уже была полуслепая SSRF с возможностью выполнять сканирование портов.
Как только я понял, что использовал почти весь потенциал этой SSRF, я переключился на эксплуатацию функции unserialize(
.
Небезопасная десериализация
Вкратце объясню, что необходимо для успешной эксплуатации небезопасной десериализации в PHP:
- контролируемые входные данные;
- класс с магическим методом (
__wakeup(
,) __destroy(
,) __toString(
и так далее);) - в магическом методе определена полезная функциональность, которой можно злоупотребить (например, манипуляция с файловой системой или выполнение запросов к базе данных);
- класс загружен.
Как видишь, на тот момент выполнялось только одно требование из четырех. О серверном коде на хосте я знал слишком мало, поэтому единственный способ эксплуатации — это вслепую попробовать все известные цепочки гаджетов. Для этого я использовал инструмент PHPGGC, который по сути является набором полезных нагрузок для эксплуатации функции unserialize(
вместе с инструментом для их генерации. В то время он содержал почти 90 доступных нагрузок. Большая часть из них предназначена для различных CMS и фреймворков, таких как WordPress, ThinkPHP, TYPO3, Magento, Laravel, которые в моем случае были совершенно бесполезны. Поэтому я сделал ставку на такие широко используемые библиотеки, как Doctrine, Guzzle, Monolog и Swift Mailer.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»