CVE-2023-22463 (published 2023-01-04T16:15Z)

KubePi is a k8s panel. The jwt authentication function of KubePi through version 1.6.2 uses hard-coded Jwtsigkeys, resulting in the same Jwtsigkeys for all online projects. This means that an attacker can forge any jwt token to take over the administrator account of any online project. Furthermore, they may use the administrator to take over the k8s cluster of the target enterprise. `session.go`, the use of hard-coded JwtSigKey, allows an attacker to use this value to forge jwt tokens arbitrarily. The JwtSigKey is confidential and should not be hard-coded in the code. The vulnerability has been fixed in 1.6.3. In the patch, JWT key is specified in app.yml. If the user leaves it blank, a random key will be used. There are no workarounds aside from upgrading.

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

Weakness information:
cwe_id: CWE-798
cwe_name: Use of Hard-coded Credentials
description: The product contains hard-coded credentials, such as a password or cryptographic key.
extended_description: 
		   There are two main variations:
		   
			 Inbound: the product contains an authentication mechanism that checks the input credentials against a hard-coded set of credentials. In this variant, a default administration account is created, and a simple password is hard-coded into the product and associated with that account. This hard-coded password is the same for each installation of the product, and it usually cannot be changed or disabled by system administrators without manually modifying the program, or otherwise patching the product. It can also be difficult for the administrator to detect.
			 Outbound: the product connects to another system or component, and it contains hard-coded credentials for connecting to that component. This variant applies to front-end systems that authenticate with a back-end service. The back-end service may require a fixed password that can be easily discovered. The programmer may simply hard-code those back-end credentials into the front-end product.
		   
         
         

Fix information:
repo_url: https://github.com/KubeOperator/KubePi
commit_details: repo_url: https://github.com/KubeOperator/KubePi
author: zhengkunwang223
author_date: 2023-01-04 17:28:22+08:00
msg: fix: 解决 jwt 硬编码导致的 k8s 集群接管漏洞
repository: repo_url: https://github.com/KubeOperator/KubePi
repo_name: 1Panel-dev/KubePi
description: 🚀 现代化、开源的 K8s 面板，1Panel 官方出品。
repo_language: Go
owner: KubeOperator
file_changes: filename: app.yml
change_type: ModificationType.MODIFY
diff: @@ -12,4 +12,6 @@ spec:
   db:
     path: /var/lib/kubepi/db/kubepi.db
   session:
-    expires: 24
\ No newline at end of file
+    expires: 24
+  jwt:
+    key:
\ No newline at end of file

programming_language: CoffeeScript
filename: session.go
change_type: ModificationType.MODIFY
diff: @@ -32,7 +32,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-var JwtSigKey = []byte("signature_hmac_secret_shared_key")
 var jwtMaxAge = 10 * time.Minute
 
 type Handler struct {
@@ -51,7 +50,7 @@ func NewHandler() *Handler {
 		roleService:        role.NewService(),
 		rolebindingService: rolebinding.NewService(),
 		ldapService:        ldap.NewService(),
-		jwtSigner:          jwt.NewSigner(jwt.HS256, JwtSigKey, jwtMaxAge),
+		jwtSigner:          jwt.NewSigner(jwt.HS256, server.Config().Spec.Jwt.Key, jwtMaxAge),
 	}
 }
 

programming_language: Go
filename: v1.go
change_type: ModificationType.MODIFY
diff: @@ -401,8 +401,7 @@ func resourceNameInvalidHandler() iris.Handler {
 }
 
 func WarpedJwtHandler() iris.Handler {
-
-	verifier := jwt.NewVerifier(jwt.HS256, session.JwtSigKey)
+	verifier := jwt.NewVerifier(jwt.HS256, server.Config().Spec.Jwt.Key)
 	verifier.WithDefaultBlocklist()
 	verifyMiddleware := verifier.Verify(func() interface{} {
 		return new(session.UserProfile)

programming_language: Go
filename: config.go
change_type: ModificationType.MODIFY
diff: @@ -1,12 +1,15 @@
 package config
 
 import (
+	"crypto/rand"
 	"encoding/json"
 	"fmt"
 	"github.com/KubeOperator/kubepi/internal/model/v1/config"
 	"github.com/KubeOperator/kubepi/pkg/file"
 	"github.com/coreos/etcd/pkg/fileutil"
 	"github.com/spf13/viper"
+	"math/big"
+	"strconv"
 )
 
 const configNotFoundSkipErr = "config file not found in %s, skip"
@@ -17,7 +20,7 @@ var configFilePaths = []string{
 	"/etc/kubepi",
 }
 
-func ReadConfig(c *config.Config, path ...string)  error {
+func ReadConfig(c *config.Config, path ...string) error {
 	v := viper.New()
 	v.SetConfigName("app")
 	v.SetConfigType("yaml")
@@ -41,19 +44,36 @@ func ReadConfig(c *config.Config, path ...string)  error {
 		if err := v.MergeInConfig(); err != nil {
 			fmt.Println(fmt.Sprintf(configMergeErr, configFilePaths))
 		}
+
 	}
 
 	var configMap map[string]interface{}
 	if err := v.Unmarshal(&configMap); err != nil {
-		return  err
+		return err
 	}
 	str, err := json.Marshal(&configMap)
 	if err != nil {
-		return  err
+		return err
 	}
 	if err := json.Unmarshal(str, &c); err != nil {
-		return  nil
+		return nil
+	}
+	if c.Spec.Jwt.Key == "" {
+		v.Set("spec.jwt.key", generate(32))
+		if err := v.WriteConfig(); err != nil {
+			return err
+		}
 	}
-	return  nil
+	return nil
 }
 
+func generate(length int) string {
+	const base = 36
+	size := big.NewInt(base)
+	n := make([]byte, length)
+	for i := range n {
+		c, _ := rand.Int(rand.Reader, size)
+		n[i] = strconv.FormatInt(c.Int64(), base)[0]
+	}
+	return string(n)
+}

programming_language: Go
filename: config.go
change_type: ModificationType.MODIFY
diff: @@ -12,6 +12,7 @@ type Spec struct {
 	DB      DBConfig      `json:"db"`
 	Session SessionConfig `json:"session"`
 	Logger  LoggerConfig  `json:"logger"`
+	Jwt     JwtConfig     `json:"jwt"`
 	AppId   string        `json:"appId"`
 }
 
@@ -42,3 +43,7 @@ type DBConfig struct {
 type SessionConfig struct {
 	Expires int `json:"expires"`
 }
+
+type JwtConfig struct {
+	Key string `json:"key"`
+}

programming_language: Go
filename: server.go
change_type: ModificationType.MODIFY
diff: @@ -340,6 +340,7 @@ func getDefaultConfig() *v1Config.Config {
 				Expires: 72,
 			},
 			Logger: v1Config.LoggerConfig{Level: "debug"},
+			Jwt:    v1Config.JwtConfig{},
 		},
 	}
 }

programming_language: Go