Summary
This advisory board aims to describe and address a two vulnerabilities found in the Evmos codebase.
- A bug causes discrepancies between EVM and bank module balances during precompile calls. The fix comprises explicitly updating balances in the
stateDB
during such transactions. It also includes the addition of a new journal entry to allow to revert precompile-related state changes
- The
claimRewards
method on the distribution precompile is vulnerable of a DoS attack.
1. Incorrect balance update
Details
Within the precompiles, we have identified a bug which results in a discrepancy between the balance of an account in the EVM and the bank module. This is the special case in which:
- a precompile contract is called from a smart contract
- the called precompile contract method changes an account's balance
- within the transaction that calls the precompile, the account affected by the precompile call changes its state (besides the changes performed by the precompile contract call).
For example, calling the delegate
function of the staking precompile under this scenario:
- the smart contract function to call is a method that increases a
counter
variable before the precompile call and decreases after the precompile call
- the smart contract address is the delegator
Consequently, the smart contract state object has two sources of state update:
- the bank module when calling the precompile
delegate
function
- the state on the EVM when modifying the
counter
variable of the contract
When this happens, the balance on the bank module and the EVM are different. Currently, the application considers the EVM state as the source of truth, because this is an EVM transaction. This results in improper balance after a precompile transaction, where its balance remains unchanged during this transaction that should have decreased its balance.
Fix
When calling precompiles methods that change accounts balances from a smart contract, the application should update explicitly the balance of the affected account/s on the stateDB
.
Severity
Based on ImmuneFi Severity Classification System the severity was evaluated to Critical since the attack could have lead to direct loss of funds.
2. DoS attack vector
Details
A vulnerability was identified in the evmos Distribution precompile that causes an out-of-memory condition, resulting in a node denial-of-service (DoS). Testing revealed that it was possible to trigger this vulnerability without the need to deploy a smart contract, or hold a balance on the network
The claimRewards
function, accessible via the Distribution precompile, allows the caller to specify an arbitrary maxRetrieval value that is later passed to the staking module's GetDelegatorValidators
function. GetDelegatorValidators
directly uses maxRetrieval
during a call to make([]types.Validator, maxRetrieve), which allocates a significant amount of memory, leading to an irrecoverable out-of-memory issue. The Distribution precompile makes a claimRewards
function available, allowing a delegator to claim rewards from multiple validators. It accepts two arguments: the delegator address delegatorAddr
) and the maximum number of validators to retrieve rewards from maxRetrieve
). After parsing these arguments from the call's calldata, the staking module's GetDelegatorValidators
is called.
In the staking module's GetDelegatorValidators
function, the maxRetrieve
amount is used directly during a call to make()
.
A malicious actor can call the Distribution precompile's claimRewards
with maxRetrieval = 0xffffffff
(max uint32) to allocate a large amount of memory, which triggers a denial of service condition
Fix
Use the MaxValidators
number from the module parameters and restrict the maxRetrieve
to that value.
Patches
The issue has been patched in versions >=VXX0.0
Summary
This advisory board aims to describe and address a two vulnerabilities found in the Evmos codebase.
stateDB
during such transactions. It also includes the addition of a new journal entry to allow to revert precompile-related state changesclaimRewards
method on the distribution precompile is vulnerable of a DoS attack.1. Incorrect balance update
Details
Within the precompiles, we have identified a bug which results in a discrepancy between the balance of an account in the EVM and the bank module. This is the special case in which:
For example, calling the
delegate
function of the staking precompile under this scenario:counter
variable before the precompile call and decreases after the precompile callConsequently, the smart contract state object has two sources of state update:
delegate
functioncounter
variable of the contractWhen this happens, the balance on the bank module and the EVM are different. Currently, the application considers the EVM state as the source of truth, because this is an EVM transaction. This results in improper balance after a precompile transaction, where its balance remains unchanged during this transaction that should have decreased its balance.
Fix
When calling precompiles methods that change accounts balances from a smart contract, the application should update explicitly the balance of the affected account/s on the
stateDB
.Severity
Based on ImmuneFi Severity Classification System the severity was evaluated to Critical since the attack could have lead to direct loss of funds.
2. DoS attack vector
Details
A vulnerability was identified in the evmos Distribution precompile that causes an out-of-memory condition, resulting in a node denial-of-service (DoS). Testing revealed that it was possible to trigger this vulnerability without the need to deploy a smart contract, or hold a balance on the network
The
claimRewards
function, accessible via the Distribution precompile, allows the caller to specify an arbitrary maxRetrieval value that is later passed to the staking module'sGetDelegatorValidators
function.GetDelegatorValidators
directly usesmaxRetrieval
during a call to make([]types.Validator, maxRetrieve), which allocates a significant amount of memory, leading to an irrecoverable out-of-memory issue. The Distribution precompile makes aclaimRewards
function available, allowing a delegator to claim rewards from multiple validators. It accepts two arguments: the delegator addressdelegatorAddr
) and the maximum number of validators to retrieve rewards frommaxRetrieve
). After parsing these arguments from the call's calldata, the staking module'sGetDelegatorValidators
is called.In the staking module's
GetDelegatorValidators
function, themaxRetrieve
amount is used directly during a call tomake()
.A malicious actor can call the Distribution precompile's
claimRewards
withmaxRetrieval = 0xffffffff
(max uint32) to allocate a large amount of memory, which triggers a denial of service conditionFix
Use the
MaxValidators
number from the module parameters and restrict themaxRetrieve
to that value.Patches
The issue has been patched in versions >=VXX0.0