
Представьте: вы открываете письмо, кликаете по безобидной ссылке, и ваш корпоративный аккаунт теперь принадлежит кому-то другому. И это лишь один из четырех критических багов в коробочном SSO-решении, которые мы обнаружили во время рутинного пентеста.
Мы покажем, как одна логическая ошибка в продукте может привести к полному захвату аккаунтов, и объясним, как действовать, когда находишь подобный «подарок» в своей инфраструктуре.
Нам на тестирование отдали масштабную систему управления ресурсами одной крупной организации. Не так важно, для чего именно она использовалась. Главное — эта система, по сути, была платформой, через которую проходили заявки многочисленных партнерских и подрядных организаций.
Цель проекта: получить несанкционированный доступ к системе, смоделировать действия злоумышленника и выяснить, какой ущерб могут нанести анонимный атакующий из интернета или нелояльный сотрудник партнерской организации.
Система была самописной, но ее сердце — механизм аутентификации и распределения прав — билось на базе коробочного SSO-решения. Доступ к системе управления ресурсами имели не только штатные сотрудники нашего заказчика, но и сотни специалистов извне.
Вся эта разношерстная публика проходила аутентификацию через тот самый вендорский SSO. Недолго думая, мы решили направить наш исследовательский азарт именно туда. Спойлер: это было лучшее решение за весь проект.
Дисклеймер: о вендорах, багах и чувстве такта
Прежде чем мы начнем препарировать найденные уязвимости, важно сделать одну оговорку. Цель этого разбора — не бросить камень в огород конкретного вендора. Современная разработка ПО — это сумасшедшая гонка. Новые фичи, жесткие дедлайны, требования по интеграции с десятками других систем... В таком темпе ошибки — почти неизбежная плата за скорость и сложность. Наша задача как исследователей — помочь сделать продукты лучше, а не покритиковать вендоров. В конечном счете, безопасность — не поиск виноватых, а совместная работа по устранению недостатков.
Также стоит четко проговорить: у нас нет никаких сведений, что эти уязвимости когда-либо эксплуатировались злоумышленниками «в дикой природе». Все баги были обнаружены в контролируемых условиях анализа защищенности.
И наконец, нельзя забывать, что безопасность любого продукта — дело обоюдное. Ответственность лежит не только на вендоре, который пишет код, но и на компании, которая этот код внедряет. Даже самый надежный и защищенный софт можно превратить в решето из-за неверной конфигурации, кривой интеграции или просто оставленных по умолчанию паролей. Поэтому многое из того, что мы нашли, стало возможным, в том числе благодаря конкретному контексту внедрения, настройки и применения ПО у заказчика.
Низко висящие фрукты
Поскольку вендор все исправил, можно с чистой совестью раскрыть карты. Речь идет о системе Blitz Identity Provider. Впрочем, это могла бы быть любая другая система контроля доступа, вряд ли бы наши действия изменились.
Первый шаг при исследовании веб-приложения — составить его карту: понять архитектуру, ключевые функции и потоки данных. Нас интересуют «болевые точки» любой системы: аутентификация, регистрация, восстановление пароля, управление сессиями и правами доступа. Мы ищем, где приложение принимает данные от пользователя, как их обрабатывает и как взаимодействует с другими компонентами инфраструктуры, в нашем случае — с SSO.
Затем приходит время для творчества — моделирования угроз и выдвижения гипотез. Здесь мы надеваем шляпу злоумышленника и играем в игру «А что, если...?». Глядя на составленную схему, мы задаем себе сотни вопросов, основанных на типичных шаблонах уязвимостей (привет, OWASP Top 10) и логике конкретного приложения.
«А что, если ссылка для смены почты не привязана к сессии?»
«А что, если на ввод кодов подтверждения нет лимитов?»
«А что, если JWT-токен примут без подписи?»
Каждая из этих гипотез — вектор атаки, основанный на понимании того, как разработчики могли ошибиться.
И лишь затем приходит время проверить эти гипотезы и реализовать возможные атаки.
Баг #1. Захват аккаунта через смену почты
Документация по уязвимости
Представьте, что вы можете сменить почтовый ящик, к которому привязан чужой аккаунт, просто отправив его владельцу специальную ссылку. Звучит как сценарий из фильма про хакеров из 90-х? А вот и нет.
Первым делом мы инициировали смену почты в своем аккаунте, указав новый email, который контролировали (назовем его hacker@evil.com).
Система, как и положено, отправила на наш новый адрес (hacker@evil.com) письмо с кодом подтверждения и специальной ссылкой. Эта ссылка содержала некий идентификатор, связывающий сессию смены почты с новым адресом.
Выяснилось, что система не проверяла, а кто, собственно, переходит по ссылке. Она не была привязана к нашей учетной записи или сессии — и это была ошибка. Мы могли отправить эту ссылку любому авторизованному в системе пользователю. Если жертва кликала по ней, ее учетная запись без каких-либо дополнительных подтверждений привязывалась к нашей почте hacker@evil.com.
Теперь коды сброса пароля и всевозможные уведомления летели прямиком к нам на почту. Оставалось только запросить сброс пароля, перехватить код и окончательно завладеть учетной записью.
Баг #2. Полный перебор кодов подтверждения
Документация по уязвимости
Мы продолжили изучать процесс смены почты. Что, если не отправлять ссылку, а попытаться угадать код подтверждения, который приходит на email? Если такое осуществимо, то это шаг к эксплуатации другого обнаруженного бага — отправки злоумышленником каких угодно писем с почты нашего заказчика на любой указанный email. Но не будем забегать вперед.
Обычно системы защищаются от этого простыми, как топор, методами: три-пять попыток, и включается временная блокировка на минуту или более. При таких простых методах защиты шансы подобрать шестизначный код брутфорсом примерно 1 к 333 333 за итерацию при трех попытках и 1 к 200 000 — при пяти. Вот только в нашем случае тайм-аута почему-то не было.
Можно было запросить код на чужой email, а затем перебирать комбинации не в случайном порядке, а системно и последовательно: 000001, 000002, 000003… и так при необходимости до 999999. Для такого взлома требуются только терпение и запас времени — не нужно даже писать никаких скриптов для брутфорса, подойдет и простенький фаззер.

