В этой статье я расскажу о том, как я связал в цепочку несколько проблем безопасности с целью удаленного выполнения кода (RCE) на серверах компании VK. Я описал свои шаги в подробностях, чтобы показать, как исследователь мыслит во время обнаружения необычных уязвимостей.
В августе 2023 года прошла церемония награждения Pentest Award — премии для специалистов по тестированию на проникновение, которую учредила компания Awillix. Мы публикуем лучшие работы из каждой номинации. Эта статья — победитель в номинации «Пробив». Читай также райтапы, занявшие второе, третье и четвертое места.
Не буду скрывать, я — фанат программы баг‑баунти VK на HackerOne. Иногда холдинг приобретает новые компании, и программа расширяется, что дает баг‑хантерам неплохой шанс собрать «низко висящие фрукты» — уязвимости, которые могут быть найдены без существенных затрат времени и усилий.
По своему опыту могу сказать, что получить доступ к чему‑то, что до тебя никто не пытался взломать, очень выгодно. На площадке HackerOne есть возможность подписаться на интересующую тебя программу и получать обновления о любых изменениях в правилах. Этим я и воспользовался, чтобы быть одним из первых, кто начнет тестировать недавно добавленный сервис.
В течение 2021 года я не очень активно хантил и не следил за обновлениями в избранной программе. Именно по этой причине я пропустил уведомление о том, что платформа Seedr, которая помогает быстро распространять видео в интернете (сейчас уже не действующая), была добавлена в скоуп.
Моя первая встреча с Seedr состоялась в октябре 2021 года. Я начал тестировать и сразу же обнаружил несколько банальных XSS-уязвимостей, но решил не сообщать о них, так как шанс получить дубликат был слишком высок.
В настоящее время ты можешь просмотреть раскрытые отчеты других баг‑хантеров и заметить, насколько нетипичные для современных приложений уязвимости они нашли в Seedr:
Подумав, что мой поезд ушел, я решил не тратить силы на этот проект и продолжил прокрастинировать.
Я вернулся к тестированию Seedr во время декабрьского отпуска в другой стране, где с собой у меня был лишь рюкзак и ноутбук. После некоторого времени пребывания в таких условиях у меня просыпается «баг‑баунти‑голод» и появляется желание найти что‑нибудь интересное. Для разогрева я обычно возвращаюсь к уже знакомым сервисам и стараюсь взглянуть на них свежим взглядом.
На этот раз я уделил больше внимания разведке Seedr, а именно поиску и перечислению поддоменов, сканированию портов, перебору веб‑директорий и так далее. К счастью, я нашел более заманчивые вещи: GitLab, Grafana, несколько хостов API, cron-файлы в веб‑директории, трассировки стека и многое другое. Чем больше точек входа находишь, тем выше шанс отыскать что‑то интересное. Хотя ни одна из находок не оказалась стоящей того, чтобы о ней сообщить, кое‑что все же привлекло мое внимание.
В исходном HTML-коде страницы
Готов поспорить, что более опытный читатель уже захотел изменить GET-параметр config на свой хост для получения входящего HTTP-соединения, что я и сделал. Но после нескольких попыток не получил ни одного отстука и продолжил экспериментировать с другими параметрами.
Когда я открыл в браузере ссылку
После нескольких тестовых запросов я понял, что GET-параметры post_id и config не оказывают существенного влияния на ответ, поэтому упростил URL до
Предположив, что плеер, скорее всего, поддерживает не только 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 сервер делает запрос к
Я предположил, что после функции file_get_contents() сервер десериализует ответ от Vimeo с помощью функции unserialize().
Ого, неужели у нас здесь небезопасная десериализация? Безопасная, пока ответ контролирует Vimeo.
В тот момент у себя в голове я уже видел три возможных сценария атаки:
После нескольких часов различных модификаций GET-параметра vid и локального фаззинга функции file_get_contents() я не нашел ничего полезного и параллельно решил поделиться всей имеющейся информацией об этой находке с несколькими надежными товарищами.
Итак, первый сценарий не сработал, поэтому я перешел к следующему — контролируемому ответу на vimeo.com.
Эндпоинт с контролируемым ответом должен отвечать следующим требованиям:
Ниже представлены некоторые из моих попыток найти требуемое поведение на vimeo.com.
Первая: injection is not a valid method.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {}, «».
Вторая: injection is not a valid format.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {}, «».
Третья: JavaScript callback.
Недостатки: /**/ в начале строки, не поддерживаются символы {}, «».
Четвертая: экспорт чата прямой трансляции.
Недостатки: дата и имя в начале строки, требуется аутентификация.
К сожалению, второй сценарий также не сработал, поэтому моей последней надеждой оставалось найти открытый редирект на vimeo.com. Ранее я уже встречал опубликованный отчет на HackerOne от 2015 года с открытым редиректом на vimeo.com, поэтому предположил, что есть небольшой шанс найти еще один. На самом деле я одновременно искал открытый редирект еще во время проверки второго сценария, но снова ничего не нашел.
Все это время, пока я раскручивал уязвимость, я думал о статье Harsh Jaiswal Vimeo SSRF with code execution potential. Я отчетливо помнил, что для успешной эксплуатации использовалось несколько открытых редиректов на vimeo.com. Уязвимость была найдена еще в 2019 году, поэтому я ожидал, что описываемые в статье открытые редиректы уже исправлены. Но поскольку, вероятно, это был мой единственный шанс, я начал копать в этом направлении.
Из‑за того, что информация на скриншотах была недостаточно скрыта, удалось предположить уязвимый эндпоинт по используемым GET-параметрам. Учитывая это, я немного погуглил и почитал документацию Vimeo API и смог определить, какой именно эндпоинт использовал Harsh в своей цепочке. В любом случае оставалось неясным, какие значения GET-параметров я должен передать.
Я редко прошу кого‑то о помощи, не считая нескольких друзей, но, поскольку я был в тупике, Harsh был моей последней надеждой.
После того как я написал ему и предоставил всю имеющуюся информацию, он поделился со мной рабочей ссылкой с открытым редиректом, которая оказалась такой же, как я и подозревал, но с верными значениями GET-параметров. По этой ссылке я понял, что это не баг на vimeo.com, а фича (действительно, это не шутка).
Итак, теперь у меня есть работающий открытый редирект на vimeo.com, осталось только его применить.
Отлично, я наконец‑то словил HTTP-запрос на свой хост. Прежде чем перейти к десериализации, я решил немного поиграть с SSRF. Результаты смотри на скриншотах.
https://127.0.0.1https://127.0.0.1:22http://127.0.0.1:25
Из‑за того что возвращаемое значение из функции file_get_contents() передается сразу в функцию unserialize(), у меня не получилась полная SSRF, чтобы читать успешные ответы от внутренних сервисов. Но по крайней мере, у меня уже была полуслепая SSRF с возможностью выполнять сканирование портов.
Как только я понял, что использовал почти весь потенциал этой SSRF, я переключился на эксплуатацию функции unserialize().
Вкратце объясню, что необходимо для успешной эксплуатации небезопасной десериализации в PHP:
Как видишь, на тот момент выполнялось только одно требование из четырех. О серверном коде на хосте я знал слишком мало, поэтому единственный способ эксплуатации — это вслепую попробовать все известные цепочки гаджетов. Для этого я использовал инструмент PHPGGC, который по сути является набором полезных нагрузок для эксплуатации функции unserialize() вместе с инструментом для их генерации. В то время он содержал почти 90 доступных нагрузок. Большая часть из них предназначена для различных CMS и фреймворков, таких как WordPress, ThinkPHP, TYPO3, Magento, Laravel, которые в моем случае были совершенно бесполезны. Поэтому я сделал ставку на такие широко используемые библиотеки, как Doctrine, Guzzle, Monolog и Swift Mailer.
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
9990 рублей 4000 р.
[TD]
920 р.
[/TD]
Я уже участник «Xakep.ru»
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-коде страницы
You do not have permission to view link please Вход or Регистрация
я заметил следующий комментарий:
You do not have permission to view link please Вход or Регистрация
Готов поспорить, что более опытный читатель уже захотел изменить GET-параметр config на свой хост для получения входящего HTTP-соединения, что я и сделал. Но после нескольких попыток не получил ни одного отстука и продолжил экспериментировать с другими параметрами.
Когда я открыл в браузере ссылку
You do not have permission to view link please Вход or Регистрация
57b6ceef64225d5b0f8b456c&config=https%3A%2F%2Fseedr.com%2Fconfig%2F57975d1b64225d607e8b456e.json&hosting=youtube, я заметил, что метатеги заполнены по разметке Open Graph и содержат информацию о видео: название, описание, превью и так далее.После нескольких тестовых запросов я понял, что GET-параметры post_id и config не оказывают существенного влияния на ответ, поэтому упростил URL до
You do not have permission to view link please Вход or Регистрация
.Предположив, что плеер, скорее всего, поддерживает не только 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 сервер делает запрос к
You do not have permission to view link please Вход or Регистрация
. И оказывается, что при использовании расширения .php в пути Vimeo возвращает не JSON, а сериализованные данные!Я предположил, что после функции file_get_contents() сервер десериализует ответ от Vimeo с помощью функции unserialize().
Ого, неужели у нас здесь небезопасная десериализация? Безопасная, пока ответ контролирует Vimeo.
Возможные сценарии
В тот момент у себя в голове я уже видел три возможных сценария атаки:
- Фаззинг функции file_get_contents() с целью добиться слепой SSRF, то есть выполнить HTTP-запрос на подконтрольный мне ресурс и в теории добиться небезопасной десериализации.
- Найти контролируемый ответ на vimeo.com и добиться небезопасной десериализации.
- Найти открытый редирект на vimeo.com → SSRF → небезопасная десериализация.
После нескольких часов различных модификаций GET-параметра vid и локального фаззинга функции file_get_contents() я не нашел ничего полезного и параллельно решил поделиться всей имеющейся информацией об этой находке с несколькими надежными товарищами.
Итак, первый сценарий не сработал, поэтому я перешел к следующему — контролируемому ответу на vimeo.com.
Эндпоинт с контролируемым ответом должен отвечать следующим требованиям:
- код ответа HTTP — 200 OK;
- доступен для неавторизованного пользователя;
- контролируемая строка должна находиться в начале тела ответа (PHP успешно десериализует {VALID_SER_STRING}TRASH);
- контролируемая строка должна поддерживать символы { }, «», необходимые для хранения сериализованных объектов.
Ниже представлены некоторые из моих попыток найти требуемое поведение на vimeo.com.
Первая: injection is not a valid method.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {}, «».
Вторая: injection is not a valid format.
Недостатки: код ответа HTTP 404 Not Found, не поддерживаются символы {}, «».
Третья: JavaScript callback.
Недостатки: /**/ в начале строки, не поддерживаются символы {}, «».
Четвертая: экспорт чата прямой трансляции.
Недостатки: дата и имя в начале строки, требуется аутентификация.
К сожалению, второй сценарий также не сработал, поэтому моей последней надеждой оставалось найти открытый редирект на vimeo.com. Ранее я уже встречал опубликованный отчет на HackerOne от 2015 года с открытым редиректом на vimeo.com, поэтому предположил, что есть небольшой шанс найти еще один. На самом деле я одновременно искал открытый редирект еще во время проверки второго сценария, но снова ничего не нашел.
Открытый редирект
Все это время, пока я раскручивал уязвимость, я думал о статье Harsh Jaiswal Vimeo SSRF with code execution potential. Я отчетливо помнил, что для успешной эксплуатации использовалось несколько открытых редиректов на vimeo.com. Уязвимость была найдена еще в 2019 году, поэтому я ожидал, что описываемые в статье открытые редиректы уже исправлены. Но поскольку, вероятно, это был мой единственный шанс, я начал копать в этом направлении.
Из‑за того, что информация на скриншотах была недостаточно скрыта, удалось предположить уязвимый эндпоинт по используемым GET-параметрам. Учитывая это, я немного погуглил и почитал документацию Vimeo API и смог определить, какой именно эндпоинт использовал Harsh в своей цепочке. В любом случае оставалось неясным, какие значения GET-параметров я должен передать.
Я редко прошу кого‑то о помощи, не считая нескольких друзей, но, поскольку я был в тупике, Harsh был моей последней надеждой.
После того как я написал ему и предоставил всю имеющуюся информацию, он поделился со мной рабочей ссылкой с открытым редиректом, которая оказалась такой же, как я и подозревал, но с верными значениями GET-параметров. По этой ссылке я понял, что это не баг на vimeo.com, а фича (действительно, это не шутка).
Итак, теперь у меня есть работающий открытый редирект на vimeo.com, осталось только его применить.
Отлично, я наконец‑то словил HTTP-запрос на свой хост. Прежде чем перейти к десериализации, я решил немного поиграть с SSRF. Результаты смотри на скриншотах.
https://127.0.0.1https://127.0.0.1:22http://127.0.0.1:25
Из‑за того что возвращаемое значение из функции 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.
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
-60% |
1 year
9990 рублей 4000 р.
[TD]
1 month_r
920 р.
[/TD]
Я уже участник «Xakep.ru»