
Prometheus v3.15.0 - Alta - Remoto - Vazamento de credenciais - remote_write google_iam/azuread encaminha Bearer em 3xx entre hosts
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-bearer-redirect
Classe: Vazamento de credenciais Alcance: Remoto
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
prometheus/common v0.71.0 já remove credenciais de scrape / HTTPClientConfig em redirecionamentos entre hosts (isCrossHostRedirect, #901). NewWriteClient constrói esse cliente primeiro e depois envolve azuread.NewAzureADRoundTripper ou googleiam.NewRoundTripper fora da remoção. follow_redirects ainda tem como padrão true.
Se a URL de remote_write configurada responder 301/302/307 para outro host, o Prometheus envia Authorization: Bearer mais o token monitoring.write do Azure AD ou GCP para esse host. O Go já havia descartado o cabeçalho copiado. Esses RoundTrippers o definem novamente.
Não é o CVE-2026-42151 (dump de client_secret em /-/config). O prometheus.yml padrão não tem remote_write. Isso é condicionado a azuread: ou google_iam:.
| ID | ainda sem CVE |
| Classe | Vazamento de credenciais (Bearer de remote_write em 3xx entre hosts) |
| Alcance | Remoto (saída do Prometheus para um alvo 3xx) |
| CWE | CWE-201 |
| CVSS | Alto: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| Produto | Prometheus |
| Afetado | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd), imagem prom/prometheus:v3.15.0 |
| Autenticação | remote_write condicionado por configuração com azuread: ou google_iam: |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
O operador tem remote_write com azuread: ou google_iam:. A URL de RW, ou um salto para o qual ela responde 3xx, é um host controlado pelo atacante.
| Privilégio | Método / destino | Comprovado em lab ou inferido |
|---|---|---|
Ler o Bearer monitoring.write do GCP em um 3xx entre hosts | POST de remote_write google_iam, 307 para outro host | Comprovado em lab. O oráculo POST /write recebeu Authorization: Bearer lab-gcp-PROMETHEUS-BEARER-REDIRECT-WITNESS |
Mesmo vazamento para azuread | azureADRoundTripper.RoundTrip sempre faz Header.Set("Authorization", "Bearer "+token) sem isCrossHostRedirect | Inferido a partir do código fixado. Mesmo local de envolvimento em NewWriteClient. O lab usou google_iam, então não chamou o Azure |
| Reutilizar esse token contra o Azure Monitor / GCP Monitoring como aquela identidade | O token é uma credencial de nuvem real quando esses blocos de autenticação estão configurados | Inferido. O lab gerou um token stub; não chamou o Azure nem o GCP |
| Scrape / oauth2 / basic / bearer no mesmo pin | RoundTrippers de HTTPClientConfig do common v0.71.0 | Comprovado em lab como fechado. GET de scrape oauth2 seguiu 302 para o mesmo oráculo sem Authorization |
| OSS padrão | remote_write vazio | Fechado |
remote_write.headers definindo Authorization | rejeitado | Fechado |
/-/config fazendo dump de client_secret | CVE-2026-42151; o campo é tipado como Secret neste pin | Fechado |
| Admin / ciclo de vida do Prometheus | flags desativadas por padrão | Fechado |
| RCE / um shell | não é este resquício | Fechado |
Fixe o pin na tag v3.15.0 do prometheus/prometheus após a onda de redação de segredos e limite de snappy das versões 3.11/3.12. Dezenove buscas. OSS padrão: API de admin, ciclo de vida HTTP, receptor de remote-write e receptor OTLP desativados. O common #901 está na v0.71.0. O resquício são os dois wrappers de remote_write que ficam fora desse cliente.
func (rt *azureADRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
accessToken, err := rt.tokenProvider.getAccessToken(req.Context())
// ...
req.Header.Set("Authorization", bearerAccessToken)
return rt.next.RoundTrip(req)
}
googleiam.NewRoundTripper é google.golang.org/api/transport/http -> oauth2.Transport, que faz SetAuthHeader em cada salto.
Caminhos errados já registrados: reabrir o CVE-2026-42151; tratar o oauth2 de scrape como ainda vazando (não vaza); chamar isso de Alto em instalação padrão (não é - remote_write fica vazio até você ativá-lo). Teatro: um reverse shell. O oráculo é um Bearer no POST redirecionado.
python3 prometheus-bearer-redirect-Abraxas-Labs.py
ou
cd lab
./run.sh
Apenas loopback: Prometheus http://127.0.0.1:19380, oráculo http://127.0.0.1:19381. Imagem prom/prometheus:v3.15.0. O run.sh escreve um gcp-sa.json de lab com token_uri: http://hop:8080/token para que o google_iam nunca fale com o Google.
SUCCESS prometheus-bearer-redirect how=live rw-method=POST rw-bearer=yes token-has-witness=yes scrape-method=GET scrape-bearer=no PROMETHEUS-BEARER-REDIRECT-WITNESS
Pular a anexação do Bearer do Azure AD / Google IAM quando isCrossHostRedirect(req), assim como no common #901. Não redefinir Authorization depois que o Go o descartou em um 3xx entre hosts.
5241a27fe3c6983549fccc32f6e65917408c63cd)storage/remote/client.go · storage/remote/azuread/azuread.go · storage/remote/googleiam/googleiam.goGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantias.