VYPR
Moderate severityNVD Advisory· Published Nov 29, 2022· Updated Aug 3, 2024

Prometheus Exporter Toolkit vulnerable to basic authentication bypass

CVE-2022-46146

Description

Prometheus Exporter Toolkit is a utility package to build exporters. Prior to versions 0.7.2 and 0.8.2, if someone has access to a Prometheus web.yml file and users' bcrypted passwords, they can bypass security by poisoning the built-in authentication cache. Versions 0.7.2 and 0.8.2 contain a fix for the issue. There is no workaround, but attacker must have access to the hashed password to use this functionality.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2022-46146 allows attackers who possess the bcrypt password hash to poison the authentication cache in Prometheus Exporter Toolkit, bypassing basic authentication.

CVE-2022-46146 is a vulnerability in the Prometheus Exporter Toolkit, a Go library that provides basic authentication for Prometheus exporters. To reduce the performance overhead of bcrypt password verification, the toolkit implements a cache keyed by a predictable string: hex(username + hashed password + input password) [1][4]. If an attacker obtains the bcrypt password hash from the web.yml file, they can craft requests that populate the cache with entries for arbitrary passwords, effectively bypassing authentication [4].

Exploitation requires prior access to the bcrypt hash of a valid user's password, which might be obtained through file disclosure or other means. Once the attacker has the hash, they can send HTTP requests with crafted credentials to poison the cache [2]. The cache caches both successful and failed attempts, so by pre-computing a cache entry for a known password guess, the attacker causes subsequent authentication to succeed without needing to compute the bcrypt match [4][3].

Successful exploitation allows an attacker to bypass basic authentication entirely and gain unauthorized access to the exporter's HTTP endpoints [1]. Depending on the exporter's functionality, this could lead to disclosure of Prometheus metrics or other sensitive data.

Mitigation

