CVE-2021-41137 (published 2021-10-13T14:15Z)

Minio is a Kubernetes native application for cloud storage. All users on release `RELEASE.2021-10-10T16-53-30Z` are affected by a vulnerability that involves bypassing policy restrictions on regular users. Normally, checkKeyValid() should return owner true for rootCreds. In the affected version, policy restriction did not work properly for users who did not have service (svc) or security token service (STS) accounts. This issue is fixed in `RELEASE.2021-10-13T00-23-17Z`. A downgrade back to release `RELEASE.2021-10-08T23-58-24Z` is available as a workaround.

Severity: MEDIUM
CVSS v2 base score: 6.5 (AV:N/AC:L/Au:S/C:P/I:P/A:P)
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: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/minio/minio
commit_details: repo_url: https://github.com/minio/minio
author: Harshavardhana
author_date: 2021-10-12 13:18:02-07:00
msg: checkKeyValid() should return owner true for rootCreds (#13422)

Looks like policy restriction was not working properly
for normal users when they are not svc or STS accounts.

- svc accounts are now properly fixed to get
  right permissions when its inherited, so
  we do not have to set 'owner = true'

- sts accounts have always been using right
  permissions, do not need an explicit lookup

- regular users always have proper policy mapping
repository: repo_url: https://github.com/minio/minio
repo_name: minio/minio
description: The Object Store for AI Data Infrastructure
repo_language: Go
owner: minio
file_changes: filename: signature-v4-utils.go
change_type: ModificationType.MODIFY
diff: @@ -31,7 +31,6 @@ import (
 	"github.com/minio/minio/internal/auth"
 	xhttp "github.com/minio/minio/internal/http"
 	"github.com/minio/minio/internal/logger"
-	iampolicy "github.com/minio/pkg/iam/policy"
 )
 
 // http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD" indicates that the
@@ -150,8 +149,7 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A
 		return auth.Credentials{}, false, ErrServerNotInitialized
 	}
 
-	var owner = true
-	var cred = globalActiveCred
+	cred := globalActiveCred
 	if cred.AccessKey != accessKey {
 		// Check if the access key is part of users credentials.
 		ucred, ok := globalIAMSys.GetUser(accessKey)
@@ -165,18 +163,9 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A
 	if s3Err != ErrNone {
 		return cred, false, s3Err
 	}
+	cred.Claims = claims
 
-	if len(claims) > 0 {
-		cred.Claims = claims
-
-		// Now check if we have a sessionPolicy.
-		if _, ok := claims[iampolicy.SessionPolicyName]; ok {
-			owner = false
-		} else {
-			owner = cred.AccessKey == cred.ParentUser
-		}
-	}
-
+	owner := cred.AccessKey == globalActiveCred.AccessKey
 	return cred, owner, ErrNone
 }
 

programming_language: Go
filename: signature-v4-utils_test.go
change_type: ModificationType.MODIFY
diff: @@ -18,12 +18,75 @@
 package cmd
 
 import (
+	"context"
 	"net/http"
+	"os"
 	"testing"
 
+	"github.com/minio/madmin-go"
+	"github.com/minio/minio/internal/auth"
 	xhttp "github.com/minio/minio/internal/http"
 )
 
+func TestCheckValid(t *testing.T) {
+	objLayer, fsDir, err := prepareFS()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(fsDir)
+	if err = newTestConfig(globalMinioDefaultRegion, objLayer); err != nil {
+		t.Fatalf("unable initialize config file, %s", err)
+	}
+
+	newAllSubsystems()
+
+	initAllSubsystems(context.Background(), objLayer)
+
+	globalIAMSys.InitStore(objLayer)
+
+	req, err := newTestRequest(http.MethodGet, "http://example.com:9000/bucket/object", 0, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err = signRequestV4(req, globalActiveCred.AccessKey, globalActiveCred.SecretKey); err != nil {
+		t.Fatal(err)
+	}
+
+	_, owner, s3Err := checkKeyValid(req, globalActiveCred.AccessKey)
+	if s3Err != ErrNone {
+		t.Fatalf("Unexpected failure with %v", errorCodes.ToAPIErr(s3Err))
+	}
+
+	if !owner {
+		t.Fatalf("Expected owner to be 'true', found %t", owner)
+	}
+
+	_, _, s3Err = checkKeyValid(req, "does-not-exist")
+	if s3Err != ErrInvalidAccessKeyID {
+		t.Fatalf("Expected error 'ErrInvalidAccessKeyID', found %v", s3Err)
+	}
+
+	ucreds, err := auth.CreateCredentials("myuser1", "mypassword1")
+	if err != nil {
+		t.Fatalf("unable create credential, %s", err)
+	}
+
+	globalIAMSys.CreateUser(ucreds.AccessKey, madmin.UserInfo{
+		SecretKey: ucreds.SecretKey,
+		Status:    madmin.AccountEnabled,
+	})
+
+	_, owner, s3Err = checkKeyValid(req, ucreds.AccessKey)
+	if s3Err != ErrNone {
+		t.Fatalf("Unexpected failure with %v", errorCodes.ToAPIErr(s3Err))
+	}
+
+	if owner {
+		t.Fatalf("Expected owner to be 'false', found %t", owner)
+	}
+}
+
 // TestSkipContentSha256Cksum - Test validate the logic which decides whether
 // to skip checksum validation based on the request header.
 func TestSkipContentSha256Cksum(t *testing.T) {

programming_language: Go