CVE-2022-46146 (published 2022-11-29T14:15Z)

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.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         
cwe_id: CWE-303
cwe_name: Incorrect Implementation of Authentication Algorithm
description: The requirements for the product dictate the use of an established authentication algorithm, but the implementation of the algorithm is incorrect.
extended_description: This incorrect implementation may allow authentication to be bypassed.
         

Fix information:
repo_url: https://github.com/prometheus/exporter-toolkit
commit_details: repo_url: https://github.com/prometheus/exporter-toolkit
author: Julien Pivotto
author_date: 2022-11-29 10:22:49+01:00
msg: Merge pull request from GHSA-7rg2-cxvp-9p7p

* Fix authentication bypass if stored password hash is known

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>

* Add test for CVE-2022-46146

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>
repository: repo_url: https://github.com/prometheus/exporter-toolkit
repo_name: prometheus/exporter-toolkit
description: Utility package to build exporters
repo_language: Go
owner: prometheus
file_changes: filename: handler.go
change_type: ModificationType.MODIFY
diff: @@ -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)
 		}
 

programming_language: Go
filename: handler_test.go
change_type: ModificationType.MODIFY
diff: @@ -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{

programming_language: Go