Platform: Code4rena
Start Date: 03/05/2022
Pot Size: $75,000 USDC
Total HM: 6
Participants: 55
Period: 7 days
Judge: Albert Chon
Total Solo HM: 2
Id: 116
League: COSMOS
Rank: 10/55
Findings: 3
Award: $1,789.31
🌟 Selected for report: 0
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L660-L669 https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L276-L358
The constructor of the Gravity
contract checks that cumulativePower
is greater than _powerThreshold
. However, the updateValset()
function contains no check that cumulativePower
> state_powerThreshold
for the new validator set. The missing check is dangerous because if the new validator set does not contain sufficient cumulative voting power, then all function calls that require the successful execution of checkValidatorSignatures()
function would fail, rendering the gravity
contract unusable.
Note that this problem cannot be fixed by the validators subsequently signing off on a new validator set that does satisfy the condition, because the updateValset()
function also requires the successful execution of the checkValidatorSignatures()
function.
In the constructor of the Gravity
contract, the following lines of code check that cumulativePower
is greater than _powerThreshold
. The state_powerThreshold
variable then records the _powerThreshold
of the validator set.
However, in the updateValset()
function, there is no check that the cumulativePower
of the _newValset
is still greater than state_powerThreshold
, which could potentially result in insufficient cumulativePower
for the _newValset
, in which case the submitBatch()
and submitLogicCall()
functions would always revert, because the checkValidatorSignatures()
function would be guaranteed to fail.
Manual review
Recommend adding a check that the cumulativePower
of the _newValset
is still greater than state_powerThreshold
in the updateValset()
function, similar to the check in the constructor.
#0 - V-Staykov
2022-05-11T11:13:15Z
Duplicate of #123
🌟 Selected for report: p_crypt0
Also found by: 0x1337, GermanKuber, IllIllI, WatchPug, csanuragjain, danb, dirk_y, kirk-baird
If one admin
of the cudosAccessControls
contract is hacked or compromised, the entire value locked and all user fund in the bridge could be lost on both the Ethereum side and the Cudos side.
The security design of the bridge is that a sufficient number of validators need to sign a transaction, in order for the gravity
contract to execute a transaction via the submitBatch()
or the submitLogicCall()
function. These functions are used to transfers tokens from addresses on the Cudos chain to addresses on the Ethereum chain, or execute other logic on the Ethereum side.
However, the withdrawERC20()
function completely bypasses this security design. Any address with the admin
role in the cudosAccessControls
contract can call withdrawERC20()
, and drain any ERC20 token balance from the gravity
contract. These tokens might either be users' deposit into the gravity
contract (via the sendToCosmos()
function), or synthetic tokens that represent tokens deposited and bridged on the Cudos chain. In the first scenario, users' fund on the Ethereum chain could be lost. In the second scenario, a compromised admin
can bridge the synthetic tokens back to the Cudos chain, by calling sendToCosmos()
and using the event emitted to withdraw from the Cudos chain, and users fund on the Cudos chain would be lost. As a result, the total value locked in the bridge on both chains could be lost if one admin
is compromised or hacked.
Manual Review
The withdrawERC20()
function should only be used to withdraw tokens sent to the contract by accident, and not users deposit or synthetic tokens that represent tokens deposited on the Cudos chain. Recommend adding a check in this function that the _tokenAddress
cannot represent any token that uses deposit or tokens deposited on the Cudos chain.
Alternatively, the same security model should be applied to the withdrawERC20()
function, which means that there needs to be validator signature verification in this function, just like in the submitBatch()
and the submitLogicCall()
functions.
#0 - maptuhec
2022-05-11T08:23:48Z
Duplicate of #14
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0xDjango, 0xkatana, AmitN, CertoraInc, Dravee, Funen, GermanKuber, GimelSec, Hawkeye, JC, MaratCerby, WatchPug, Waze, broccolirob, cccz, ch13fd357r0y3r, cryptphi, danb, defsec, delfin454000, dipp, dirk_y, ellahi, gzeon, hake, hubble, ilan, jah, jayjonah8, kebabsec, kirk-baird, m9800, orion, oyc_109, robee, shenwilly, simon135, sorrynotsorry
666.4988 USDC - $666.50
The whitelisted
variable, onlyWhitelisted
modifier, and the manageWhitelist
function are declared in the gravity
contract, and yet do not play any role in the functioning of the bridge.
Recommend removing unused variables, modifiers, and functions.