
PoC mínimo en Python para CVE-2026-40179: inyecta un nombre de métrica malicioso a través de remote_write de Prometheus sin autenticación para desencadenar XSS almacenado en la interfaz web. No se requieren stubs de protobuf.
remote_writeUna prueba de concepto mínima y con pocas dependencias para CVE-2026-40179 (GHSA-vffh-x6r8-xx99): cross-site scripting almacenado en la interfaz web de Prometheus, entregado a través de un endpoint remote_write sin autenticación.
Prometheus 3.x renderiza los nombres de métricas y los valores de etiquetas en la interfaz web mediante innerHTML sin escaparlos. Debido a que Prometheus v3 relajó la validación de etiquetas para permitir UTF-8 arbitrario — incluidos <, > y " — un nombre de métrica puede transportar una carga HTML que se ejecuta cuando alguien navega por la interfaz.
| Afectado | Prometheus 3.0.0 – 3.5.1 |
| Corregido en | 3.5.2 |
| Impacto | XSS almacenado, se ejecuta en el navegador de cualquier usuario que visualice la métrica afectada |
| Superficies vulnerables | Tooltips de la página Graph, Metrics Explorer |
El XSS en sí necesita una vía para introducir una serie manipulada en el TSDB. Cualquiera de estas opciones sirve:
remote_write abierto (lo que usa este PoC),Un Prometheus expuesto a Internet o a la LAN sin autenticación por delante proporciona todo lo anterior sin esfuerzo adicional, que es lo que hace que esto sea explotable en la práctica y no meramente teórico.
Envía un único POST /api/v1/write que contiene una serie temporal cuyo __name__ es:
pentest_poc_cve_2026_40179
El WriteRequest de protobuf está codificado a mano en ~40 líneas, por lo que no hay dependencia de protobuf, prometheus_pb2 ni promtool — solo python-snappy para la compresión en el cable y requests para el POST. Eso facilita desplegarlo en un host de salto donde no se pueden compilar stubs protobuf generados.
python3 -m pip install python-snappy requests
python-snappy necesita la librería C de Snappy:
# Debian/Ubuntu
sudo apt install libsnappy-dev
# macOS
brew install snappy
python3 cve-2026-40179-poc.py <target_ip>
El script asume HTTPS en el puerto 9090 con la verificación de certificados deshabilitada (típico en una instancia interna con un certificado autofirmado). Si tu objetivo usa HTTP plano u otro puerto, edita la línea url en main().
Una inyección exitosa devuelve HTTP 204:
POST https://192.0.2.10:9090/api/v1/write -> 204
Consulta la serie en la interfaz o API de Prometheus — debería devolver resultados de inmediato:
{__name__=~"pentest_poc.*"}
La vista de tabla realiza el escape correctamente y muestra la carga como texto plano. Eso es lo esperado y no es la superficie vulnerable.
Abre el Metrics Explorer (el navegador de métricas junto al cuadro de consulta), busca pentest_poc y pasa el cursor sobre la entrada listada.
En pruebas de campo contra 3.2.1, la ruta del Metrics Explorer se disparó de forma fiable mientras que el tooltip de la pestaña Graph no se reprodujo — la muestra inyectada (valor 1.0) no se renderizó visiblemente en el gráfico trazado, y pasar el cursor sobre la leyenda tampoco la disparó. Ambas superficies se mencionan en el aviso; si una no se dispara en tu versión objetivo, prueba la otra antes de concluir que no es vulnerable.
La carga predeterminada usa console.log() para no interrumpir a quien no la esté buscando. Para una demo visible — una captura de pantalla para un informe, por ejemplo — cambia METRIC_NAME para usar alert():
METRIC_NAME = ('pentest_poc_cve_2026_40179'
'')
La serie inyectada es persistente. Una vez escrita, permanece en el TSDB hasta que la retención la expire. Eliminarla requiere la API de administración, que está deshabilitada por defecto:
# 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'
Si la API de administración está desactivada, no hay vía de limpieza remota. La única alternativa es promtool tsdb ejecutado localmente en el host, al que probablemente no tengas acceso.
Consecuencias prácticas:
job="pentest_poc"), de modo que sea fácil de encontrar y resulte obvio que es un artefacto de prueba.Para reproducirlo contra una instancia desechable en lugar de algo en producción:
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
Luego apunta el script a 127.0.0.1 (cambia el esquema de la URL a http:// en main()). Iniciar con --web.enable-admin-api significa que realmente podrás eliminar la serie después.
--web.config.file con basic_auth_users.--web.enable-remote-write-receiver a menos que lo necesites, y nunca lo expongas sin autenticación.Ten en cuenta que (1) por sí solo corrige el XSS, pero un Prometheus sin autenticación sigue siendo por sí mismo una superficie sustancial de divulgación de información: la lista completa de nombres de métricas, el inventario de objetivos de scrape y los nombres de host internos son legibles sin credenciales.
Esto se publica para defensores, investigadores y evaluadores que trabajan bajo autorización. La vulnerabilidad está divulgada públicamente y parcheada upstream.
Ejecutar esto contra un sistema escribe datos persistentes en él. No lo apuntes a infraestructura que no poseas o para la que no tengas permiso escrito explícito para realizar pruebas.
MIT — consulta LICENSE.