VYPR
Moderate severityNVD Advisory· Published Oct 20, 2021· Updated Aug 4, 2024

Authz Module Non-Determinism

CVE-2021-41135

Description

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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A non-deterministic time check in Cosmos SDK's x/authz module can cause a consensus halt; fixed in v0.44.2.

Vulnerability

The Cosmos SDK, versions 0.43.x and 0.44.0 through 0.44.1, contain a non-deterministic behavior in the x/authz module's ValidateBasic method. Specifically, the MsgGrant contains a Grant field with a user-defined expiration time. In Grant.ValidateBasic(), this expiration time is compared to the node's local clock time using time.Now().Unix(), which is subjective and non-deterministic across nodes. This vulnerability affects any blockchain network built with the affected SDK versions and with the authz module enabled [1][2][4].

Exploitation

An attacker with the ability to send transactions on the target chain can craft multiple MsgGrant messages with different but close expiration times (e.g., separated by a few seconds). By exercising the granted functionality for all of them near their expiration time, it becomes likely that some nodes consider grants expired while others do not, leading to a disagreement in state and a consensus halt. No special privileges beyond transaction submission are required [2][4].

Impact

Successful exploitation halts the blockchain's consensus, preventing further block production and transaction processing. Recovery requires applying the patch and rolling back the latest block, making this a high-severity availability impact [1][2].

Mitigation

The vulnerability is fixed in Cosmos SDK version 0.44.2, released on October 12, 2021. The fix removes the problematic time check entirely, as full validation of grant parameters occurs in the keepers. Users should upgrade to v0.44.2 or later. Network operators who cannot immediately upgrade should consider temporarily disabling the authz module. The vulnerability is not listed in CISA's Known Exploited Vulnerabilities catalog as of this writing [1][2][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/cosmos/cosmos-sdkGo
>= 0.43.0, < 0.44.20.44.2

Affected products

3

Patches

1
68ab790a761e

Merge pull request from GHSA-2p6r-37p9-89p2

https://github.com/cosmos/cosmos-sdkRobert ZarembaOct 12, 2021via ghsa
5 files changed · +54 10
  • x/authz/authorization_grant.go+5 5 modified
    @@ -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 {
    
  • x/authz/authorization_grant_test.go+44 0 added
    @@ -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)
    +		})
    +	}
    +
    +}
    
  • x/authz/client/testutil/tx.go+3 3 modified
    @@ -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",
    
  • x/authz/keeper/msg_server.go+1 1 modified
    @@ -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)
    
  • x/authz/msgs_test.go+1 1 modified
    @@ -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(
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.