CVE-2021-41137 (Go, published 2021-10-13T14:15Z)
Description: value: 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 v3 base score: 8.8)
Weakness: NVD-CWE-Other \u2014 Other
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.
Repository: https://github.com/minio/minio
Files changed:
cmd/signature-v4-utils.go
cmd/signature-v4-utils_test.go
Commit message: 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

Vulnerable code:
	iampolicy "github.com/minio/pkg/iam/policy"
	var owner = true
	var cred = globalActiveCred
	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
		}
	}


Fixed code:
	cred := globalActiveCred
	cred.Claims = claims
	owner := cred.AccessKey == globalActiveCred.AccessKey
	"context"
	"os"
	"github.com/minio/madmin-go"
	"github.com/minio/minio/internal/auth"
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)
	}
}