build manifest, которая неожиданно помогает сузить поиск скрытых routes. Доклад Анатолия Катюшина
Цветёт, но дурно пахнет: странный кейс с Bloom filter в Next.js
это не история про «магическую уязвимость в Next.js», а про то, как AppSec-исследователь работает с тем, что доступно снаружи. Если нельзя бесконечно брутфорсить
API, приходится смотреть на то, что приложение само отдаёт клиенту.Инсайты из доклада:
⚡️ В build manifest Next.js живёт Bloom filter маршрутов. По пути
/_next/static/{buildId}/_buildManifest.js можно найти объект routerFilterStatic. На первый взгляд — непонятная закодированная строка. На второй — вероятностная структура данных, которая участвует в маршрутизации приложения.⚡️ Bloom filter — это локальный оракул для атакующего. Фильтр отвечает на один вопрос: «Существует ли этот путь?» с двумя возможными ответами: «Точно нет» или «Возможно да». False negative невозможны, если путь был добавлен при сборке, фильтр его узнает. Это значит, что вместо миллионов запросов к серверу можно прогнать миллионы кандидатов локально и отправить наружу только выживших.
⚡️ Цифры из реального кейса. ~2,4 миллиона кандидатов → после локальной проверки осталось 642 записи. Большинство — false positive, но это уже совсем другой масштаб для ручного разбора при жёстком rate limit. В демо: ~100 тысяч вариантов → 8 кандидатов. Нужный id оказался среди них.
⚡️ API-routes тоже попадают в фильтр. По логике Bloom filter должен относиться к клиентским страницам. Но в Webpack-сборке Next.js API-маршруты тоже оказываются внутри фильтра, особенно если папка
api лежит внутри app. Именно это превращает оптимизацию фреймворка в разведывательный инструмент.⚡️ Сам фильтр не уязвимость — уязвимость нашлась за ним. Найденный скрытый API-путь оказался плохо закрыт: запрос вернул список пользователей и данные привилегированного аккаунта. Классическая BOLA. Без ошибки авторизации находка была бы нерепортабельной утечкой структуры приложения.
⚡️ Выводы для исследователя. Нельзя полагаться на «скрытость» API-endpoints. Если доступ к
/api/users/{id} ограничен только тем, что путь трудно угадать — рано или поздно его найдут: через Burp, чанки, sitemap, логику клиента или вот через Bloom filter. Каждая ручка должна проходить нормальную проверку авторизации на уровне объекта.🎧Смотреть, читать конспект, листать презентацию тут
