
PoC Python mínimo para CVE-2026-40179: injeta um nome de métrica malicioso via Prometheus remote_write não autenticado para acionar XSS armazenado na interface web. Não são necessários stubs de protobuf.
remote_writeUma prova de conceito mínima, com poucas dependências, para CVE-2026-40179 (GHSA-vffh-x6r8-xx99): cross-site scripting armazenado na interface web do Prometheus, entregue por meio de um endpoint remote_write sem autenticação.
O Prometheus 3.x renderiza nomes de métricas e valores de labels na interface web via innerHTML sem escape. Como o Prometheus v3 flexibilizou a validação de labels para permitir UTF-8 arbitrário — incluindo <, >, e " — um nome de métrica pode carregar um payload HTML que é executado quando alguém navega pela interface.
| Afetados | Prometheus 3.0.0 – 3.5.1 |
| Corrigido em | 3.5.2 |
| Impacto | XSS armazenado, executa no navegador de qualquer usuário que visualizar a métrica afetada |
| Superfícies vulneráveis | Tooltips da página Graph, Metrics Explorer |
O XSS em si precisa de uma forma de colocar uma série criada no TSDB. Qualquer uma destas opções serve:
remote_write aberto (o que esta PoC usa),Um Prometheus exposto à internet ou à LAN, sem autenticação na frente, oferece tudo isso de graça, e é isso que o torna explorável na prática, e não apenas teórico.
Envia um único POST /api/v1/write contendo uma série temporal cujo __name__ é:
pentest_poc_cve_2026_40179
O WriteRequest em protobuf é codificado manualmente em ~40 linhas, portanto não há dependência de protobuf, prometheus_pb2 ou promtool — apenas python-snappy para a compressão no transporte e requests para o POST. Isso facilita colocá-lo em um host de salto onde você não pode compilar stubs protobuf gerados.
python3 -m pip install python-snappy requests
python-snappy precisa da biblioteca C do Snappy:
# Debian/Ubuntu
sudo apt install libsnappy-dev
# macOS
brew install snappy
python3 cve-2026-40179-poc.py <target_ip>
O script assume HTTPS na porta 9090 com a verificação de certificado desabilitada (típico para uma instância interna com certificado autoassinado). Se o seu alvo for HTTP simples ou estiver em outra porta, edite a linha url em main().
Uma injeção bem-sucedida retorna HTTP 204:
POST https://192.0.2.10:9090/api/v1/write -> 204
Consulte a série na interface do Prometheus ou na API — ela deve retornar imediatamente:
{__name__=~"pentest_poc.*"}
A visão Table faz o escape corretamente e exibe o payload como texto simples. Isso é esperado e não é a superfície vulnerável.
Abra o Metrics Explorer (o navegador de métricas ao lado da caixa de consulta), pesquise por pentest_poc e passe o mouse sobre a entrada listada.
Em testes de campo contra 3.2.1, o caminho do Metrics Explorer disparou de forma confiável, enquanto o tooltip da aba Graph não reproduziu — a amostra injetada (valor 1.0) não foi renderizada visivelmente no gráfico plotado, e passar o mouse sobre a legenda também não a disparou. Ambas as superfícies são citadas no advisory; se uma não disparar na sua versão alvo, tente a outra antes de concluir que ela não é vulnerável.
O payload padrão usa console.log() para não interromper quem não está procurando por ele. Para uma demonstração visível — uma captura de tela para um relatório, por exemplo — altere METRIC_NAME para usar alert():
METRIC_NAME = ('pentest_poc_cve_2026_40179'
'')
A série injetada é persistente. Uma vez gravada, ela permanece no TSDB até que a retenção a expire. Removê-la exige a admin API, que está desabilitada por padrão:
# 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'
Se a admin API estiver desabilitada, não há caminho de limpeza remota. A única alternativa é executar promtool tsdb localmente no host, ao qual você provavelmente não tem acesso.
Consequências práticas:
job="pentest_poc"), para que seja fácil de encontrar e obviamente um artefato de teste.Para reproduzir contra uma instância descartável, em vez de algo em produção:
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
Em seguida, aponte o script para 127.0.0.1 (altere o esquema da URL para http:// em main()). Iniciar com --web.enable-admin-api significa que você pode de fato excluir a série depois.
--web.config.file com basic_auth_users.--web.enable-remote-write-receiver a menos que precise, e nunca o exponha sem autenticação.Observe que apenas (1) corrige o XSS, mas um Prometheus sem autenticação continua sendo uma superfície substancial de divulgação de informações por si só: a lista completa de nomes de métricas, o inventário de alvos de scrape e os hostnames internos são todos legíveis sem credenciais.
Isso foi publicado para defensores, pesquisadores e testadores que atuam sob autorização. A vulnerabilidade é publicamente divulgada e corrigida no upstream.
Executar isso contra um sistema grava dados persistentes nele. Não aponte para infraestrutura que você não possui ou para a qual não tenha permissão explícita por escrito para testar.
MIT — consulte LICENSE.