
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
Classe: SSRF Portata: Remota
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
La v3.15.0 ha introdotto lo scrape su Unix Domain Socket (#12024 / #18091). Il percorso è una label del target __unix_socket__, non una allowlist dell'operatore. http_sd e file_sd copiano quella label così com'è. PopulateLabels non la valida mai. newUnixSocketScrapeClient chiama poi DialContext(ctx, "unix", socketPath) senza 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 address viene rifiutato. La nuova label è il buco.
La configurazione stock non imposta la label. HTTP non autenticato verso Prometheus non può aggiungere job. Questo è SD-gated.
| ID | nessuna CVE al momento |
| Classe | SSRF (Dial unix non sanificato dalle label SD) |
| Portata | Remota (SD-gated: chi scrive http_sd / file_sd) |
| CWE | CWE-918, CWE-22 |
| CVSS | High: 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 | SD-gated: chi scrive le label http_sd / file_sd (o un relabel che copia meta non fidati 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 SD è configurazione dell'operatore. Ciò che resta è che le labels del gruppo possono impostare __unix_socket__ e Prometheus farà il Dial.
| Privilegio | Metodo / sink | Provato in lab o dedotto |
|---|---|---|
| Connettersi a un unix socket dell'host che l'UID di Prometheus può aprire | clientForTarget -> DialContext(ctx, "unix", socketPath) da __unix_socket__ | Provato in lab. Percorso /sockets/jail/../oracle.sock da http_sd; salute dello scrape up |
Seguire ../ in quel percorso | nessun filepath.Clean / nessun rifiuto di .. | Provato in lab. Il kernel ha risolto il traversal in /sockets/oracle.sock |
| Fare il Dial di un socket astratto Linux | stesso Dial di @name | Provato in lab. __unix_socket__=@prom-lab-oracle; salute dello scrape up |
| HTTP GET su quel socket come UID di Prometheus | lo scrape è GET; __metrics_path__ è il percorso della richiesta | Provato in lab. 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 lab. unix:///var/run/docker.sock come __address__ ha registrato is not a valid hostname; non è un target attivo; prometheus_target_sync_failed_total=1 |
| Fare GET sulle API HTTP di Docker Engine / kubelet se quei socket sono in scope | __unix_socket__=/var/run/docker.sock più un percorso dell'API Docker | Dedotto. Stesso Dial non sanificato. Questo lab non monta un vero socket del motore. Le API JSON di solito falliscono il parsing dell'exposition, quindi l'High è la connect/GET, non l'exfil del JSON di Docker dal TSDB |
| Modificare Docker (creare container, scrivere) | verbi dello scrape | Chiuso. Lo scrape è solo GET |
| Aggiungere job di scrape tramite HTTP Prometheus non autenticato | /-/reload, admin API | Chiuso. Entrambi i flag sono disattivati di default; reload non ha body |
| Usarlo sulla configurazione di esempio predefinita | __unix_socket__ non impostato; l'esempio fa scrape di localhost:9090 via TCP | Chiuso. 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à alle SD di scegliere i target di scrape TCP. La nuova primitiva è IPC sull'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 admin API, lifecycle HTTP, remote-write receiver e OTLP receiver disattivati. PopulateDiscoveredLabels copia ogni label 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 per il dial.
Vicoli ciechi già registrati: valutarlo come NONE in-modello perché le SD scelgono già i target TCP (la nuova primitiva è IPC sull'host); trattare unix:///var/run/docker.sock come __address__ come il buco (viene rifiutato); sostenere 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
oppure
cd lab
./run.sh
prom/prometheus:v3.15.0 condivide una netns con un sidecar Python. Il sidecar serve http_sd su 127.0.0.1:8080/targets.json e due oracoli HTTP unix: filesystem /sockets/oracle.sock e astratto @prom-lab-oracle. Prometheus è pubblicato su 127.0.0.1:19390. Admin API e lifecycle 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 salute up. Lo slash TCP non è mai diventato un target attivo.
Non fare il Dial di __unix_socket__ dalle SD finché non è un percorso filesystem assoluto e sanificato. Rifiutare valori vuoti, NUL, nomi astratti Linux @, percorsi relativi e qualsiasi .. dopo filepath.Clean. Una allowlist di prefissi dallo YAML di scrape è più forte. Eliminare la label da PopulateDiscoveredLabels a meno che non provenga dalla configurazione dell'operatore chiude anch'esso l'iniezione SD.