
Prometheus v3.15.0 - Alta - Remota - SSRF - http_sd __unix_socket__ Dial sin sanear remanente de scrape UDS
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-unix-socket-dial
Análisis: https://abraxaslabs.tech/research/prometheus-unix-socket-dial
Clase: SSRF Alcance: Remoto
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
v3.15.0 añadió el scrape de Unix Domain Socket (#12024 / #18091). La ruta es una etiqueta de destino __unix_socket__, no una lista de permitidos del operador. http_sd y file_sd copian esa etiqueta tal cual. PopulateLabels nunca la valida. Después, newUnixSocketScrapeClient llama a DialContext(ctx, "unix", socketPath) sin filepath.Clean, sin lista de permitidos y sin rechazar ../ ni los nombres abstractos de Linux @.
El scrape TCP no puede hacer esto. CheckTargetAddress sigue descartando los valores de __address__ que contienen /, por lo que unix:///var/run/docker.sock como dirección es rechazado. La nueva etiqueta es el agujero.
La configuración por defecto no establece la etiqueta. El HTTP sin autenticación a Prometheus no puede añadir jobs. Esto está condicionado por SD.
| ID | aún sin CVE |
| Clase | SSRF (Dial unix sin sanear desde etiquetas SD) |
| Alcance | Remoto (condicionado por SD: escritor de http_sd / file_sd) |
| CWE | CWE-918, CWE-22 |
| CVSS | Alto: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| Producto | Prometheus |
| Afectado | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd), imagen prom/prometheus:v3.15.0 |
| Autenticación | Condicionado por SD: escritor de etiquetas http_sd / file_sd (o un relabel que copie metadatos no confiables en __unix_socket__) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Basta con controlar el JSON de http_sd (o un file_sd escribible). La URL de SD es configuración del operador. Lo que queda es que las labels del grupo pueden establecer __unix_socket__ y Prometheus hará Dial sobre ella.
| Privilegio | Método / sink | Demostrado en laboratorio o inferido |
|---|---|---|
| Conectarse a un socket unix de host que el UID de Prometheus puede abrir | clientForTarget -> DialContext(ctx, "unix", socketPath) desde __unix_socket__ | Demostrado en laboratorio. Ruta /sockets/jail/../oracle.sock desde http_sd; salud del scrape up |
Seguir ../ en esa ruta | sin filepath.Clean / sin rechazo de .. | Demostrado en laboratorio. El kernel resolvió el recorrido a /sockets/oracle.sock |
| Marcar un socket abstracto de Linux | mismo Dial de @name | Demostrado en laboratorio. __unix_socket__=@prom-lab-oracle; salud del scrape up |
| HTTP GET en ese socket como el UID de Prometheus | el scrape es GET; __metrics_path__ es la ruta de la petición | Demostrado en laboratorio. GET /fs-traversal y /abstract; User-Agent Prometheus/; lab_witness ingerido |
| Alcanzar sockets que el scrape TCP no puede nombrar | CheckTargetAddress rechaza / en __address__ | Demostrado en laboratorio. unix:///var/run/docker.sock como __address__ registró is not a valid hostname; no es un target activo; prometheus_target_sync_failed_total=1 |
| GET a las APIs HTTP de Docker Engine / kubelet si esos sockets están en alcance | __unix_socket__=/var/run/docker.sock más una ruta de la API de Docker | Inferido. Mismo Dial sin sanear. Este laboratorio no monta un socket real del engine. Las APIs JSON normalmente fallan el parseo de exposición, así que el riesgo Alto es la conexión/GET, no la exfiltración de JSON de Docker vía TSDB |
| Mutar Docker (crear contenedor, escribir) | verbos del scrape | Cerrado. El scrape es solo GET |
| Añadir trabajos de scrape sobre HTTP de Prometheus sin autenticación | /-/reload, API de administración | Cerrado. Ambas flags están desactivadas por defecto; reload no tiene cuerpo |
| Usar esto en la configuración de ejemplo por defecto | __unix_socket__ sin definir; el ejemplo hace scrape de localhost:9090 sobre TCP | Cerrado. Los SD k8s/consul/docker del árbol solo emiten __meta_*; esos prefijos se eliminan tras el relabel |
El modelo de seguridad del proveedor ya permite que el SD elija targets de scrape TCP. La nueva primitiva es IPC de host que __address__ no podía nombrar.
Fijé prometheus/prometheus v3.15.0 tras la línea FEATURE de 3.15 para scrape UDS. El OSS por defecto en este pin tiene desactivadas la API de administración, el ciclo de vida HTTP, el receptor remote-write y el receptor OTLP. PopulateDiscoveredLabels copia cada etiqueta de grupo de http_sd. __unix_socket__ no es __meta_*, así que sobrevive al borrado de meta. PopulateLabels solo comprueba __address__.
func (sp *scrapePool) clientForTarget(t *Target) *http.Client {
socketPath := t.labels.Get(UnixSocketLabel)
if socketPath == "" {
return sp.client
}
client, err := newUnixSocketScrapeClient(socketPath, ...)
func newUnixSocketScrapeClient(socketPath string, ...) (*http.Client, error) {
optFuncs = append([]config_util.HTTPClientOption{config_util.WithDialContextFunc(
func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", socketPath)
},
)}, optFuncs...)
CheckTargetAddress sigue rechazando / en la address. URL() conserva scheme/host/path de __scheme__ / __address__ / __metrics_path__; el socket es solo para dial.
Errores ya registrados: puntuar esto como NONE en el modelo porque SD ya elige objetivos TCP (la nueva primitiva es IPC de host); tratar unix:///var/run/docker.sock como __address__ como el agujero (se rechaza); afirmar que la configuración de ejemplo por defecto es este remanente. Teatro: una reverse shell. El oráculo es la salud de scrapeo up en los sockets que el JSON de SD nombró.
python3 prometheus-unix-socket-dial-Abraxas-Labs.py
o
cd lab
./run.sh
prom/prometheus:v3.15.0 comparte un netns con un sidecar de Python. El sidecar sirve http_sd en 127.0.0.1:8080/targets.json y dos oráculos HTTP unix: el de sistema de archivos /sockets/oracle.sock y el abstracto @prom-lab-oracle. Prometheus se publica en 127.0.0.1:19390. La API de administración y el ciclo de vida permanecen desactivados.
SUCCESS prometheus-unix-socket-dial sd=http_sd fs-traversal=yes abstract=yes tcp-slash-rejected=yes how=sd-labels PROMETHEUS-UNIX-SOCKET-DIAL-WITNESS
IOC: scrapeUrl=http://localhost:1/fs-traversal y /abstract, ambos con estado up. La barra TCP nunca llegó a ser un objetivo activo.