23 000 $ за обход аутентификации, загрузку файлов и произвольную перезапись файлов
Сегодня я поделюсь недавней интересной уязвимостью. Однако я не могу раскрыть название программы и домен, так как не получил разрешения на их публикацию.
Предположим, что целью является test.com.
Начав тестирование программы, я нашел способ обхода пользовательского интерфейса административной панели. Цель использует JSON Web Token (JWT) в качестве механизма аутентификации. Я уделил немало времени, чтобы разобраться и выявить возможные уязвимости на объектах программы, использующих JWT.
При входе на основной сайт test.com, для обычного пользователя генерируется JWT.
После изучения работы цели я начал собирать данные:
- Читал JavaScript-файлы.
- Использовал Burp Suite для анализа запросов.
- Переходил по кнопкам на сайте.
- Использовал Wayback Machine для поиска всех возможных конечных точек.
- Проводил перечисление поддоменов.
В результате я обнаружил интересный поддомен admin.test.com.
На поддомене admin.test.com был открыт JavaScript-файл app.js. Прочитав его (200 000 строк кода), я выяснил, что он также использует JWT для аутентификации.
Кроме того, я нашел список realm. В нём нашлось кое-что интересное — test-dashboard.
Параметр "realm" в аутентификации используется для указания области защиты. Пространство защиты определяется каноническим корневым URI (схемой и компонентами authority запрашиваемого URI).
Подробнее можно прочитать в RFC 7235, раздел 2.2:
https://www.rfc-editor.org/rfc/rfc7235#section-2.2
Я использовал jwt.io для декодирования токена пользователя. В токене был указан realm=test-user.
Теперь я предположил, что если смогу изменить realm на test-dashboard, то смогу войти в административную панель.
test-dashboard — это имя веб-сайта, которое заменяет test, то есть оно выглядело как: target-dashboard.
- Перейдите на сайт: https://test.com/.
- Войдите в свою учётную запись. Измените параметр realm на test-dashboard в POST-запросе: https://test.com/api/v1/login
HTTP request
POST /api/v1/login HTTP/1.1 Host: accounts.test.com Connection: close Content-Length: 79 Accept: */* Content-Type: application/json {“email”:”[email protected]”,”password”:”<password>”,”realm”:”test-dashboard”}
Если декодировать JWT, можно увидеть, что параметр realm был изменён.
Теперь, используя модифицированный JWT-токен, я смог получить доступ к административной панели.
Я немедленно сообщил об этой уязвимости, но получил вполне ожидаемый ответ от программы Bug Bounty:
Мы обсудили это с разработчиками, и они заявили, что административная панель, к которой вы получили доступ, представляет собой всего лишь React-приложение, отрендеренное на стороне клиента (страницы, которые используют только публичную информацию для отображения), и ничего более. Фактический API является отдельным приложением с эндпоинтами, которые требуют действительного токена авторизации с определёнными правами доступа. Таким образом, если вы не сможете создать токен, который позволит вам взаимодействовать с API, уровень серьёзности данной проблемы будет низким.
Они изменили уровень серьёзности проблемы с Critical на Medium.
Я был готов сдаться, но решил продолжить копать глубже.
Согласившись с командой, я понял, что для того, чтобы считать уязвимость критической, мне нужно манипулировать параметром scope в токене JSON Web Token (JWT).
Однако это казалось невозможным, поскольку для этого потребовалась бы эксплуатация 0-day уязвимости в механизме JWT, что сделало бы уязвимыми любые сайты, использующие JSON Web Token (JWT).
Но я был достаточно настойчив, чтобы продолжить искать что-то подобное.
Так как я мог управлять параметром realm и генерировать валидные JWT-токены, я попробовал всевозможные полезные нагрузки для манипуляции scope, но ничего не работало, и мне не удалось добиться нужного результата.
Затем я начал поиск контента с использованием ffuf на поддомене admin.test.com, но, к сожалению, не нашел никаких валидных точек.
По умолчанию ffuf использует HTTP-метод GET, поэтому я решил попробовать метод POST. И я обнаружил https://admin.test.com/upload, который возвращал 403 Forbidden. Это показалось мне интересным, так как эта ссылка упоминался в файле app.js.
Тогда я подумал: "А что, если я смогу загрузить веб-оболочку?" Эта идея меня очень воодушевила.
После нескольких часов изучения JavaScript-файла мне удалось составить запрос для загрузки файла:
POST /upload HTTP/1.1 Host: admin.test.com Connection: close Content-Length: 300 Accept: application/json, text/plain, */* Content-Type: multipart/form-data; boundary=---- WebKitFormBoundarypxxxxxx Authorization: Bearer <JWT> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4629.0 Safari/537.36 ------WebKitFormBoundarypxxxxxx Content-Disposition: form-data; name="destination" gallery/ ------WebKitFormBoundarypxxxxxx Content-Disposition: form-data; name="file"; filename="poc.txt" Content-Type: Text/plain h4x0r-dz POC ------WebKitFormBoundarypxxxxxx--
Но я получил ошибку 401 HTTP :(, даже после того как я манипулировал realm в JWT.
Знаете ли вы, что такое Фаззинг?
Если ваш ответ — НЕТ, то вы упустили множество ошибок, которые могут быть найдены!
Фаззинг (или Fuzz-тестирование) — это метод “черного ящика” тестирования программного обеспечения, который, по сути, заключается в нахождении ошибок реализации с помощью автоматизированной инъекции.
Я начал фаззинг заголовка Authorization: Bearer <JWT>, и наконец я увидел ответ 200 :)
Проблема заключается в том, что если вы удалите слово Bearer из заголовка Authorization, вы сможете пройти аутентификацию на https://admin.test.com и получить права администратора.
Отправьте этот запрос для загрузки файла.
POST /upload HTTP/1.1 Host: admin.test.com Connection: closeContent-Length: 300 Accept: application/json, text/plain, */* Content-Type: multipart/form-data; boundary=---- WebKitFormBoundarypxxxxxx Authorization: <JWT> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4629.0 Safari/537.36 ------WebKitFormBoundarypxxxxxx Content-Disposition: form-data; name="destination" gallery/ ------WebKitFormBoundarypxxxxxxContent-Disposition: form-data; name="file"; filename="poc.txt" Content-Type: Text/plain h4x0r-dz POC ------WebKitFormBoundarypxxxxxx--
Я получил ответ 200 OK с сообщением uploaded.
Теперь возникает вопрос: где найти путь к моему файлу?
Сначала я подумал, что это конец, поскольку невозможно определить, куда был загружен мой файл.
Я попробовал контентную разведку с помощью ffuf для всех поддоменов, пытаясь найти что-то вроде admin.test.com/uploads/poc.txt, но ничего не обнаружил.
Затем я начал просматривать историю запросов в Burp Suite и анализировать ответы. В одном из них я обнаружил следующую строку:
href=https://xxxxxxxx.cloudfront.net/gallery/xxxxxxxxx
Интересно, что gallery совпадает со значением, которое я передавал в поле destination при загрузке файла.
Я перешел по адресу:
https://xxxxxxxxx.cloudfront.net/gallery/poc.txt
И обнаружил, что мой файл находится именно там.
Amazon CloudFront — это сеть доставки контента (CDN), предоставляемая Amazon Web Services. Сети доставки контента обеспечивают глобально распределенную сеть прокси-серверов, которые кэшируют контент, такой как веб-видео или другие ресурсоемкие данные, улучшая скорость доступа к скачиваемому контенту.
Таким образом, загрузить веб-оболочку не получится :(
Даже если я сейчас сообщу о данной уязвимости загрузки файла, её уровень серьезности будет крайне низким. Поэтому я продолжил копать глубже.
Перезапись произвольного файла
По умолчанию Amazon S3 уязвим к некорректной конфигурации, позволяющей осуществить перезапись произвольного файла. Например, если загрузить file.txt, это приведет к его перезаписи на Amazon S3.
Теперь у меня есть возможность перезаписи произвольного файла. Это открывает множество возможностей.
Я обнаружил, что xxxxxxxx.cloudfront.net используется на основном сайте для размещения JavaScript, HTML и других файлов.
Множество файлов размещается на xxxxxxxx.cloudfront.net, и как атакующий, я могу изменять содержимое этих файлов. Это позволило мне получить stored XSS и другие уязвимости безопасности на основном домене, потому что они использовали xxxxxxxx.cloudfront.net для размещения программного обеспечения Windows и PDF-файлов. Эти файлы являются частью основного сайта.
Таким образом, я могу изменять содержимое этих файлов и получить удалённое выполнение кода (RCE) на компьютерах пользователей, внедряя вредоносный код в существующие EXE или PDF файлы, CSS и т.д.
Мы не могли подтвердить уязвимость через браузер из-за кэширования, поэтому можно использовать CURL.
Теперь, я изменил содержимое файла poc.txt через этот запрос:
POST /upload HTTP/1.1 Host: admin.test.com Connection: closeContent-Length: 300 Accept: application/json, text/plain, */* Content-Type: multipart/form-data; boundary=---- WebKitFormBoundarypxxxxxx Authorization: <JWT> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4629.0 Safari/537.36 ------WebKitFormBoundarypxxxxxx Content-Disposition: form-data; name="destination" gallery/ ------WebKitFormBoundarypxxxxxx Content-Disposition: form-data; name="file"; filename="poc.txt" Content-Type: Text/plainArbitrary File Overwrite ------WebKitFormBoundarypxxxxxx--
Как видно из моего терминала, мне удалось перезаписать существующий файл.
Я получил 20 тысяч долларов за эту уязвимость.
Также я получил 3000 долларов за доступ к панели администратора, в сумме получилось 23 тысячи долларов.
Я надеюсь, что сегодня вы узнали что-то новое, и прошу прощения, если мой отчет оказался недостаточно ясным или если я слишком много говорил.