CVE-2023-46740: Use of Insufficiently Random Values (CWE-330)
value: CubeFS is an open-source cloud-native file storage system. Prior to version 3.3.1, CubeFS used an insecure random string generator to generate user-specific, sensitive keys used to authenticate users in a CubeFS deployment. This could allow an attacker to predict and/or guess the generated string and impersonate a user thereby obtaining higher privileges. When CubeFS creates new users, it creates a piece of sensitive information for the user called the “accessKey”. To create the "accesKey", CubeFS uses an insecure string generator which makes it easy to guess and thereby impersonate the created user. An attacker could leverage the predictable random string generator and guess a users access key and impersonate the user to obtain higher privileges. The issue has been fixed in v3.3.1. There is no other mitigation than to upgrade.
CWE description: The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers.
CVSS v3 base score: 9.8
Published: 2024-01-03T17:15Z
Repository: https://github.com/cubefs/cubefs
Language: Go
Files changed: proto/packet.go, sdk/data/stream/stream_writer.go, util/string.go
Commit date: 2023-10-23 10:53:38 +0800
Commit message: [Enhancement]: Optimize insecure random number generation in function util/string.go:RandomString
close: #2696
Signed-off-by: true1064 <tangjingyu@oppo.com>

Diff: diff --git a/proto/packet.go b/proto/packet.go
index 2f10b56d5..b3efd26d0 100644
--- a/proto/packet.go
+++ b/proto/packet.go
@@ -594,10 +594,12 @@ func (p *Packet) GetOpMsg() (m string) {
 		m = "OpLcNodeHeartbeat"
 	case OpLcNodeScan:
 		m = "OpLcNodeScan"
 	case OpLcNodeSnapshotVerDel:
 		m = "OpLcNodeSnapshotVerDel"
+	case OpMetaReadDirOnly:
+		m = "OpMetaReadDirOnly"
 	default:
 		m = fmt.Sprintf("op:%v not found", p.Opcode)
 	}
 	return
 }
diff --git a/sdk/data/stream/stream_writer.go b/sdk/data/stream/stream_writer.go
index 667837bf2..9a388adea 100644
--- a/sdk/data/stream/stream_writer.go
+++ b/sdk/data/stream/stream_writer.go
@@ -393,11 +393,12 @@ begin:
 				if err = checkFunc(); err != nil {
 					return
 				}
 			}
 			// try append write, get response
-			log.LogDebugf("action[streamer.write] doAppendWrite req %v FileOffset %v size %v", req.ExtentKey, req.FileOffset, req.Size)
+			log.LogDebugf("action[streamer.write] doAppendWrite req: ExtentKey(%v) FileOffset(%v) size(%v)",
+				req.ExtentKey, req.FileOffset, req.Size)
 			var status int32
 			// First, attempt sequential writes using neighboring extent keys. If the last extent has a different version,
 			// it indicates that the extent may have been fully utilized by the previous version.
 			// Next, try writing and directly checking the extent at the datanode. If the extent cannot be reused, create a new extent for writing.
 			if writeSize, err, status = s.doAppendWrite(req.Data, req.FileOffset, req.Size, direct, true); status == LastEKVersionNotEqual {
diff --git a/util/string.go b/util/string.go
index aab9b9ef4..84f475969 100644
--- a/util/string.go
+++ b/util/string.go
@@ -13,13 +13,13 @@
 // permissions and limitations under the License.
 
 package util
 
 import (
-	"math/rand"
+	"crypto/rand"
+	"math/big"
 	"strings"
-	"time"
 )
 
 func SubString(sourceString string, begin, end int) string {
 	bytes := []byte(sourceString)
 	stringLength := len(bytes)
@@ -57,11 +57,11 @@ const (
 
 func RandomString(length int, seed RandomSeed) string {
 	runs := seed.Runes()
 	result := ""
 	for i := 0; i < length; i++ {
-		rand.Seed(time.Now().UnixNano())
-		randNumber := rand.Intn(len(runs))
-		result += string(runs[randNumber])
+		lenInt64 := int64(len(runs))
+		randNumber, _ := rand.Int(rand.Reader, big.NewInt(lenInt64))
+		result += string(runs[randNumber.Uint64()])
 	}
 	return result
 }

Vulnerable code: 			log.LogDebugf("action[streamer.write] doAppendWrite req %v FileOffset %v size %v", req.ExtentKey, req.FileOffset, req.Size)
	"math/rand"
	"time"
		rand.Seed(time.Now().UnixNano())
		randNumber := rand.Intn(len(runs))
		result += string(runs[randNumber])
Fixed code: 	case OpMetaReadDirOnly:
		m = "OpMetaReadDirOnly"
			log.LogDebugf("action[streamer.write] doAppendWrite req: ExtentKey(%v) FileOffset(%v) size(%v)",
				req.ExtentKey, req.FileOffset, req.Size)
	"crypto/rand"
	"math/big"
		lenInt64 := int64(len(runs))
		randNumber, _ := rand.Int(rand.Reader, big.NewInt(lenInt64))
		result += string(runs[randNumber.Uint64()])