Признавать уязвимости в Enterprise-продукте непросто. Но на той стадии развития, когда среди его пользователей крупные организации, которым важны требования регуляторов и риски, внутренних проверок и пен-тестов становится уже недостаточно. Участие в bugbounty — это показатель зрелости продукта и для рынка, и для самого разработчика.
Багбаунти (bug bounty) — это программа денежных вознаграждений за найденные уязвимости. В ноябре 2024 года мы в команде Orion soft запустили такую программу для нашей платформы виртуализации zVirt на площадке Standoff Bug Bounty.
Сегодня я расскажу о нашем опыте bugbounty, расскажу, какие уязвимости были найдены в ходе тестирования и как мы их исправляли.

Что нашли белые хакеры
За время работы программы исследователи прислали нам 24 отчета. В итоге после валидации подтвердилось 10 уязвимостей. Критических среди них не оказалось. Две из них — слепая SSRF с утечкой конфигураций и уязвимость открытого перенаправления в OAuth — представляли наибольшую угрозу и получили высокий уровень опасности. Остальные восемь были оценены как уязвимости среднего уровня. Самые серьезные мы уже закрыли, исправления внесены, начиная с релиза zVirt 4.4. С оставшимися разбираемся прямо сейчас, и изменения войдут в ближайшие релизы.
Дальше я разберу пять самых показательных находок: от blind SSRF с утечкой конфигов до раскрытия трейсбеков базы данных. Расскажу, как эти уязвимости можно было эксплуатировать и какие технические решения мы применили для защиты.
1. Blind SSRF с подменой хоста для бэкапов и IDOR
Уровень: Высокий
В чем суть:
Была найдена связка уязвимостей в механизме бэкапов конфигурационных файлов: blind SSRF плюс IDOR. В результате у злоумышленника была возможность просканировать внутреннюю сеть через сервер виртуализации, подменить адрес хранилища бэкапов на свой и получить архивы с конфигурационными файлами, включая пароли в открытом виде.
Уязвимость работала после обычной аутентификации. Авторизованный пользователь без административных прав мог обращаться к эндпоинтам админки, которые должны были быть ему недоступны. Первый шаг — разведка через blind SSRF. Отправляя запросы к внутренним хостам через функционал бэкапов, можно было по ответам сервера понять топологию сети:
Permission denied — хост существует, доступ ограничен;
Connect call failed — хоста нет в сети;
Timeout exceeded — порт закрыт или фильтруется.
Это позволяло картографировать инфраструктуру без прямого доступа к ней и без срабатывания стандартных средств обнаружения атак.
Второй шаг — IDOR. Через PUT-запрос злоумышленник мог создать или изменить цель для бэкапов и указать там внешний хост под своим контролем. Когда система выполняла задачу бэкапа, она отправляла архив с конфигурационными файлами именно туда. В архиве лежали критичные данные: пароль администратора Grafana в plaintext, ключи доступа, настройки компонентов инфраструктуры.
Получив доступ к этим данным, атакующий мог войти в Grafana под админом, провести эскалацию привилегий в системе виртуализации, использовать украденные ключи для lateral movement по инфраструктуре и атаковать другие узлы сети уже изнутри периметра.
Как мы это исправили:
Добавили обязательную проверку авторизации для всех методов, работающих с sshTargetHost: GET, POST, PUT и DELETE. Теперь недостаточно просто войти в систему — нужны соответствующие права администратора. Без них пользователь не увидит список целей для бэкапов, не сможет создать новую цель или изменить существующую. Это закрывает как возможность разведки через SSRF, так и подмену хоста для перехвата конфигурационных файлов.
2. Open Redirect с угоном аккаунта
Уровень: Высокий
В чем суть:
В системе аутентификации на базе Keycloak нашли уязвимость открытого перенаправления в параметре redirect_url. Злоумышленник мог подменить адрес, куда система отправляет пользователя после успешного входа, и перенаправить его на свой домен. Главная опасность — не сам редирект, а то, что вместе с пользователем утекал одноразовый авторизационный код (authorization code). С этим кодом можно было получить токены доступа и зайти в систему под чужой учетной записью.
Атака работала через OAuth 2.0 с расширением PKCE (Proof Key for Code Exchange). В обычном сценарии PKCE защищает от перехвата кода: клиент генерирует случайную строку code_verifier, вычисляет из нее хеш code_challenge и отправляет его при запросе авторизации. Когда сервер выдает код, клиент должен предъявить оригинальный code_verifier, чтобы обменять код на токены. Без него код бесполезен.
Но здесь злоумышленник сам инициировал процесс авторизации и сам генерировал пару code_verifier/code_challenge. Дальше он подменял redirect_url в с��ылке, используя специальные символы вроде %e5, которые браузер преобразовывал в ?, меняя структуру URL и обходя первичную валидацию. В результате параметр redirect_url указывал на домен злоумышленника.
Жертва переходит по такой ссылке. Если пользователь уже был авторизован, то происходил немедленный редирект на поддельный домен с кодом в URL. Если не авторизован, то сначала производился ввод логина и пароля, затем редирект. Код попадал на сервер злоумышленника (например, через коллаборатор oastify.com). На руках у злоумышленника оказывался перехваченный код и свой code_verifier. После этого он отправлял запрос на обмен кода на токены и получал полный доступ к аккаунту жертвы. Один клик по ссылке, и аккаунт скомпрометирован.
Как мы это исправили:
Внедрили строгую валидацию redirect_url на стороне сервера через allowlist: теперь перенаправление возможно только на заранее разрешенные домены и пути, жестко прописанные в конфигурации безопасности. Дополнительно усилили защиту от XSS-атак, которая теперь включена по умолчанию и управляется через параметр XSSProtection. Реализовали ее через фильтр на Java, который блокирует попытки манипуляции с URL-параметрами.
3. Reflected XSS на главной странице
Уровень: Средний
В чем суть:
На главной странице системы нашли reflected XSS через параметр error_description. Это был обход уже внедренных защит: похожую XSS ранее закрывали, но исследователь нашел способ обойти фильтры и снова внедрить JavaScript-код в страницу.
Reflected XSS здесь работала через специально сформированный URL. Злоумышленник собир��л ссылку с вредоносным кодом внутри error_description и использовал комбинацию HTML-тегов и событий наподобие onerror и onmouseover. Сервер возвращал страницу с ошибкой, подставлял содержимое параметра в шаблон без достаточной санитизации, и браузер выполнял код прямо в контексте домена системы.
Сценарий был типичным: жертва переходит по ссылке, видит страницу ошибки, а в фоне уже отрабатывает JavaScript. Так можно красть сессионные cookie, подменять содержимое страницы, показывать фишинговые формы, перенаправлять пользователя на другие ресурсы. Если жертва имеет права администратора, через такую XSS можно было получить доступ к его сессии и дальше уже работать от его имени. Формально по программе багбаунти уязвимость получила средний уровень, но в связке с другими находками ее потенциал выше.
Как мы это исправили:
Добавили экранирование XML-символов для содержимого error_description и других похожих полей. Теперь спецсимволы вроде <, >, " и ' превращаются в безопасные сущности, и браузер воспринимает их как текст, а не как HTML или JavaScript. Это ломает используемые обходные техники и закрывает класс уязвимостей, связанных с выводом сырых параметров в шаблон.
4. Утечка данных о пользователях через API
Уровень: Средний
В чем суть:
Обычный авторизованный пользователь без прав администратора мог через API получить полную информацию о доменах системы и всех пользователях в этих доменах. Эти данные не показывались в веб-интерфейсе, но API-эндпоинты /ovirt-engine/api/domains и /ovirt-engine/api/domains/{domain-id}/users отдавали их без дополнительных проверок прав.
Механика была простой: пользователь проходил обычную авторизацию, отправлял GET-запрос к эндпоинту доменов и получал в ответ список всех доменов с их настройками. Дальше можно было запросить список пользователей конкретного домена и даже получить доступ к опциям отдельных пользователей через /ovirt-engine/api/users/{user-uuid}/options. API не требовал административных привилегий для этих операций, хотя информация явно относилась к данным, которые должны быть доступны только администраторам.
На первый взгляд это не выглядит как критическая проблема. Ведь злоумышленник не получает пароли и не может напрямую изменить критичные настройки. Но для атакующего это отличный шанс: полный список учетных записей, структура доменов, информация о ролях пользователей. С такими данными можно составить карту инфраструктуры, выбрать цели для таргетированного фишинга (например, найти админов), подготовить атаку на конкретные учетные записи, провести social engineering против ключевых пользователей.
И даже больше. Через эндпоинт опций пользователей можно было не только читать, но и потенциально модифицировать настройки в зависимости от реализации прав на конкретные операции. Даже если прямая модификация была ограничена, сама утечка метаданных о пользователях уже нарушала принцип наименьших привилегий и создавала плацдарм для дальнейших атак.
Как мы это исправили:
Добавили проверку прав администратора для всех запросов к API доменов и пользователей. Теперь эндпоинты /ovirt-engine/api/domains и связанные с ними методы работы с пользователями требуют административных привилегий. Обычный авторизованный пользователь получает отказ в доступе, если попытается запросить информацию о доменах или списке пользователей. Это закрывает утечку метаданных и приводит контроль доступа в API в соответствие с логикой веб-интерфейса.
5. Трейсбеки базы данных в ответах API
Уровень: Средний
В чем суть:
API-эндпоинт /ovirt-engine/api/vms при обработке невалидных запросов возвращал подробные трейсбеки ошибок из базы данных. Вместо короткого сообщения об ошибке сервер отдавал полный стек вызовов с техническими деталями: названия таблиц и полей в БД, версию СУБД, пути к файлам конфигурации, фрагменты SQL-запросов.
Чтобы получить эту информацию, злоумышленнику было достаточно авторизоваться в системе и отправить запрос с намеренно искаженными параметрами. Например, изменить значение параметра page с числа на букву: вместо page=1 подставить page=B. Сервер пытался обработать запрос, база данных выдавала ошибку из-за невалидного параметра, и вся эта внутренняя кухня попадала в HTTP-ответ.
На первый взгляд это выглядит как техническая мелочь — ну показался трейсбек, ну и что. Но для атакующего это ценная разведка. Зная структуру таблиц, имена полей и типы данных, можно готовить целенаправленные SQL-инъекции, если где-то в системе найдется точка входа. Версия СУБД подсказывает, какие известные уязвимости искать. Пути к файлам помогают понять архитектуру системы и найти другие потенциальные векторы атаки. Даже просто знание бизнес-логики через названия таблиц уже дает преимущество — становится понятно, как система устроена изнутри, какие сущности связаны, где хранятся ��ритичные данные.
Проблема усугублялась тем, что такие трейсбеки видели не только администраторы, но и обычные авторизованные пользователи. API отдавал одинаковый объем информации всем, кто мог отправить запрос, не учитывая уровень привилегий.
Как мы это исправили:
Реализовали скрытие служебных полей и деталей ошибок для пользователей без роли администратора. Теперь API возвращает стандартизованные сообщения об ошибках без технических подробностей про базу данных, пути к файлам или версии ПО. Полные трейсбеки логируются только во внутренние логи системы, где их могут посмотреть разработчики и администраторы при отладке. Внешнему пользователю выдается короткий ответ вроде "Invalid request parameter". Этого достаточно, чтобы понять суть проблемы, но без раскрытия внутреннего устройства.
В рамках программы мы также нашли и закрыли несколько менее критичных, но показательных проблем. Отключили Directory Listing на веб-сервере, чтобы не светить структуру каталогов и содержимое служебных директорий. Ограничили возможность DNS Zone Transfer, оставив ее только доверенным серверам, чтобы не раздавать карту внутренней сети всем желающим. Исправили Self-XSS, где пользователь мог навредить только себе, но все равно показывалось, что интерфейс не до конца аккуратно работает с пользовательским вводом.
Заключение
Найденные проблемы мы закрыли в релизах 4.3, 4.4 и 4.5. Но важнее другое: каждая из них выявила то или иное слабое звено, которое неизбежно встречается в процессах у каждого разработчика, будь то ревью кода или валидация входных данных. Факт их появления в продукте стал для нас сигналом к тому, чтобы усилить и пересмотреть практики безопасной разработки. Мы не завершаем участие в bugbounty. Пока что тестирование продолжается в закрытом контуре, но новыми результатами мы продолжим делиться.
Участие в этой программе стало еще одним компонентом защищенности zVirt. Кроме этого мы получили сертификат ФСТЭК, перешли на свежее безопасное ядро Linux от НИИ ИСП РАН, обеспечили изолированную сборку продукта, внедрили практики DevSecOps, создали руководство по харденингу и другое — обо всем этом у нас есть отдельный подробный материал. Здесь я разбирал точечные исправления, доработки и проекты в этой сфере.
Есть вопросы по техническим деталям или хотите обсудить что-то из разобранных уязвимостей? Пишите в комментариях.
