LIMB
Вход Регистрация

тема

Bug Bounty: ищим новые фичи в JS файлах

nipaah · · 0 ответов · 320 просмотров

Автоматизация мониторинга JavaScript-файлов для Bug Bounty​

Представим, что у нас есть перспективный таргет на баг баунти, по которому мы уже получили не одну выплату. Мы изучили его работу вдоль и поперёк: каждый endpoint, весь клиентский код, и сдали все найденные баги. Что же делать дальше?

Конечно, ждать добавления новых фич разработчиками, чтобы приступить к немедленному тестированию. Но заходить раз в день и нажимать кнопочки — не наш метод. Мы хотим быть самыми первыми, кто узнает о новых фичах. Мы хотим просто запустить команду в терминале и увидеть, что нового появилось за наше отсутствие.

В этой статье я расскажу о методах и техниках построения собственного баг-баунти тулкита для сбора информации из JS-файлов и мониторинга их обновлений. Подчеркну, что статья рассчитана не на новичков, а на тех, кто знаком как минимум с языком JavaScript и автоматизацией. Кода здесь не будет — только общее описание используемого мной подхода. Код с разбором опубликую в следующих частях, если эта заинтересует достаточно людей.

Проблематика​

Казалось бы, задача просто решается с помощью всем известных open-source утилит: через hakrawler запускаем краулер, собираем ссылки на JS-файлы какого-нибудь GoLinkFinder, сохраняем их, с помощью jsluice извлекаем URLs и пути для поиска API-вызовов и прочих интересностей. Складываем в папку и запускаем команду diff, верно?

Нет. Это лишь общий принцип, которому мы будем следовать на практике. На деле же в современных SPA-приложениях мы столкнёмся с множеством трудностей:

1. WAF вроде Cloudflare — не позволит просто запустить краулер и получить JS-файлы
2. Сборщики вроде webpack — упаковывают код так, что он подгружается только при необходимости (так называемые chunks)
3. Изменчивые имена файлов — от сборки к сборке имена чанков могут различаться, появляются новые файлы
4. Минификация кода — если повезёт, будут включены source maps (разработчики часто забывают их отключить на Next.js проектах — хантерам на заметку), а если нет — придётся иметь дело с малопонятным минифицированным кодом
5. Автоматизация — с помощью каких инструментов выстраивать пайплайн, организовать хранение файлов и создание удобочитаемого diff между запусками

Теперь разберём общую архитектуру нашего тулкита, используемые технологии и то, как решаем вышеизложенные проблемы.

1. Crawler​

Идея проста: нам необходимо посетить сервис так, как будто мы делаем это с помощью обычного браузера. Здесь нам на помощь придут Playwright и Camoufox.

