1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
Author: Julien Pivotto <roidelapluie@o11y.eu>
Date: Tue Nov 29 10:22:49 2022 +0100
Forwarded: not-needed
Last-Updated: Mon, 19 Dec 2022 20:11:12 +0000
Description:
Backport of upstream commits 2528877 and 0af5c3f:
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>
* Fix tests
Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>
--- a/web/users.go
+++ b/web/users.go
@@ -18,6 +18,7 @@
import (
"encoding/hex"
"net/http"
+ "strings"
"sync"
"github.com/go-kit/kit/log"
@@ -74,7 +75,12 @@
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 {
@@ -83,7 +89,7 @@
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(pass))
u.bcryptMtx.Unlock()
- authOk = err == nil
+ authOk = validUser && err == nil
u.cache.set(cacheKey, authOk)
}
--- a/web/users_test.go
+++ b/web/users_test.go
@@ -131,3 +131,47 @@
// Login with the response cached.
login()
}
+
+// TestByPassBasicAuthVuln tests for CVE-2022-46146.
+func TestByPassBasicAuthVuln(t *testing.T) {
+ server := &http.Server{
+ Addr: port,
+ 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() {
+ ListenAndServe(server, "testdata/web_config_users_noTLS.good.yml", 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")
+}
--- /dev/null
+++ b/web/testdata/web_config_users_noTLS.good.yml
@@ -0,0 +1,5 @@
+basic_auth_users:
+ alice: $2y$12$1DpfPeqF9HzHJt.EWswy1exHluGfbhnn3yXhR7Xes6m3WJqFg0Wby
+ bob: $2y$18$4VeFDzXIoPHKnKTU3O3GH.N.vZu06CVqczYZ8WvfzrddFU6tGqjR.
+ carol: $2y$10$qRTBuFoULoYNA7AQ/F3ck.trZBPyjV64.oA4ZsSBCIWvXuvQlQTuu
+ dave: $2y$10$2UXri9cIDdgeKjBo4Rlpx.U3ZLDV8X1IxKmsfOvhcM5oXQt/mLmXq
|