Versions 0.7.2 and 0.8.2 of the Exporter Toolkit fix the issue by changing the cache key to include a random value, making poisoning infeasible [1]. No workarounds exist; users must upgrade to the patched versions [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/prometheus/exporter-toolkitGo
< 0.7.20.7.2
github.com/prometheus/exporter-toolkitGo
>= 0.8.0, < 0.8.20.8.2

Affected products

203

Patches

2
5b1eab34484d

Merge pull request from GHSA-7rg2-cxvp-9p7p

https://github.com/prometheus/exporter-toolkitJulien PivottoNov 29, 2022via ghsa
2 files changed · +56 2
  • web/handler.go+8 2 modified
    @@ -19,6 +19,7 @@ import (
     	"encoding/hex"
     	"fmt"
     	"net/http"
    +	"strings"
     	"sync"
     
     	"github.com/go-kit/log"
    @@ -113,7 +114,12 @@ func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     			hashedPassword = "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSi"
     		}
     
    -		cacheKey := hex.EncodeToString(append(append([]byte(user), []byte(hashedPassword)...), []byte(pass)...))
    +		cacheKey := strings.Join(
    +			[]string{
    +				hex.EncodeToString([]byte(user)),
    +				hex.EncodeToString([]byte(hashedPassword)),
    +				hex.EncodeToString([]byte(pass)),
    +			}, ":")
     		authOk, ok := u.cache.get(cacheKey)
     
     		if !ok {
    @@ -122,7 +128,7 @@ func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     			err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass))
     			u.bcryptMtx.Unlock()
     
    -			authOk = err == nil
    +			authOk = validUser && err == nil
     			u.cache.set(cacheKey, authOk)
     		}
     
    
  • web/handler_test.go+48 0 modified
    @@ -137,6 +137,54 @@ func TestBasicAuthWithFakepassword(t *testing.T) {
     	login()
     }
     
    +// TestByPassBasicAuthVuln tests for CVE-2022-46146.
    +func TestByPassBasicAuthVuln(t *testing.T) {
    +	server := &http.Server{
    +		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +			w.Write([]byte("Hello World!"))
    +		}),
    +	}
    +
    +	done := make(chan struct{})
    +	t.Cleanup(func() {
    +		if err := server.Shutdown(context.Background()); err != nil {
    +			t.Fatal(err)
    +		}
    +		<-done
    +	})
    +
    +	go func() {
    +		flags := FlagConfig{
    +			WebListenAddresses: &([]string{port}),
    +			WebSystemdSocket:   OfBool(false),
    +			WebConfigFile:      OfString("testdata/web_config_users_noTLS.good.yml"),
    +		}
    +		ListenAndServe(server, &flags, testlogger)
    +		close(done)
    +	}()
    +
    +	login := func(username, password string) {
    +		client := &http.Client{}
    +		req, err := http.NewRequest("GET", "http://localhost"+port, nil)
    +		if err != nil {
    +			t.Fatal(err)
    +		}
    +		req.SetBasicAuth(username, password)
    +		r, err := client.Do(req)
    +		if err != nil {
    +			t.Fatal(err)
    +		}
    +		if r.StatusCode != 401 {
    +			t.Fatalf("bad return code, expected %d, got %d", 401, r.StatusCode)
    +		}
    +	}
    +
    +	// Poison the cache.
    +	login("alice$2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby", "fakepassword")
    +	// Login with a wrong password.
    +	login("alice", "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSifakepassword")
    +}
    +
     // TestHTTPHeaders validates that HTTP headers are added correctly.
     func TestHTTPHeaders(t *testing.T) {
     	server := &http.Server{
    
25288779bc59

Merge pull request from GHSA-7rg2-cxvp-9p7p

https://github.com/prometheus/exporter-toolkitJulien PivottoNov 29, 2022via ghsa
2 files changed · +56 2
  • web/handler.go+8 2 modified
    @@ -19,6 +19,7 @@ import (
     	"encoding/hex"
     	"fmt"
     	"net/http"
    +	"strings"
     	"sync"
     
     	"github.com/go-kit/log"
    @@ -113,7 +114,12 @@ func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     			hashedPassword = "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSi"
     		}
     
    -		cacheKey := hex.EncodeToString(append(append([]byte(user), []byte(hashedPassword)...), []byte(pass)...))
    +		cacheKey := strings.Join(
    +			[]string{
    +				hex.EncodeToString([]byte(user)),
    +				hex.EncodeToString([]byte(hashedPassword)),
    +				hex.EncodeToString([]byte(pass)),
    +			}, ":")
     		authOk, ok := u.cache.get(cacheKey)
     
     		if !ok {
    @@ -122,7 +128,7 @@ func (u *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     			err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass))
     			u.bcryptMtx.Unlock()
     
    -			authOk = err == nil
    +			authOk = validUser && err == nil
     			u.cache.set(cacheKey, authOk)
     		}
     
    
  • web/handler_test.go+48 0 modified
    @@ -129,6 +129,54 @@ func TestBasicAuthWithFakepassword(t *testing.T) {
     	login()
     }
     
    +// TestByPassBasicAuthVuln tests for CVE-2022-46146.
    +func TestByPassBasicAuthVuln(t *testing.T) {
    +	server := &http.Server{
    +		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +			w.Write([]byte("Hello World!"))
    +		}),
    +	}
    +
    +	done := make(chan struct{})
    +	t.Cleanup(func() {
    +		if err := server.Shutdown(context.Background()); err != nil {
    +			t.Fatal(err)
    +		}
    +		<-done
    +	})
    +
    +	go func() {
    +		flags := FlagConfig{
    +			WebListenAddresses: &([]string{port}),
    +			WebSystemdSocket:   OfBool(false),
    +			WebConfigFile:      OfString("testdata/web_config_users_noTLS.good.yml"),
    +		}
    +		ListenAndServe(server, &flags, testlogger)
    +		close(done)
    +	}()
    +
    +	login := func(username, password string) {
    +		client := &http.Client{}
    +		req, err := http.NewRequest("GET", "http://localhost"+port, nil)
    +		if err != nil {
    +			t.Fatal(err)
    +		}
    +		req.SetBasicAuth(username, password)
    +		r, err := client.Do(req)
    +		if err != nil {
    +			t.Fatal(err)
    +		}
    +		if r.StatusCode != 401 {
    +			t.Fatalf("bad return code, expected %d, got %d", 401, r.StatusCode)
    +		}
    +	}
    +
    +	// Poison the cache.
    +	login("alice$2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby", "fakepassword")
    +	// Login with a wrong password.
    +	login("alice", "$2y$10$QOauhQNbBCuQDKes6eFzPeMqBSjb7Mr5DUmpZ/VcEd00UAV/LDeSifakepassword")
    +}
    +
     // TestHTTPHeaders validates that HTTP headers are added correctly.
     func TestHTTPHeaders(t *testing.T) {
     	server := &http.Server{
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

18

News mentions

0

No linked articles in our index yet.