Camoufox (https://camoufox.com/) — это антидетект-браузер, который позволяет с лёгкостью обходить капчу Cloudflare (вернее, просто не триггерить её). Он легко интегрируется в Playwright.

Playwright — это фреймворк для E2E-тестирования. С его помощью мы закодим основные сценарии:
- Прокликивание ссылок на сайте без логина
- Логин и выполнение критического флоу (например, процесс покупки)
- Под учётными данными высокопривилегированного пользователя — прокликивание всех основных страниц внутри админки

Каждый из сценариев пытается максимально покрыть функционал сайта, чтобы захватить как можно большее количество JS-файлов. Весь этот трафик сохраняется в виде HAR-файлов (это JSON, куда записывается трафик в формате запрос/ответ).

​

Дополнительные возможности​

На самом деле здесь есть некоторые тонкости. Например, manifest-файлы webpack, где описаны пути всех JS-модулей. Мы можем извлечь их и вытащить ещё больше JS-файлов, которые не были покрыты с помощью краулинга. Так мне удалось найти файлы, отвечающие за реализацию внутренней админки. Но это лучше всего показывать на примере с кодом, поэтому пока пропустим.

​

2. HAR Processor​

Здесь всё просто: проходим по массиву запросов и ответов, фильтруем по домену и путям (только .js и, например, только домен cdn.ourtarget.com), сохраняем содержимое файлов на диск, а также составляем списки из URL'ов всех сохранённых JS-файлов — они нам ещё пригодятся.

Извлечение source maps​

Самое ценное, что мы можем получить из JS-файлов — это неминифицированный исходный код. Лучшая утилита из всех, что я перепробовал — это https://github.com/unblu/js-source-extractor. Она умеет:
- Проверять ссылки по пути .map и скачивать source maps, восстанавливая исходную структуру проекта
- Искать source maps внутри уже скачанных JS-файлов, если разработчиками был выбран режим inline source maps

Также не забудем прогнать все загруженные файлы через https://github.com/beautifier/js-beautify для форматирования.

​

3. JS Extractors​


Извлекаем полезные данные из кода. Приведу самое популярное:
- Домены
- Ссылки
- Пути и части путей для поиска вызовов API
- GraphQL queries/mutations
- JS-зависимости для проверки на уязвимости dependency confusion
- Составление wordlist для последующего фаззинга
- Поиск забытых секретов

Большую часть проще написать самому на регулярках — под каждый таргет почти всегда специфичные требования. Но есть пара хороших open-source утилит, которые помогут всегда:
- https://github.com/BishopFox/jsluice — извлекает пути и ссылки
- https://github.com/trufflesecurity/trufflehog — комбайн по поиску секретов

4.. Сравнение результатов​

Итак, теперь у нас есть JS-файлы и извлечённая полезная информация. Как же нам лучше всего сравнить результаты между текущей версией и предыдущей? Через Git!
Поэтому смело добавляем наши извлечённые source maps (только не забудьте выкинуть `node_modules`) и результаты работы JS-экстракторов в репозиторий и делаем коммит.

​

Работа с минифицированными файлами​

А вот теперь самое сложное и интересное — минифицированные JS-файлы. Рассмотрим самый простой случай, когда имена загруженных JS-файлов отличаются только частью имени, например dashboard.abcd12.js и dashboard.feac21.js, и нам не нужно гадать, какой файл из предыдущей версии с каким соотносить из новой версии для запуска diff. Если же все файлы имеют в названии только хэш, то мы можем найти похожие файлы с помощью утилиты https://ssdeep-project.github.io/ssdeep/index.html но это выходит за рамки данной статьи.

Если мы просто запустим diff между двумя минифицированными файлами, которые отличаются только одной новой функцией (допустим, разработчик изменил запрос к API-эндпоинту из-за изменения интерфейса), мы всё равно получим огромную портянку текста, в которой мало что можно понять.

Причина: каждый билд через webpack минифицирует код, меняя названия всех переменных и функций на случайные максимально короткие варианты вроде a, b, c, aa, bc, ba и т.д.

Решение: нормализация идентификаторов​

Казалось бы, ничего с этим нельзя сделать. Но решение гениально простое! Нам всего лишь нужно взять и переименовать все изменённые имена идентификаторов обратно. Но так как исходного названия переменной мы не знаем (да оно нам и не нужно), нам нужно только различие между предыдущей и новой версией кода.

Поэтому мы выбираем какое-нибудь нейтральное новое имя, например __VAR__ обходим весь JS-файл и переименовываем все короткие идентификаторы. На выходе diff теперь короткий и ясный, отображающий только реальные изменения. Так же вместо обычного diff есть более красивый и удобный для чтения difft: 
sudo apt install difft

Технические детали​

Для переименования переменных вариант с регулярками, конечно же, не подойдёт. Поэтому мы воспользуемся обходом AST (Abstract Syntax Tree), а именно библиотекой @babel/traverse под nodejs.

Однако при работе с большими JS-файлами и их большим количеством она показала неэффективность. Поэтому я переписал решение на Rust с помощью swc_core — просто несоизмеримо быстрее, меньше потребляет памяти, и, как оказалось, работает гораздо точнее.

Заключение​

Мы рассмотрели основные подходы и инструменты для построения своего мини-тулкита для анализа JS-файлов баг-баунти таргета, что называется «под ключ». К сожалению, каждый таргет требует уникального подхода, и разработать один универсальный не представляется возможным.

Дело осталось за малым — взять вышеописанные модули и склеить их в один пайплайн, например, с помощью команды make или just. Без рассмотрения исходников я вижу в этом мало смысла, поэтому оставим это на потом.

Если вы хотите увидеть продолжение — исходный код каждого модуля с разбором, а также финальный проект — шлите письма, ставьте лайки. Я продолжу писать статьи.

P.S. Для форматирования конечного текста и правки ошибок я использовал Claude, все остальное на 100% авторское
👍14 😁3 😮3

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.