
Минимальный PoC на Python для CVE-2026-40179: внедряет вредоносное имя метрики через неаутентифицированный Prometheus remote_write, вызывая хранимую XSS в веб-интерфейсе. Заглушки protobuf не требуются.
remote_writeМинимальный proof of concept, почти не требующий зависимостей, для CVE-2026-40179 (GHSA-vffh-x6r8-xx99): хранимый межсайтовый скриптинг в веб-интерфейсе Prometheus, доставляемый через неаутентифицированную конечную точку remote_write.
Prometheus 3.x отображает имена метрик и значения меток в веб-интерфейсе через innerHTML без экранирования. Поскольку Prometheus v3 ослабил проверку меток, разрешив произвольный UTF-8 — включая <, > и ", — имя метрики может содержать HTML-нагрузку, которая выполняется при просмотре пользователем веб-интерфейса.
| Затронутые версии | Prometheus 3.0.0 – 3.5.1 |
| Исправлено в | 3.5.2 |
| Воздействие | Хранимый XSS, выполняется в браузере любого пользователя, просматривающего затронутую метрику |
| Уязвимые поверхности | Всплывающие подсказки страницы Graph, Metrics Explorer |
Самому XSS нужен способ доставить специально сформированный ряд в TSDB. Подойдёт любой из этих вариантов:
remote_write (используется в этом PoC),Prometheus, открытый в интернет или LAN и не защищённый аутентификацией, предоставляет всё перечисленное без дополнительных усилий, что делает эту уязвимость практически эксплуатируемой, а не теоретической.
Отправляет один POST /api/v1/write, содержащий один временной ряд, чей __name__ равен:
pentest_poc_cve_2026_40179
Protobuf-сообщение WriteRequest закодировано вручную примерно в 40 строках, поэтому здесь нет зависимости от protobuf, prometheus_pb2 или promtool — нужны только python-snappy для сжатия при передаче и requests для POST. Это упрощает развёртывание на промежуточном хосте (jump host), где нельзя собрать сгенерированные protobuf-заглушки.
python3 -m pip install python-snappy requests
Для python-snappy требуется C-библиотека Snappy:
# Debian/Ubuntu
sudo apt install libsnappy-dev
# macOS
brew install snappy
python3 cve-2026-40179-poc.py <target_ip>
Скрипт предполагает HTTPS на порту 9090 с отключённой проверкой сертификата (типично для внутреннего экземпляра с самоподписанным сертификатом). Если ваша цель — обычный HTTP или другой порт, измените строку url в main().
Успешная инъекция возвращает HTTP 204:
POST https://192.0.2.10:9090/api/v1/write -> 204
Запросите ряд в веб-интерфейсе или API Prometheus — он должен вернуться немедленно:
{__name__=~"pentest_poc.*"}
Представление Table корректно экранирует его и показывает нагрузку как обычный текст. Это ожидаемо и не является уязвимой поверхностью.
Откройте Metrics Explorer (браузер метрик рядом с полем запроса), найдите pentest_poc и наведите курсор на запись в списке.
В ходе тестирования на версии 3.2.1 путь через Metrics Explorer срабатывал стабильно, тогда как всплывающая подсказка на вкладке Graph не воспроизводилась — внедрённый сэмпл (значение 1.0) не отображался на построенном графике, и наведение на легенду также не вызывало срабатывания. Обе поверхности указаны в advisory; если одна не срабатывает на вашей целевой версии, попробуйте другую, прежде чем делать вывод, что уязвимости нет.
Нагрузка по умолчанию использует console.log(), чтобы не мешать тем, кто специально её не ищет. Для наглядной демонстрации — например, для скриншота в отчёт — измените METRIC_NAME, чтобы использовать alert():
METRIC_NAME = ('pentest_poc_cve_2026_40179'
'')
Внедрённый ряд сохраняется постоянно. После записи он остаётся в TSDB до истечения срока хранения (retention). Для его удаления требуется admin API, который отключён по умолчанию:
# Only works if Prometheus was started with --web.enable-admin-api
curl -X POST -g 'https://<target>:9090/api/v1/admin/tsdb/delete_series?match[]={__name__=~"pentest_poc.*"}'
curl -X POST 'https://<target>:9090/api/v1/admin/tsdb/clean_tombstones'
Если admin API отключён, удалённого пути очистки не существует. Единственная альтернатива — запустить promtool tsdb локально на хосте, к которому у вас, скорее всего, нет доступа.
Практические последствия:
job="pentest_poc"), чтобы её было легко найти и было очевидно, что это тестовый артефакт.Чтобы воспроизвести уязвимость на одноразовом экземпляре, а не на реальной системе:
docker run --rm -p 9090:9090 prom/prometheus:v3.2.1 \
--config.file=/etc/prometheus/prometheus.yml \
--web.enable-remote-write-receiver \
--web.enable-admin-api
Затем направьте скрипт на 127.0.0.1 (измените схему URL на http:// в main()). Запуск с --web.enable-admin-api означает, что после теста вы действительно сможете удалить ряд.
--web.config.file с basic_auth_users.--web.enable-remote-write-receiver, если в этом нет необходимости, и никогда не открывайте его без аутентификации.Обратите внимание: только пункт (1) устраняет XSS, но Prometheus без аутентификации сам по себе остаётся существенной поверхностью раскрытия информации — полный список имён метрик, перечень целей scrape и внутренние имена хостов доступны для чтения без учётных данных.
Этот материал опубликован для защитников, исследователей и тестировщиков, работающих в рамках санкционированных мероприятий. Уязвимость публично раскрыта и исправлена в апстриме.
Запуск этого скрипта против системы записывает в неё постоянные данные. Не направляйте его на инфраструктуру, которой вы не владеете или на тестирование которой не имеете явного письменного разрешения.
MIT — см. LICENSE.