Platform: Code4rena
Start Date: 11/11/2022
Pot Size: $90,500 USDC
Total HM: 52
Participants: 92
Period: 7 days
Judge: LSDan
Total Solo HM: 20
Id: 182
League: ETH
Rank: 41/92
Findings: 3
Award: $146.87
🌟 Selected for report: 0
🚀 Solo Findings: 0
88.5851 USDC - $88.59
Judge has assessed an item in Issue #205 as M risk. The relevant finding follows:
L[01] - No check if EOARepresentative or EOARepresentativeOfNodeRunner is an EOA or a smart contract Impact A smart contract can end up being assigned as a smartWalletRepresentative. Such smart contract might not have the functionality to interact with LSD or funds.
function rotateEOARepresentative(address _newRepresentative) external { require(_newRepresentative != address(0), "Zero address"); require(isNodeRunnerBanned(msg.sender) == false, "Node runner is banned from LSD network");
address smartWallet = smartWalletOfNodeRunner[msg.sender]; require(smartWallet != address(0), "No smart wallet"); require(stakedKnotsOfSmartWallet[smartWallet] == 0, "Not all KNOTs are minted"); require(smartWalletRepresentative[smartWallet] != _newRepresentative, "Invalid rotation to same EOA"); // unauthorize old representative _authorizeRepresentative(smartWallet, smartWalletRepresentative[smartWallet], false); // authorize new representative _authorizeRepresentative(smartWallet, _newRepresentative, true); }
function _authorizeRepresentative( address _smartWallet, address _eoaRepresentative, bool _isEnabled ) internal { if(!_isEnabled && smartWalletRepresentative[_smartWallet] != address(0)) { // authorize the EOA representative on the Stakehouse IOwnableSmartWallet(_smartWallet).execute( address(getTransactionRouter()), abi.encodeWithSelector( ITransactionRouter.authorizeRepresentative.selector, _eoaRepresentative, _isEnabled ) ); // delete the mapping delete smartWalletRepresentative[_smartWallet]; emit RepresentativeRemoved(_smartWallet, _eoaRepresentative); } else if(_isEnabled && smartWalletRepresentative[_smartWallet] == address(0)) { // authorize the EOA representative on the Stakehouse IOwnableSmartWallet(_smartWallet).execute( address(getTransactionRouter()), abi.encodeWithSelector( ITransactionRouter.authorizeRepresentative.selector, _eoaRepresentative, _isEnabled ) ); // store EOA to the wallet mapping smartWalletRepresentative[_smartWallet] = _eoaRepresentative; emit RepresentativeAppointed(_smartWallet, _eoaRepresentative); } else { revert("Unexpected state"); } }
Recommended Mitigation Steps Add a check like the one seen here: https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/LiquidStakingManager.sol#L435-L436
require(!Address.isContract(_eoaRepresentative), "Only EOA representative permitted");
#0 - c4-judge
2022-12-01T23:19:52Z
dmvt marked the issue as duplicate of #187
#1 - c4-judge
2022-12-05T10:31:11Z
dmvt marked the issue as satisfactory
#2 - C4-Staff
2022-12-21T05:53:33Z
JeeberC4 marked the issue as duplicate of #93
6.2548 USDC - $6.25
Enabling/disabling whitelisting of a noderunner is not possible.
If enableWhitelisting
is set to true
registering BLS public keys through registerBLSPublicKeys()
will fail because _isNodeRunnerValid()
will always revert.
The check below will never work because isNodeRunnerWhitelisted[_nodeRunner]
is being compared against itself.
require(isNodeRunnerWhitelisted[_nodeRunner] != isNodeRunnerWhitelisted[_nodeRunner], "Unnecessary update to same status");
If enableWhitelisting
is set to true
the if
statement below will always revert.
function _isNodeRunnerValid(address _nodeRunner) internal view returns (bool) { require(_nodeRunner != address(0), "Zero address"); if(enableWhitelisting) { require(isNodeRunnerWhitelisted[_nodeRunner] == true, "Invalid node runner"); }
Consequently, registerBLSPublicKeys()
will also fail because it calls _isNodeRunnerValid()
.
function registerBLSPublicKeys( bytes[] calldata _blsPublicKeys, bytes[] calldata _blsSignatures, address _eoaRepresentative ) external payable nonReentrant { uint256 len = _blsPublicKeys.length; require(len >= 1, "No value provided"); require(len == _blsSignatures.length, "Unequal number of array values"); require(msg.value == len * 4 ether, "Insufficient ether provided"); require(!Address.isContract(_eoaRepresentative), "Only EOA representative permitted"); require(_isNodeRunnerValid(msg.sender) == true, "Unrecognised node runner"); require(isNodeRunnerBanned(msg.sender) == false, "Node runner is banned from LSD network");
Change the following line to: https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/LiquidStakingManager.sol#L280-L281
require(isNodeRunnerWhitelisted[_nodeRunner] != isWhitelisted, "Unnecessary update to same status");
#0 - c4-judge
2022-11-21T14:01:02Z
dmvt marked the issue as duplicate of #74
#1 - c4-judge
2022-11-21T16:45:02Z
dmvt marked the issue as not a duplicate
#2 - c4-judge
2022-11-21T16:45:12Z
dmvt marked the issue as duplicate of #67
#3 - c4-judge
2022-11-30T11:44:52Z
dmvt marked the issue as satisfactory
#4 - C4-Staff
2022-12-21T00:11:17Z
JeeberC4 marked the issue as duplicate of #378
🌟 Selected for report: 0xSmartContract
Also found by: 0x4non, 0xNazgul, 0xRoxas, 0xdeadbeef0x, 0xmuxyz, 9svR6w, Awesome, Aymen0909, B2, Bnke0x0, CloudX, Deivitto, Diana, Franfran, IllIllI, Josiah, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, Secureverse, SmartSek, Trust, Udsen, a12jmx, aphak5010, brgltd, bulej93, c3phas, ch0bu, chaduke, chrisdior4, clems4ever, cryptostellar5, datapunk, delfin454000, fs0c, gogo, gz627, hl_, immeas, joestakey, lukris02, martin, nogo, oyc_109, pashov, pavankv, peanuts, pedr02b2, rbserver, rotcivegaf, sahar, sakman, shark, tnevler, trustindistrust, zaskoh, zgo
52.0338 USDC - $52.03
EOARepresentative
or EOARepresentativeOfNodeRunner
is an EOA or a smart contractA smart contract can end up being assigned as a smartWalletRepresentative
.
Such smart contract might not have the functionality to interact with LSD or funds.
function rotateEOARepresentative(address _newRepresentative) external { require(_newRepresentative != address(0), "Zero address"); require(isNodeRunnerBanned(msg.sender) == false, "Node runner is banned from LSD network"); address smartWallet = smartWalletOfNodeRunner[msg.sender]; require(smartWallet != address(0), "No smart wallet"); require(stakedKnotsOfSmartWallet[smartWallet] == 0, "Not all KNOTs are minted"); require(smartWalletRepresentative[smartWallet] != _newRepresentative, "Invalid rotation to same EOA"); // unauthorize old representative _authorizeRepresentative(smartWallet, smartWalletRepresentative[smartWallet], false); // authorize new representative _authorizeRepresentative(smartWallet, _newRepresentative, true); }
function _authorizeRepresentative( address _smartWallet, address _eoaRepresentative, bool _isEnabled ) internal { if(!_isEnabled && smartWalletRepresentative[_smartWallet] != address(0)) { // authorize the EOA representative on the Stakehouse IOwnableSmartWallet(_smartWallet).execute( address(getTransactionRouter()), abi.encodeWithSelector( ITransactionRouter.authorizeRepresentative.selector, _eoaRepresentative, _isEnabled ) ); // delete the mapping delete smartWalletRepresentative[_smartWallet]; emit RepresentativeRemoved(_smartWallet, _eoaRepresentative); } else if(_isEnabled && smartWalletRepresentative[_smartWallet] == address(0)) { // authorize the EOA representative on the Stakehouse IOwnableSmartWallet(_smartWallet).execute( address(getTransactionRouter()), abi.encodeWithSelector( ITransactionRouter.authorizeRepresentative.selector, _eoaRepresentative, _isEnabled ) ); // store EOA to the wallet mapping smartWalletRepresentative[_smartWallet] = _eoaRepresentative; emit RepresentativeAppointed(_smartWallet, _eoaRepresentative); } else { revert("Unexpected state"); } }
Add a check like the one seen here: https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/LiquidStakingManager.sol#L435-L436
require(!Address.isContract(_eoaRepresentative), "Only EOA representative permitted");
DAOAddress
transfer is unsafefunction updateDAOAddress(address _newAddress) external onlyDAO { require(_newAddress != address(0), "Zero address"); require(_newAddress != dao, "Same address"); emit UpdateDAOAddress(dao, _newAddress); dao = _newAddress; }
If dao
is set to the incorrect address, all the functionality vetted by onlyDAO
modifier will be bricked.
Please implement a two-step process.
#0 - c4-judge
2022-12-01T23:19:33Z
dmvt marked the issue as grade-b