Platform: Code4rena
Start Date: 21/04/2022
Pot Size: $100,000 USDC
Total HM: 18
Participants: 60
Period: 7 days
Judge: gzeon
Total Solo HM: 10
Id: 112
League: ETH
Rank: 18/60
Findings: 2
Award: $606.22
🌟 Selected for report: 1
🚀 Solo Findings: 0
293.0606 USDC - $293.06
https://github.com/code-423n4/2022-04-backd/blob/c856714a50437cb33240a5964b63687c9876275b/backd/contracts/access/RoleManager.sol#L43-L46 https://github.com/code-423n4/2022-04-backd/blob/c856714a50437cb33240a5964b63687c9876275b/backd/contracts/access/RoleManager.sol#L115-L128
The impact of this vulnerability, i.e., losing all governance control is very High. There is a possibility, due to a corner case as described below.
Contract : RoleManager.sol Function : renounceGovernance()
Step 0: Let current governance role given to = CURRENT_GOV_ADDRESS so, getRoleMemberCount() for "governance" role will return = 1 Step 1: Add a new address say ALICE to governance role, by addGovernor(ALICE) now, ALICE also has governace role, and getRoleMemberCount() for "governance" role will return = 2 Step 2: Assume that ALICE renounces governance role, by renounceGovernance() now, ALICE does not have governance role, but getRoleMemberCount() for "governance" role will return = 2, due to a BUG Step 3: In some distant future, if there is a compromise of CURRENT_GOV_ADDRESS keys or due to some other reason, its decided to revoke governance role for CURRENT_GOV_ADDRESS via renounceGovernance(), and the command succeeds It can be assumed that since getRoleMemberCount() for "governance" role returns = 2, at least there is one other active governor address. But now, CURRENT_GOV_ADDRESS does not have governance role, and the total governance control on the protocol is lost my mistake.
getRoleMemberCount() currently returns _roleMembers[role].length(); It should return the count only for _roles[role].members[account] = true;
Its recommended to add a new function to know who are the active members for any role, like getRoleMembers(bytes32 role) returning address account []
#0 - chase-manning
2022-04-28T15:12:15Z
Duplicate of #164
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x52, 0xDjango, 0xkatana, Dravee, Funen, Kenshin, Ruhum, StyxRave, Tadashi, TerrierLover, TrungOre, antonttc, berndartmueller, catchup, csanuragjain, defsec, dipp, fatherOfBlocks, hake, horsefacts, hubble, jayjonah8, joestakey, kebabsec, kenta, m4rio_eth, oyc_109, pauliax, peritoflores, rayn, remora, robee, securerodd, simon135, sorrynotsorry, sseefried, z3s
313.1606 USDC - $313.16
L-01 : Inconsistencies in constructor parameters of the contracts
L-02 : No reset equivalent for function prepareKeeperRequiredStakedBKD
L-03 : Wrong use of amount in the emit Deposit event in VaultReserve.sol
L-04 : Upgradability of the core contracts not considered
NC-01 : modifier onlyPool not used anywhere in Vault contract
NC-02 : Wrong and misleading dev comment in AddressProvider.sol
The constructors for Controller, CvxCrvRewardsLocker, etc., use 'IAddressProvider _addressProvider' as parameter, and other values are obtained from the addressProvider functions.
On the contrary, the constructors for TopUpAction, Vault, LiquidityPool, GasBank, etc., use 'IController _controller' as parameter, and other values like addressProvider are derived from the controller functions.
There is a possibility of maliciously deploying multiple controllers, (though each can use same or different addressProvider), and have multiple sources of truth.
Consistently use addressProvider as the contract parameter and derive other values from it, since the AddressProvider contract is a single source of truth. This will probibit any misuse or mistakes during deployment of contracts in future.
The protocol will not be able to reset the values that was prepared for KEEPER_REQUIRED_STAKED_BKD. In case reset is required, it has to be executed and prepared again for correcting any mistake.
Contract : Controller.sol Function : prepareKeeperRequiredStakedBKDO() and executeKeeperRequiredStakedBKD()
Define a new function resetKeeperRequiredStakedBKD, to be consistent with the prepare, reset and execute pattern.
In the emit event of Deposit() function, instead of using 'amount' , the correct value to use is 'received'
Possibility of emitting a wrong value in case the value of 'received' is more than 'amount'.
uint256 received = newBalance - balance; require(received >= amount, Error.INVALID_AMOUNT); _balances[msg.sender][token] += received;
Contract : VaultReserve.sol Function : deposit
line 61 emit Deposit(msg.sender, token, amount);
Change as below
line 61 emit Deposit(msg.sender, token, received);
Since the protocol is still in development, the features and functions may be needed to be modified/added in say the AddressProvider, Controller, etc., In that eventuality, it may be difficult to upgrade these contracts.
Use upgradable proxy pattern of deployment for AddressProvider, Controller etc.,
Contract : Vault.sol
modifier onlyPool() {
require(msg.sender == pool, Error.UNAUTHORIZED_ACCESS);
_;
}
Since its a dead code, delete the modifier onlyPool()
Contract : AddressProvider.sol Function : addStakerVault()
line 271 /** line 272 * @notice Add a new staker vault and add it's lpGauge if set in vault.
There is no lpGause set in this contract, but its set in Controller.sol for the same function.
Move the @notice comment to against the function addStakerVault in Controller.sol contract where its relevant.
#0 - chase-manning
2022-04-28T10:17:36Z
I consider this report to be of particularly high quality