Use of Hard-coded Cryptographic Key in Netmaker
Description
Netmaker is a platform for creating and managing virtual overlay networks using WireGuard. Prior to versions 0.8.5, 0.9.4, and 010.0, there is a hard-coded cryptographic key in the code base which can be exploited to run admin commands on a remote server if the exploiter know the address and username of the admin. This effects the server (netmaker) component, and not clients. This has been patched in Netmaker v0.8.5, v0.9.4, and v0.10.0. There are currently no known workarounds.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/gravitl/netmakerGo | < 0.8.5 | 0.8.5 |
github.com/gravitl/netmakerGo | >= 0.9.0, < 0.9.4 | 0.9.4 |
Affected products
1Patches
2e9bce264719fMerge pull request #777 from gravitl/hotfix_v0.9.4
4 files changed · +67 −2
logic/jwts.go+19 −1 modified@@ -5,11 +5,29 @@ import ( "time" "github.com/golang-jwt/jwt/v4" + "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" ) -var jwtSecretKey = []byte("(BytesOverTheWire)") +var jwtSecretKey []byte + +// SetJWTSecret - sets the jwt secret on server startup +func SetJWTSecret() { + currentSecret, jwtErr := FetchJWTSecret() + if jwtErr != nil { + newString, err := GenerateRandomString(64) + if err != nil { + logger.FatalLog("something went wrong when generating the auth secret") + } + jwtSecretKey = []byte(newString) // 512 bit random password + if err := StoreJWTSecret(string(jwtSecretKey)); err != nil { + logger.FatalLog("something went wrong when configuring JWT authentication") + } + } else { + jwtSecretKey = []byte(currentSecret) + } +} // CreateJWT func will used to create the JWT while signing in and signing out func CreateJWT(macaddress string, network string) (response string, err error) {
logic/serverconf.go+29 −0 modified@@ -43,3 +43,32 @@ func FetchPrivKey(serverID string) (string, error) { func RemovePrivKey(serverID string) error { return database.DeleteRecord(database.SERVERCONF_TABLE_NAME, serverID) } + +// FetchJWTSecret - fetches jwt secret from db +func FetchJWTSecret() (string, error) { + var dbData string + var err error + var fetchedData = serverData{} + dbData, err = database.FetchRecord(database.SERVERCONF_TABLE_NAME, "nm-jwt-secret") + if err != nil { + return "", err + } + err = json.Unmarshal([]byte(dbData), &fetchedData) + if err != nil { + return "", err + } + return fetchedData.PrivateKey, nil +} + +// StoreJWTSecret - stores server jwt secret if needed +func StoreJWTSecret(privateKey string) error { + var newData = serverData{} + var err error + var data []byte + newData.PrivateKey = privateKey + data, err = json.Marshal(&newData) + if err != nil { + return err + } + return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME) +}
logic/util.go+18 −1 modified@@ -2,8 +2,10 @@ package logic import ( + crand "crypto/rand" "encoding/base64" "encoding/json" + "math/big" "math/rand" "strconv" "strings" @@ -278,7 +280,7 @@ func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr strin // RandomString - returns a random string in a charset func RandomString(length int) string { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789" var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) @@ -289,6 +291,21 @@ func RandomString(length int) string { return string(b) } +// GenerateRandomString - generates random string of n length +func GenerateRandomString(n int) (string, error) { + const chars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + ret := make([]byte, n) + for i := range ret { + num, err := crand.Int(crand.Reader, big.NewInt(int64(len(chars)))) + if err != nil { + return "", err + } + ret[i] = chars[num.Int64()] + } + + return string(ret), nil +} + // == Private Methods == func getNetworkEgressAndNodes(networkName string) ([]models.Node, []models.Node, error) {
main.go+1 −0 modified@@ -41,6 +41,7 @@ func initialize() { // Client Mode Prereq Check logger.FatalLog("Error connecting to database") } logger.Log(0, "database successfully connected") + logic.SetJWTSecret() var authProvider = auth.InitializeAuthProvider() if authProvider != "" {
3d4f44ecfe8bMerge pull request #770 from gravitl/hotfix_v0.10.0_jwt_data
3 files changed · +52 −2
logic/jwts.go+22 −1 modified@@ -2,14 +2,29 @@ package logic import ( "errors" + "fmt" "time" "github.com/golang-jwt/jwt/v4" + "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" ) -var jwtSecretKey = []byte("(BytesOverTheWire)") +var jwtSecretKey []byte + +// SetJWTSecret - sets the jwt secret on server startup +func SetJWTSecret() { + currentSecret, jwtErr := FetchJWTSecret() + if jwtErr != nil { + jwtSecretKey = []byte(RandomString(64)) // 512 bit random password + if err := StoreJWTSecret(string(jwtSecretKey)); err != nil { + logger.FatalLog("something went wrong when configuring JWT authentication") + } + } else { + jwtSecretKey = []byte(currentSecret) + } +} // CreateJWT func will used to create the JWT while signing in and signing out func CreateJWT(uuid string, macAddress string, network string) (response string, err error) { @@ -19,6 +34,9 @@ func CreateJWT(uuid string, macAddress string, network string) (response string, Network: network, MacAddress: macAddress, StandardClaims: jwt.StandardClaims{ + Issuer: "Netmaker", + Subject: fmt.Sprintf("node|%s", uuid), + IssuedAt: time.Now().Unix(), ExpiresAt: expirationTime.Unix(), }, } @@ -39,6 +57,9 @@ func CreateUserJWT(username string, networks []string, isadmin bool) (response s Networks: networks, IsAdmin: isadmin, StandardClaims: jwt.StandardClaims{ + Issuer: "Netmaker", + IssuedAt: time.Now().Unix(), + Subject: fmt.Sprintf("user|%s", username), ExpiresAt: expirationTime.Unix(), }, }
logic/serverconf.go+29 −0 modified@@ -43,3 +43,32 @@ func FetchPrivKey(serverID string) (string, error) { func RemovePrivKey(serverID string) error { return database.DeleteRecord(database.SERVERCONF_TABLE_NAME, serverID) } + +// FetchJWTSecret - fetches jwt secret from db +func FetchJWTSecret() (string, error) { + var dbData string + var err error + var fetchedData = serverData{} + dbData, err = database.FetchRecord(database.SERVERCONF_TABLE_NAME, "nm-jwt-secret") + if err != nil { + return "", err + } + err = json.Unmarshal([]byte(dbData), &fetchedData) + if err != nil { + return "", err + } + return fetchedData.PrivateKey, nil +} + +// StoreJWTSecret - stores server jwt secret if needed +func StoreJWTSecret(privateKey string) error { + var newData = serverData{} + var err error + var data []byte + newData.PrivateKey = privateKey + data, err = json.Marshal(&newData) + if err != nil { + return err + } + return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME) +}
main.go+1 −1 modified@@ -40,7 +40,6 @@ func main() { func initialize() { // Client Mode Prereq Check var err error - if servercfg.GetNodeID() == "" { logger.FatalLog("error: must set NODE_ID, currently blank") } @@ -49,6 +48,7 @@ func initialize() { // Client Mode Prereq Check logger.FatalLog("Error connecting to database") } logger.Log(0, "database successfully connected") + logic.SetJWTSecret() err = logic.TimerCheckpoint() if err != nil {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-86f3-hf24-76q4ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23650ghsaADVISORY
- github.com/gravitl/netmaker/commit/3d4f44ecfe8be4ca38920556ba3b90502ffb4feeghsax_refsource_MISCWEB
- github.com/gravitl/netmaker/commit/e9bce264719f88c30e252ecc754d08f422f4c080ghsax_refsource_MISCWEB
- github.com/gravitl/netmaker/pull/781/commits/1bec97c662670dfdab804343fc42ae4b1d050a87ghsax_refsource_MISCWEB
- github.com/gravitl/netmaker/security/advisories/GHSA-86f3-hf24-76q4ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.