
Prometheus v3.15.0 - High - Remote - Credential leak - remote_write google_iam/azuread forwards Bearer on cross-host 3xx
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · prometheus-bearer-redirect
Class: Credential leak Reach: Remote
Prometheus v3.15.0 (5241a27) - CNCF / Prometheus
prometheus/common v0.71.0 already strips scrape / HTTPClientConfig credentials on cross-host redirects (isCrossHostRedirect, #901). NewWriteClient builds that client first, then wraps azuread.NewAzureADRoundTripper or googleiam.NewRoundTripper outside the strip. follow_redirects still defaults true.
If the configured remote_write URL 301/302/307s to another host, Prometheus sends Authorization: Bearer plus the Azure AD or GCP monitoring.write token to that host. Go already dropped the copied header. These RoundTrippers set it again.
Not CVE-2026-42151 (client_secret dump on /-/config). Default prometheus.yml has no remote_write. This is gated on azuread: or google_iam:.
| ID | no CVE yet |
| Class | Credential leak (remote_write Bearer on cross-host 3xx) |
| Reach | Remote (outbound from Prometheus to a 3xx target) |
| CWE | CWE-201 |
| CVSS | High: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| Product | Prometheus |
| Affected | v3.15.0 (5241a27fe3c6983549fccc32f6e65917408c63cd), image prom/prometheus:v3.15.0 |
| Auth | config-gated remote_write with azuread: or google_iam: |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Operator has remote_write with azuread: or google_iam:. The RW URL, or a hop it 3xxs to, is a host the attacker controls.
| Privilege | Method / sink | Lab-proven or inferred |
|---|---|---|
Read the live GCP monitoring.write Bearer on a cross-host 3xx | google_iam remote_write POST, 307 to another host | Lab-proven. Oracle POST /write got Authorization: Bearer lab-gcp-PROMETHEUS-BEARER-REDIRECT-WITNESS |
Same leak for azuread | azureADRoundTripper.RoundTrip always Header.Set("Authorization", "Bearer "+token) with no isCrossHostRedirect | Inferred from the pin source. Same wrap site in NewWriteClient. Lab used google_iam so it did not call Azure |
| Replay that token against Azure Monitor / GCP Monitoring as that identity | Token is a real cloud credential when those auth blocks are configured | Inferred. Lab minted a stub token; it did not call Azure or GCP |
| Scrape / oauth2 / basic / bearer on the same pin | HTTPClientConfig RoundTrippers from common v0.71.0 | Lab-proven closed. oauth2 scrape GET followed 302 to the same oracle with no Authorization |
| Default OSS | remote_write empty | Closed |
remote_write.headers setting Authorization | rejected | Closed |
/-/config dumping client_secret | CVE-2026-42151; field is typed Secret on this pin | Closed |
| Prometheus admin / lifecycle | flags default off | Closed |
| RCE / a shell | not this leftover | Closed |
I pinned prometheus/prometheus tag v3.15.0 after the 3.11/3.12 secret-redaction and snappy-cap wave. Nineteen hunts. Default OSS: admin API, lifecycle HTTP, remote-write receiver, and OTLP receiver off. Common #901 is in v0.71.0. The leftover is the two remote_write wrappers that sit outside that client.
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 is google.golang.org/api/transport/http -> oauth2.Transport, which SetAuthHeaders on every hop.
Wrong turns already recorded: re-filing CVE-2026-42151; treating scrape oauth2 as still leaky (it is not); calling this a default-install High (it is not - remote_write is empty until you turn it on). Theatre: a reverse shell. The oracle is a Bearer on the redirected POST.
python3 prometheus-bearer-redirect-Abraxas-Labs.py
or
cd lab
./run.sh
Loopback only: Prometheus http://127.0.0.1:19380, oracle http://127.0.0.1:19381. Image prom/prometheus:v3.15.0. run.sh writes a lab gcp-sa.json with token_uri: http://hop:8080/token so google_iam never talks to 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
Skip attaching Azure AD / Google IAM Bearer when isCrossHostRedirect(req), same as common #901. Do not re-set Authorization after Go dropped it on a cross-host 3xx.
5241a27fe3c6983549fccc32f6e65917408c63cd)storage/remote/client.go · storage/remote/azuread/azuread.go · storage/remote/googleiam/googleiam.goGNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.