Что изменилось по сравнению с прошлым разом
В прошлый раз я уже разбирал, что пошло не так: долгие простои между заданиями, слишком много однообразного брутфорса и мои неясные объяснения, как оформить отчет об уязвимости.
В этот раз я учел всё. Брутфорс остался, но только там, где он двигает сюжет, — остальное вырезал. Добавил более красивый фронтенд, чтобы лаба выглядела как настоящее приложение. И главное — добавил эксплуатацию реальной CVE в Grafana, чтобы взлом был не только в рамках синтетики. Теперь цепочка атаки — это целостная история.
Аудитория и сюжет
Аудитория: студенты 1–3 курсов, направление — разработчики.
Сюжет простой: есть организация, надо провести пентест. Известен только домен. Внутри — приложение для документооборота (docs), маркетинговый сайт и Grafana.
Инфраструктура лабы
Лаба развернута на двух VPS. На первом живут три сервиса: основной бэкенд на ASP.NET Core, приложение с документооборотом и маркетинговый лендинг. На втором — Grafana.
Почему именно так разделил? Во-первых, хотелось показать близкую к реальности корпоративную архитектуру: там редко всё на одном хосте. Во-вторых, финальная задача — добиться RCE именно на Grafana. Лучше, чтобы это был изолированный сервер: и безопаснее для самой лабы, и нагляднее для студентов.
Разведка
Первый шаг любой атаки — собрать информацию о цели. Что за технологии? Что торчит наружу? Какая информация есть в открытом доступе?
Перебор поддоменов
С помощью ffuf студенты искали скрытые поддомены. Логика простая: домен example.com — публичный, но вдруг есть admin.example.com или еще что-то? На этом шаге обычно находится что-то интересное — оно и открывает следующий этап.
Подбор логинов и брутфорс паролей
Нашли маркетинговый сайт. Там — блок с контактами: потенциально валидные логины. Кроме того, и формат узнали — первая_буква_имени.фамилия.
Нашли форму входа в документооборот. Обнаружили, что сервис отвечал чуть по-разному на существующий и несуществующий логин, и это позволяло нащупать валидные аккаунты по словарю из популярных имен.
Потом — брутфорс паролей для найденных логинов. И вот у нас есть несколько аккаунтов.
SQL-инъекция
Залогинились — нашли уязвимый input в фильтрах. SQL-инъекция через браузер: студенты вводили payload прямо в поле поиска и видели, как приложение отдает данные, которые не должно отдавать. Через union sqli собрали структуру БД, нашли информацию о ролях и проставили себе админа.
Запрос на структуру БД:
-- Список таблиц:
' UNION SELECT NULL,tablename,NULL,NULL,NULL,NULL
FROM pg_catalog.pg_tables
WHERE schemaname='public'--
-- Колонки таблицы user_roles:
' UNION SELECT NULL,column_name,data_type,NULL,NULL,NULL
FROM information_schema.columns
WHERE table_name='user_roles'--
После перелогина появился новый раздел с доступами — обнаружили пароль от Grafana (Viewer).
Загрузка файла
В закрытом разделе — форма загрузки. Проверки расширений нет. Загружаем то, что не должны были загружать, — получаем публичную ссылку на S3 с полезной нагрузкой.
RCE через Grafana: CVE-2024-9264
На втором VPS крутится Grafana 11.0.0 — намеренно уязвимая версия. У нее есть экспериментальная фича под названием SQL Expressions, она позволяет постобрабатывать результаты запросов к источникам данных с помощью SQL. Под капотом это работает через DuckDB.
Проблема в том, что SQL-запросы передавались в DuckDB без нормальной санитизации. А у DuckDB есть расширение shellfs, которое позволяет выполнять произвольные команды на сервере. Достаточно установить это расширение через тот же SQL — и дальше можно делать что угодно.
Для эксплуатации нужно всего два условия: учетная запись в Grafana с правами Viewer или выше (мы их нашли на предыдущих шагах) и DuckDB, установленный в PATH сервера (в лабе это сделано намеренно). Запрос выглядит примерно так:
-- устанавливаем расширение для работы с шеллом
INSTALL shellfs FROM community;
LOAD shellfs;
-- выполняем произвольную команду на сервере
SELECT * FROM read_csv_auto('ls -la|');
Через эксплойт на Python мы могли выполнять произвольные команды, и тут нам пригодился payload из S3.
Важная оговорка: в реальных инсталляциях Grafana по умолчанию не поставляется с DuckDB — его нужно устанавливать вручную. Именно поэтому в продакшене эта уязвимость эксплуатируема не везде.
Как прошло
Студенты были активны: на каждом шаге задавали вопросы «Зачем?», «Почему именно так?». Даже немного поговорили об обходе СЗИ: как оставаться незаметным.
Но без сюрпризов не обошлось. В середине воркшопа упал S3 — как раз в момент задания с загрузкой файла. Студентам нужно было скачать payload для RCE, а хранилище недоступно. Пришлось импровизировать: выложил файл на raw.githubusercontent.com, и студенты скачивали оттуда. В следующий раз буду лучше проверять развернутую лабу.
Что защищает от всей этой цепочки
Безопасность — это слои. Ни одна мера сама по себе не закрывает всё, но каждая из них усложняет атаку и сужает окно возможностей для нападающего.
Разведка и перебор поддоменов. Не выставляйте наружу сервисы, которые не должны быть публичными. Внутренние админки, сервисы — за VPN. Поддомены, которые не нужны пользователям, не должны резолвиться извне.
Подбор логинов. Сервис не должен различать «логин не существует» и «неверный пароль» в ответах. Одинаковое сообщение об ошибке в обоих случаях — и перебор логинов становится бессмысленным.
Брутфорс паролей. Rate limiting на эндпоинтах авторизации, капча, временные блокировки после N неудачных попыток. Это не остановит целенаправленную атаку, но сделает ее дорогой по времени.
SQL-инъекция. Параметризованные запросы. Всегда. Никаких конкатенаций строк в SQL.
Загрузка файлов. Валидация не только расширения, но и содержимого файла (MIME-тип, сигнатура). Загруженные файлы не должны быть исполняемыми — храните их в S3 или отдельной директории без прав на выполнение.
CVE в зависимостях. Мониторинг CVE для всего, что крутится в вашей инфраструктуре. Grafana 11.0.0 вышла в мае 2024-го, CVE-2024-9264 была опубликована в октябре того же года. Пять месяцев уязвимость была открытой на всех непропатченных инсталляциях. Следите за релизами и обновляйтесь.
Что планирую улучшить
Несколько вещей хочу добавить в следующий раз:
- Во-первых, разобрать LLM как инструмент разведки. Большие языковые модели неплохо умеют анализировать публично доступную информацию о цели, генерировать гипотезы о технологиях и искать утечки в открытых источниках. Хочу добавить этот шаг в воркшоп — это актуально и интересно. Отдельным челленджем для студентов будет заставить агента пропентестить что-то — в нейросетях высокая защита от ИБ-кейсов.
- Во-вторых, более «живая» лаба. Сейчас приложение статичное. Хочется, чтобы там была какая-то активность: логи, пользователи, события. Так разведка становится ближе к реальному пентесту. Можно имитировать фишинг, перехватывать токены и многое другое.
Если у вас есть возможность провести подобное у себя в вузе или компании, делайте (или зовите!). Студенты, которые один раз своими руками прошли цепочку атаки, думают о безопасности иначе. И в наших силах давать студентам знания.
Спасибо за прочтение! Кроме того, я веду телеграм-канал Flexible Coding, где рассказываю о своем опыте в программировании.