Баг #3. Угадывание кода при запросе авторизации
Документация по уязвимости
Окей, со сменой почты разобрались, а что с регистрацией нового аккаунта? Можно ли зарегистрировать учетку и привязать к ней почту, которая уже используется кем-то из партнеров заказчика? Если да, то, может быть, получится захватить аккаунты уже на сторонних сервисах, которые доверяют этому SSO?
Здесь разработчики, казалось, учли ошибки: после трех неверных попыток ввода кода система требовала запросить его заново. Вот только они забыли настроить задержку между этими запросами. Это означало, что злоумышленник мог совершать три попытки в секунду, тут же перезапрашивать код и снова повторять цикл.
Мы проверили эту гипотезу на практике: написали скрипт на Python, настроили три параллельных потока для брутфорса, и... через два с половиной дня заветный код был у нас в кармане. Конечно, мы создали большой объем трафика, и тем не менее достигли своей цели. Получалась беспроигрышная лотерея. Да, шансы все еще невелики, но когда на кону доступ к инфраструктуре крупной компании, многие готовы испытать удачу. Особенно когда для этого достаточно написать простенький скрипт.
Что это давало?
Мы регистрировали новый аккаунт в системе заказчика.
Привязывали к нему почту сотрудника компании-партнера, подобрав код.
Шли на сайт партнера и выбирали «Войти через SSO заказчика».
Сервис партнера видел подтвержденный email и любезно объединял нашу новую учетку с существующим аккаунтом жертвы.
Для устранения уязвимости требовалось добавить задержку не менее 1 минуты между перезапросами кода, а также считать общее число запросов и блокировать сессии пользователей, которые ими злоупотребляют.
Баг #4. JWT-токены и слепое доверие
Документация по уязвимости
Вишенкой на торте стала уязвимость в компонентах SSO-системы API «Управление пользователями» и API «Администрирование». Эти компоненты использовали для авторизации JWT-токены, передаваемые в заголовке Authorization, — современный и, казалось бы, надежный стандарт.
Если «на пальцах», JWT-токен — это как цифровой паспорт. В нем есть заголовок с информацией об алгоритме (обычно SHA-256), полезная нагрузка (ваше имя, права доступа) и подпись, подтверждающая подлинность.
Самое главное в этой схеме — проверка подписи на стороне сервера. Без нее любой может нарисовать себе паспорт с правами администратора всей системы. Именно это мы и сделали, так как сервер не проверял подпись. Можно лишь предполагать, почему. Например, в тестовых средах эту проверку иногда отключают намеренно для упрощения отладки.
В SSO в JWT-токене можно было выбрать алгоритм "alg": "none" для определенных API. Это означало отказ от подписи как таковой. Мы могли сгенерировать токен с любыми правами для любого пользователя и захватить полный контроль над API: создавать и удалять пользователей, получать данные о них, менять пароли, управлять существующими в системе подсистемами и добавлять новые.
Хождение по мукам: как мы пытались сообщить об уязвимостях
А теперь перейдем к нашим ошибкам. Прежде всего, еще до полного отчета о пентесте мы сообщили о найденных багах заказчику. Тот обезопасил инфраструктуру и передал информацию об уязвимостях поставщику SSO, который выпустил хотфикс. Затем прошел месяц, потом второй. Мы забеспокоились, ведь проблемная версия SSO могла быть развернута в других компаниях.

