
Prometheus v3.15.0 - Alto - Remoto - SSRF - http_sd __unix_socket__ Dial non sanificato residuo dello scrape UDS
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-unix-socket-dial
Write-up: https://abraxaslabs.tech/research/prometheus-unix-socket-dial
Classe: SSRF Portata: Remota
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
La v3.15.0 ha introdotto lo scrape tramite Unix Domain Socket (#12024 / #18091). Il percorso è un'etichetta di target __unix_socket__, non un'allowlist dell'operatore. http_sd e file_sd copiano quell'etichetta così com'è. PopulateLabels non la valida mai. newUnixSocketScrapeClient chiama poi DialContext(ctx, "unix", socketPath) senza alcun filepath.Clean, senza allowlist e senza rifiutare ../ o i nomi astratti Linux @.
Lo scrape TCP non può farlo. CheckTargetAddress scarta ancora i valori __address__ che contengono /, quindi unix:///var/run/docker.sock come indirizzo viene rifiutato. La nuova etichetta è la falla.
La configurazione predefinita non imposta l'etichetta. Una richiesta HTTP non autenticata a Prometheus non può aggiungere job. Questo è vincolato all'SD.
| ID | nessuna CVE al momento |
| Classe | SSRF (Dial unix non sanificato dalle etichette SD) |
| Portata | Remota (vincolata all'SD: chi scrive 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 |
| Prodotto | Prometheus |
| Affetto | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd), immagine prom/prometheus:v3.15.0 |
| Auth | Vincolata all'SD: chi scrive le etichette http_sd / file_sd (o un relabel che copia meta non attendibili in __unix_socket__) |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Il controllo del JSON di http_sd (o di un file_sd scrivibile) è sufficiente. L'URL dell'SD è configurazione dell'operatore. Ciò che resta è che le labels del gruppo possono impostare __unix_socket__ e Prometheus eseguirà il Dial su di essa.
| Privilegio | Metodo / sink | Provato in laboratorio o dedotto |
|---|---|---|
| Connettersi a un socket unix dell'host che l'UID di Prometheus può aprire | clientForTarget -> DialContext(ctx, "unix", socketPath) da __unix_socket__ | Provato in laboratorio. Percorso /sockets/jail/../oracle.sock da http_sd; salute dello scrape up |
Seguire ../ in quel percorso | nessun filepath.Clean / nessun rifiuto di .. | Provato in laboratorio. Il kernel ha risolto il traversal in /sockets/oracle.sock |
| Effettuare il dial di un socket astratto Linux | stesso Dial di @name | Provato in laboratorio. __unix_socket__=@prom-lab-oracle; salute dello scrape up |
| HTTP GET su quel socket con l'UID di Prometheus | lo scrape è una GET; __metrics_path__ è il percorso della richiesta | Provato in laboratorio. GET /fs-traversal e /abstract; User-Agent Prometheus/; lab_witness acquisito |
| Raggiungere socket che lo scrape TCP non può nominare | CheckTargetAddress rifiuta / in __address__ | Provato in laboratorio. unix:///var/run/docker.sock come __address__ ha registrato is not a valid hostname; non è un target attivo; prometheus_target_sync_failed_total=1 |
| GET alle API HTTP di Docker Engine / kubelet se quei socket sono nel perimetro | __unix_socket__=/var/run/docker.sock più un percorso dell'API Docker | Dedotto. Stesso Dial non sanificato. Questo laboratorio non monta un socket reale del motore. Le API JSON di solito falliscono il parsing dell'esposizione, quindi la gravità High riguarda la connessione/GET, non l'esfiltrazione via TSDB del JSON di Docker |
| Modificare Docker (creare container, scrivere) | verbi dello scrape | Escluso. Lo scrape è solo GET |
| Aggiungere job di scrape tramite HTTP Prometheus non autenticato | /-/reload, API admin | Escluso. Entrambi i flag sono disattivati per impostazione predefinita; il reload non ha body |
| Usare questo sulla configurazione di esempio predefinita | __unix_socket__ non impostato; l'esempio esegue lo scrape di localhost:9090 via TCP | Escluso. Le SD k8s/consul/docker in-tree emettono solo __meta_*; quei prefissi vengono eliminati dopo il relabel |
Il modello di sicurezza del vendor consente già alla SD di scegliere i target di scrape TCP. La nuova primitiva è l'IPC dell'host che __address__ non poteva nominare.
Ho fissato prometheus/prometheus v3.15.0 dopo la riga FEATURE della 3.15 per lo scrape UDS. L'OSS predefinito su questo pin ha l'API admin, il lifecycle HTTP, il ricevitore remote-write e il ricevitore OTLP disattivati. PopulateDiscoveredLabels copia ogni etichetta del gruppo http_sd. __unix_socket__ non è __meta_*, quindi sopravvive alla cancellazione dei meta. PopulateLabels controlla solo __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 rifiuta ancora / nell'address. URL() mantiene scheme/host/path da __scheme__ / __address__ / __metrics_path__; il socket è solo dial.
Vicoli ciechi già registrati: valutare questo come NONE nel modello perché SD sceglie già target TCP (la nuova primitiva è l'IPC host); trattare unix:///var/run/docker.sock come __address__ come la falla (viene rifiutato); affermare che la configurazione di esempio predefinita sia questo residuo. Teatro: una reverse shell. L'oracolo è la salute dello scrape up sui socket che il JSON SD ha nominato.
python3 prometheus-unix-socket-dial-Abraxas-Labs.py
o
cd lab
./run.sh
prom/prometheus:v3.15.0 condivide un netns con un sidecar Python. Il sidecar serve http_sd su 127.0.0.1:8080/targets.json e due oracle HTTP unix: filesystem /sockets/oracle.sock e astratto @prom-lab-oracle. Prometheus è pubblicato su 127.0.0.1:19390. L'API di amministrazione e il ciclo di vita restano disattivati.
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 e /abstract, entrambi con health up. Lo slash TCP non è mai diventato un target attivo.