Пентестер (тестировщик на проникновение) рассказывает, как ему удалось найти баг в загрузке файлов и проникнуть на сервер платежной системы PayPal.
Привет всем!
Надеюсь, у вас все хорошо. Уверен, что заголовок вас немало удивил, и вы ринулись сюда чтобы глянуть, реально ли я cмог получить удаленный доступ не куда-нибудь, а на сервер PayPal.
На самом деле это был довольно простенький взлом, направленный на проверку уязвимости (так называемый POC). Единственный момент, с которым мне повезло — поиск и успешное определение уязвимого домена.
Начнем с небольшой предыстории. Как вы могли ранее видеть в моем предыдущем посте, я недавно сдал экзамен на сертификацию OSCP. Вот на это у меня действительно ушло немало дней и ночей упорной работы — почти 4 месяца сосредоточенных занятий и никакого поиска багов, то есть, по сути, никакого зарабатывания карманных денег.
Нормальные люди: выходные полные выпивки, тусовок, веселья, похмелья и прочего. Вопросы вроде: «Ты смотрел нового «Человека-паука»? Игру престолов?»
Люди вроде меня:
Итак, в один из выходных, просматривая кое-какие блоги и ролики на YouTube, я наткнулся на материал по PayPal, подумал зайти на их страницу программы поощрения поиска багов с помощью Burp и обнаружил следующее:
На скриншоте показан обычный ответ от http://paypal.com/bugbounty/. Присмотревшись, можно увидеть любопытный список доменов PayPal, в заголовке ответа Content Security Policy. Меня заинтересовал https://*.paypalcorp.com. Этот типовой подход, который я использую в отлавливании багов. Я ищу как можно больше рабочих поддоменов конечной цели, поскольку именно их, как правило, оставляют без должного внимания, и, такие как я, в итоге, что-нибудь там находят.
Вы можете воспользоваться Subbrute, Knockpy, enumall или другими похожими утилитами. Обычно я так и делаю, но в этот выходной лень взяла верх и я просканил их в VirusTotal. Итоговый список можно посмотреть здесь.
Я скопировал список поддоменов на локальную машину, запустил dig -f paypal +noall +answer чтобы получить удобную картину того, куда на самом деле указывают все субдомены.
Один из них, brandpermission.paypalcorp.com, указывал на https://www.paypal-brandcentral.com/ — сайт для регистрации запросов в службу онлайн-поддержки вендоров, поставщиков и партнеров PayPal, посредством которого они запрашивают разрешение на использование бренда. В системе доступна функция загрузки черновых вариантов логотипов и графики во время оформления запроса.
Реакция любого охотника за багами, увидевшего форму загрузки файлов:
Пахнет уловом
Поэтому для начала я создал «тикет», загрузив простое изображение с названием finished.jpg. На сервер оно попало под именем finished_thumb.jpg по пути /content/helpdesk/368/867/. Я быстро проверил, загрузился ли файл, который мы отправляли через форму с именем finished.jpg и да, к счастью (этот факт еще сыграет свою роль позже) он там присутствовал.
Далее я немного изучил схему работы приложения: как оно загружает файлы, куда они попадают, какие шаблоны имен для файлов и папок в нем приняты. Стало ясно, что каталог 368 был назван так по номеру созданного мной тикета, а 867 — идентификатор папки, в которой хранятся все связанные с ним файлы, то есть различные графические материалы, прилагаемые к тикету.
Я быстро повторил те же действия, создал еще одно обращение, из которого стало видно, что идентификаторы тикета и папки генерируются серийно. Создал еще один тикет, но в этот раз загрузил файл с расширением .php, содержащий простой однострочный скрипт для командной строки:
Скрипт вернул 302 код (то есть, по сути, 200 ОК). Это означало, что приложение не делало никаких проверок типов файлов, контента и прочего. Есть! Мое лицо в этот момент:
Да ладно...
Увидев 302 код, я ринулся открывать новый тикет чтобы скопировать ссылку как это было в случае загрузки файла изображения. Но при отправке .php увидеть путь загружаемого файла было нельзя. Единственное что можно было узнать — номер тикета. Что делать дальше?
Ну а теперь...
Получив номер тикета (366 в данном случае), мы узнаем имя директории, куда были загружены наши «графические примеры». Не хватает только идентификатора папки, куда попадает файл (поскольку мы не можем видеть php-файл при просмотре исходного кода страницы как это возможно с изображениями).
Но мы знаем имя файла — success.php (поскольку проверили до этого, что example.jpg оказался в той же папке, что и example_thumb.jpg).
Итак, несмотря на то, что при загрузке и обработке success.php в success_thumb.php код первого был «съеден», success.php, тем не менее, по-прежнему существует на сервере и к нему можно обратиться. Мы также знаем идентификатор папки (867), полученный при загрузке ранее простого изображения. Номер тикета для загрузки проверочного php — 366.
Почему бы просто не забрутфорсить идентификаторы папок, где хранится загруженный файл?
Нельзя просто взять и забрутфорсить
Поэтому я по-быстрому запустил в Intruder’е следующий перебор с идентификаторами 500–1000:
https://www.paypal-brandcentral.com/content/_helpdesk/366/$(bruteforced 500-1000)$/success.php. В итоге 200 код был получен на идентификаторе 865.
Моя реакция:
Круто! Теперь, когда мы узнали «айдишник» каталога с файлом давайте попробуем выполнить код: https://www.paypal-brandcentral.com/content/_helpdesk/366/865/success.php?cmd=uname-a;whoami
Немного магии cat /etc/passwd чтобы самому убедиться, что возможность удаленного исполнения кода действительно есть:
На сервере также была страница авторизации для сотрудников PayPal.
Надеюсь, вам понравился этот материал! Буду очень признателен обратной связи в комментах ;)
Хронология обработки обращения:
~ 8 июля, 2017 18:03 — Сообщил о баге в PayPal
~ 11 июля, 2017 18:03 — Баг исправлен
~ 25 июля, 2017 3:30 — Получил награду
Но подождите-ка! На этом история не заканчивается. Вот еще немного материала для затравки. О нем я поведаю в своем блоге на следующих выходных:
Я сообщил о той же самой уязвимости в разных точках приложения. Одно из сообщений было довольно очевидным, поэтому их объединили в одно обращение, а второе пометили, как дублирующее.
От переводчика:
На момент публикации перевода, сайт www.paypal-brandcentral.com стал недоступен, отдавая ошибку HTTP 500. Вероятно публикация оригинальной статьи послужила источником обнаружению более серъезных уязвимостей другими исследователями.
Привет всем!
Надеюсь, у вас все хорошо. Уверен, что заголовок вас немало удивил, и вы ринулись сюда чтобы глянуть, реально ли я cмог получить удаленный доступ не куда-нибудь, а на сервер PayPal.
На самом деле это был довольно простенький взлом, направленный на проверку уязвимости (так называемый POC). Единственный момент, с которым мне повезло — поиск и успешное определение уязвимого домена.
Начнем с небольшой предыстории. Как вы могли ранее видеть в моем предыдущем посте, я недавно сдал экзамен на сертификацию OSCP. Вот на это у меня действительно ушло немало дней и ночей упорной работы — почти 4 месяца сосредоточенных занятий и никакого поиска багов, то есть, по сути, никакого зарабатывания карманных денег.
Нормальные люди: выходные полные выпивки, тусовок, веселья, похмелья и прочего. Вопросы вроде: «Ты смотрел нового «Человека-паука»? Игру престолов?»
Люди вроде меня:
Итак, в один из выходных, просматривая кое-какие блоги и ролики на YouTube, я наткнулся на материал по PayPal, подумал зайти на их страницу программы поощрения поиска багов с помощью Burp и обнаружил следующее:
На скриншоте показан обычный ответ от http://paypal.com/bugbounty/. Присмотревшись, можно увидеть любопытный список доменов PayPal, в заголовке ответа Content Security Policy. Меня заинтересовал https://*.paypalcorp.com. Этот типовой подход, который я использую в отлавливании багов. Я ищу как можно больше рабочих поддоменов конечной цели, поскольку именно их, как правило, оставляют без должного внимания, и, такие как я, в итоге, что-нибудь там находят.
Вы можете воспользоваться Subbrute, Knockpy, enumall или другими похожими утилитами. Обычно я так и делаю, но в этот выходной лень взяла верх и я просканил их в VirusTotal. Итоговый список можно посмотреть здесь.
Я скопировал список поддоменов на локальную машину, запустил dig -f paypal +noall +answer чтобы получить удобную картину того, куда на самом деле указывают все субдомены.
Один из них, brandpermission.paypalcorp.com, указывал на https://www.paypal-brandcentral.com/ — сайт для регистрации запросов в службу онлайн-поддержки вендоров, поставщиков и партнеров PayPal, посредством которого они запрашивают разрешение на использование бренда. В системе доступна функция загрузки черновых вариантов логотипов и графики во время оформления запроса.
Реакция любого охотника за багами, увидевшего форму загрузки файлов:
Пахнет уловом
Поэтому для начала я создал «тикет», загрузив простое изображение с названием finished.jpg. На сервер оно попало под именем finished_thumb.jpg по пути /content/helpdesk/368/867/. Я быстро проверил, загрузился ли файл, который мы отправляли через форму с именем finished.jpg и да, к счастью (этот факт еще сыграет свою роль позже) он там присутствовал.
Далее я немного изучил схему работы приложения: как оно загружает файлы, куда они попадают, какие шаблоны имен для файлов и папок в нем приняты. Стало ясно, что каталог 368 был назван так по номеру созданного мной тикета, а 867 — идентификатор папки, в которой хранятся все связанные с ним файлы, то есть различные графические материалы, прилагаемые к тикету.
Я быстро повторил те же действия, создал еще одно обращение, из которого стало видно, что идентификаторы тикета и папки генерируются серийно. Создал еще один тикет, но в этот раз загрузил файл с расширением .php, содержащий простой однострочный скрипт для командной строки:
Скрипт вернул 302 код (то есть, по сути, 200 ОК). Это означало, что приложение не делало никаких проверок типов файлов, контента и прочего. Есть! Мое лицо в этот момент:
Да ладно...
Увидев 302 код, я ринулся открывать новый тикет чтобы скопировать ссылку как это было в случае загрузки файла изображения. Но при отправке .php увидеть путь загружаемого файла было нельзя. Единственное что можно было узнать — номер тикета. Что делать дальше?
Ну а теперь...
Получив номер тикета (366 в данном случае), мы узнаем имя директории, куда были загружены наши «графические примеры». Не хватает только идентификатора папки, куда попадает файл (поскольку мы не можем видеть php-файл при просмотре исходного кода страницы как это возможно с изображениями).
Но мы знаем имя файла — success.php (поскольку проверили до этого, что example.jpg оказался в той же папке, что и example_thumb.jpg).
Итак, несмотря на то, что при загрузке и обработке success.php в success_thumb.php код первого был «съеден», success.php, тем не менее, по-прежнему существует на сервере и к нему можно обратиться. Мы также знаем идентификатор папки (867), полученный при загрузке ранее простого изображения. Номер тикета для загрузки проверочного php — 366.
Почему бы просто не забрутфорсить идентификаторы папок, где хранится загруженный файл?
Нельзя просто взять и забрутфорсить
Поэтому я по-быстрому запустил в Intruder’е следующий перебор с идентификаторами 500–1000:
https://www.paypal-brandcentral.com/content/_helpdesk/366/$(bruteforced 500-1000)$/success.php. В итоге 200 код был получен на идентификаторе 865.
Моя реакция:
Круто! Теперь, когда мы узнали «айдишник» каталога с файлом давайте попробуем выполнить код: https://www.paypal-brandcentral.com/content/_helpdesk/366/865/success.php?cmd=uname-a;whoami
Немного магии cat /etc/passwd чтобы самому убедиться, что возможность удаленного исполнения кода действительно есть:
На сервере также была страница авторизации для сотрудников PayPal.
Надеюсь, вам понравился этот материал! Буду очень признателен обратной связи в комментах ;)
Хронология обработки обращения:
~ 8 июля, 2017 18:03 — Сообщил о баге в PayPal
~ 11 июля, 2017 18:03 — Баг исправлен
~ 25 июля, 2017 3:30 — Получил награду
Но подождите-ка! На этом история не заканчивается. Вот еще немного материала для затравки. О нем я поведаю в своем блоге на следующих выходных:
Я сообщил о той же самой уязвимости в разных точках приложения. Одно из сообщений было довольно очевидным, поэтому их объединили в одно обращение, а второе пометили, как дублирующее.
От переводчика:
На момент публикации перевода, сайт www.paypal-brandcentral.com стал недоступен, отдавая ошибку HTTP 500. Вероятно публикация оригинальной статьи послужила источником обнаружению более серъезных уязвимостей другими исследователями.