Ожидание исправлений затянулось, поэтому мы написали в поддержку вендора через специальную форму на сайте. Информация об уязвимостях сразу же была опубликована. Оказалось, что уже готов полноценный патч.
Только после этого мы обратились во ФСТЭК, предоставив всю информацию и ссылки на публикации вендора. В итоге всё закончилось хорошо. Но сколько времени было потеряно из-за попыток взаимодействия «через два рукопожатия» — с вендором через заказчика и поставщика!
Что делать с 0-day в вендорском софте: несколько лайфхаков
Вся эта история с уязвимостями, ожиданием их исправления и финальным хэппи-эндом учит нескольким важным вещам.
Проводите регулярные пентесты всей инфраструктуры
Уязвимости в стороннем ПО часто обнаруживаются не прицельным аудитом, а в ходе комплексного тестирования. Вы ищете одно, а находите зацепку, которая выводит на совершенно другую, куда более опасную проблему. Не бойтесь расширять скоуп — самые интересные находки часто ждут на стыке систем.
Сообщайте о 0-day сразу всем: и вендору, и регулятору
Изначально мы действовали, как нам казалось, логично и этично: нашли проблему — сообщили заказчику, чтобы тот передал вендору. Мы посчитали обращение во ФСТЭК крайней мерой, «тяжелой артиллерией» для самых запущенных случаев. На самом деле это нормальный рабочий процесс. Правильный подход — бить во все колокола одновременно (если позволяет NDA).
Вендор получает технические детали для исправления.
ФСТЭК выступит как административный ресурс, который не даст вендору «забыть» о вашей заявке. Одно другому не мешает, а шансы на быстрое решение проблемы повышает кратно.
Используйте WAF в сочетании с SOC и другие средства защиты как систему раннего оповещения
WAF (Web Application Firewall) не спасет от хитроумной атаки на логику приложения вроде той, что мы провернули со сменой почты по ссылке. Для него эти запросы выглядели бы абсолютно легитимными. Но это не значит, что WAF бесполезен.
Он может засечь аномалии, связанные с брутфорсом или попытками SQL-инъекций через 0-day уязвимости в вендорском софте. Даже если WAF не сможет заблокировать атаку, он сгенерирует алерт, который увидит ваша команда мониторинга. А предупрежден — значит вооружен.
Кроме того, WAF может прикрыть вас до прибытия «официальной» помощи в виде патча. Зная точную сигнатуру атаки, ваша команда безопасности может создать на WAF кастомное правило, блокирующее конкретные вредоносные запросы.
Нашли уязвимость с "alg":"none" в JWT-токене? Создайте правило, которое немедленно блокирует любой запрос, где в JWT-токене встречается эта строка. Просто и эффективно.
Обнаружили брутфорс кодов подтверждения? Усильте правила по ограничению частоты запросов (rate limiting) именно для этого конкретного эндпоинта. Например, не более пяти запросов в минуту с одного IP-адреса.
Предоставляйте исчерпывающую информацию
Отчет в стиле «у вас тут дыра, все сломано, чините» отправится прямиком в корзину или, в лучшем случае, вызовет шквал уточняющих вопросов, затянув исправление багов на недели.
Помните: на той стороне сидит инженер, которому нужно понять, что вы сделали. Ваша задача — взять его за руку и провести по всем шагам, не оставив ни единого шанса сказать «у меня не воспроизводится».
Хороший репорт — это исчерпывающее руководство.
Четкое название уязвимости.
Пошаговый сценарий воспроизведения.
Точные версии ПО, эндпоинты, параметры.
Примеры запросов и ответов сервера.
Понятная демонстрация влияния уязвимости на систему (сама по себе регистрация УЗ на чужую почту не критична, чего не скажешь о захвате с помощью этого шага аккаунтов на сайтах партнеров).
И в качестве бонуса короткий скринкаст, демонстрирующий атаку.
Чем меньше у инженеров вендора будет поводов для сомнений, тем быстрее вы получите заветный патч.
Не бойтесь принимать тяжелые решения (и менять вендоров)
Это, пожалуй, самый радикальный, но иногда единственно верный совет. Если вы из раза в раз находите в одном и том же продукте критические уязвимости, вендор реагирует на сообщения месяцами, а качество патчей оставляет желать лучшего — пора задать себе неудобный вопрос: «А не лучше ли нам расстаться?»
Да, миграция на новое ПО — это дорого, больно и сопряжено с простоями. Никто не любит такие проекты. Но теперь посчитайте, во сколько обойдется вашему бизнесу успешная атака через этот «дырявый» компонент: финансовые потери, репутационный ущерб, штрафы регуляторов. Иногда стоимость риска настолько высока, что смена вендора из категории «немыслимо» переходит в категорию «необходимо». Тщательно взвесьте все «за» и «против», но не держитесь за продукт только потому, что к нему привыкли.
Заключение
Эта история показывает простую вещь: в современной инфраструктуре вы никогда не контролируете всё на сто процентов. Сторонние продукты, библиотеки, облачные сервисы — каждый элемент может принести сюрприз в виде критической уязвимости. И это нормально. Разработчики — не боги, они работают в жестких дедлайнах, интегрируют десятки систем и просто не успевают предусмотреть все векторы атак. Вопрос не в том, будут ли баги, а в том, как быстро их закроют.
Правильный путь: писать вендору напрямую через форму поддержки, уведомлять ФСТЭК, держать заказчика в курсе. Один канал может засбоить, второй — проигнорировать, но три канала одновременно максимизируют шанс на оперативную реакцию.
Учитывая такой опыт, мы стали сразу сообщать о багах во ФСТЭК, чтобы регулятор начинал без промедлений работать с вендором. Тогда к моменту окончания проекта у добросовестного вендора и заказчика с большой вероятностью будет готовый фикс.
Если вы внедряете сторонний софт, заранее продумайте не только техническую интеграцию, но и план действий на случай обнаружения 0-day. Кому звонить? Куда писать? Как быстро можно откатиться на другое решение? Эти вопросы могут оказаться важнее любых сертификатов безопасности.
А как у вас выстроены процессы реагирования на уязвимости в стороннем ПО? Сталкивались ли с ситуациями, когда вендор забывал про баг-репорты? Делитесь опытом в комментариях.

PURP — Telegram-канал, где кибербезопасность раскрывается с обеих сторон баррикад
t.me/purp_sec — инсайды и инсайты из мира этичного хакинга и бизнес-ориентированной защиты от специалистов Бастиона