CVE-2021-41135 (published 2021-10-20T18:15Z)

The Cosmos-SDK is a framework for building blockchain applications in Golang. Affected versions of the SDK were vulnerable to a consensus halt due to non-deterministic behaviour in a ValidateBasic method in the x/authz module. The MsgGrant of the x/authz module contains a Grant field which includes a user-defined expiration time for when the authorization grant expires. In Grant.ValidateBasic(), that time is compared to the node’s local clock time. Any chain running an affected version of the SDK with the authz module enabled could be halted by anyone with the ability to send transactions on that chain. Recovery would require applying the patch and rolling back the latest block. Users are advised to update to version 0.44.2.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:N/I:N/A:P)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-754
cwe_name: Improper Check for Unusual or Exceptional Conditions
description: The product does not check or incorrectly checks for unusual or exceptional conditions that are not expected to occur frequently during day to day operation of the product.
extended_description: 
            The programmer may assume that certain events or conditions will never occur or do not need to be worried about, such as low memory conditions, lack of access to resources due to restrictive permissions, or misbehaving clients or components. However, attackers may intentionally trigger these unusual conditions, thus violating the programmer's assumptions, possibly introducing instability, incorrect behavior, or a vulnerability.
            Note that this entry is not exclusively about the use of exceptions and exception handling, which are mechanisms for both checking and handling unusual or unexpected conditions.
         
         

Fix information:
repo_url: https://github.com/cosmos/cosmos-sdk
commit_details: repo_url: https://github.com/cosmos/cosmos-sdk
author: Robert Zaremba
author_date: 2021-10-12 16:40:49+02:00
msg: Merge pull request from GHSA-2p6r-37p9-89p2

* test: adding authz grant tests

* fix TestCLITxGrantAuthorization/Invalid_expiration_time test case

* comment out the test

* reenable test
repository: repo_url: https://github.com/cosmos/cosmos-sdk
repo_name: cosmos/cosmos-sdk
description: :chains: A Framework for Building High Value Public Blockchains :sparkles:
repo_language: Go
owner: cosmos
file_changes: filename: authorization_grant.go
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,11 @@ import (
 )
 
 // NewGrant returns new Grant
-func NewGrant(a Authorization, expiration time.Time) (Grant, error) {
+func NewGrant( /*blockTime time.Time, */ a Authorization, expiration time.Time) (Grant, error) {
+	// TODO: add this for 0.45
+	// if !expiration.After(blockTime) {
+	// 	return Grant{}, sdkerrors.ErrInvalidRequest.Wrapf("expiration must be after the current block time (%v), got %v", blockTime.Format(time.RFC3339), expiration.Format(time.RFC3339))
+	// }
 	g := Grant{
 		Expiration: expiration,
 	}
@@ -51,10 +55,6 @@ func (g Grant) GetAuthorization() Authorization {
 }
 
 func (g Grant) ValidateBasic() error {
-	if g.Expiration.Unix() < time.Now().Unix() {
-		return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past")
-	}
-
 	av := g.Authorization.GetCachedValue()
 	a, ok := av.(Authorization)
 	if !ok {

programming_language: Go
filename: authorization_grant_test.go
change_type: ModificationType.ADD
diff: @@ -0,0 +1,44 @@
+package authz
+
+import (
+	"testing"
+	"time"
+
+	// banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/stretchr/testify/require"
+)
+
+func expecError(r *require.Assertions, expected string, received error) {
+	if expected == "" {
+		r.NoError(received)
+	} else {
+		r.Error(received)
+		r.Contains(received.Error(), expected)
+	}
+}
+
+func TestNewGrant(t *testing.T) {
+	// ba := banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123)))
+	a := NewGenericAuthorization("some-type")
+	var tcs = []struct {
+		title     string
+		a         Authorization
+		blockTime time.Time
+		expire    time.Time
+		err       string
+	}{
+		// {"wrong expire time (1)", a, time.Unix(10, 0), time.Unix(8, 0), "expiration must be after"},
+		// {"wrong expire time (2)", a, time.Unix(10, 0), time.Unix(10, 0), "expiration must be after"},
+		{"good expire time (1)", a, time.Unix(10, 0), time.Unix(10, 1), ""},
+		{"good expire time (2)", a, time.Unix(10, 0), time.Unix(11, 0), ""},
+	}
+
+	for _, tc := range tcs {
+		t.Run(tc.title, func(t *testing.T) {
+			// _, err := NewGrant(tc.blockTime, tc.a, tc.expire)
+			_, err := NewGrant(tc.a, tc.expire)
+			expecError(require.New(t), tc.err, err)
+		})
+	}
+
+}

programming_language: Go
filename: tx.go
change_type: ModificationType.MODIFY
diff: @@ -127,11 +127,11 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() {
 				"send",
 				fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit),
 				fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
-				fmt.Sprintf("--%s=true", flags.FlagGenerateOnly),
+				fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
 				fmt.Sprintf("--%s=%d", cli.FlagExpiration, pastHour),
 			},
-			0,
-			true,
+			0xd,
+			false, // TODO: enable in v0.45
 		},
 		{
 			"fail with error invalid msg-type",

programming_language: Go
filename: msg_server.go
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,7 @@ import (
 
 var _ authz.MsgServer = Keeper{}
 
-// GrantAuthorization implements the MsgServer.Grant method.
+// GrantAuthorization implements the MsgServer.Grant method to create a new grant.
 func (k Keeper) Grant(goCtx context.Context, msg *authz.MsgGrant) (*authz.MsgGrantResponse, error) {
 	ctx := sdk.UnwrapSDKContext(goCtx)
 	grantee, err := sdk.AccAddressFromBech32(msg.Grantee)

programming_language: Go
filename: msgs_test.go
change_type: ModificationType.MODIFY
diff: @@ -80,7 +80,7 @@ func TestMsgGrantAuthorization(t *testing.T) {
 		{"nil granter and grantee address", nil, nil, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false},
 		{"nil authorization", granter, grantee, nil, time.Now(), true, false},
 		{"valid test case", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 1, 0), false, true},
-		{"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, false},
+		{"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, true}, // TODO need 0.45
 	}
 	for i, tc := range tests {
 		msg, err := authz.NewMsgGrant(

programming_language: Go