Platform: Code4rena
Start Date: 04/01/2023
Pot Size: $60,500 USDC
Total HM: 15
Participants: 105
Period: 5 days
Judge: gzeon
Total Solo HM: 1
Id: 200
League: ETH
Rank: 90/105
Findings: 1
Award: $36.50
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0xAgro, 0xdeadbeef0x, 0xhacksmithh, 2997ms, Atarpara, Bnke0x0, Diana, HE1M, IllIllI, Josiah, Kalzak, Lirios, MalfurionWhitehat, MyFDsYours, Raiders, RaymondFam, Rolezn, SaharDevep, Sathish9098, Udsen, Viktor_Cortess, adriro, ast3ros, betweenETHlines, btk, chaduke, chrisdior4, cryptostellar5, csanuragjain, giovannidisiena, gz627, hl_, horsefacts, joestakey, juancito, ladboy233, lukris02, nadin, oyc_109, pauliax, peanuts, prady, sorrynotsorry, zaskoh
36.5015 USDC - $36.50
The execTransaction
does not verify that the parameter signature
length is a valid signature length or not, as the signature param consists of 32 bytes s
and 32 bytes r
and 1 byte of v,
which means the signature length should be equal to 65.
Add this before line SmartAccount-L198
contracts/smart-contract-wallet/SmartAccount: require(_signature.length == 65, "invalid signature length");
The init()
function can be called by anybody when the contract is not initialized.
function init( address _owner, address _entryPointAddress, address _handler ) public override initializer { require(owner == address(0), "Already initialized"); require(address(_entryPoint) == address(0), "Already initialized"); require(_owner != address(0), "Invalid owner"); require(_entryPointAddress != address(0), "Invalid Entrypoint"); require(_handler != address(0), "Invalid Entrypoint"); owner = _owner; _entryPoint = IEntryPoint(payable(_entryPointAddress)); if (_handler != address(0)) internalSetFallbackHandler(_handler); setupModules(address(0), bytes("")); }
More importantly, if someone else runs this function, they will have full authority as it sets the owner
variable.
Add a check that init()
can only be called by the deployer.
address internal immutable deployer; constructor() { deployer = msg.sender; } ... function init(...) { if (msg.sender != DEPLOYER_ADDRESS) { revert NotDeployer(); } ... // rest same }
The package.json configuration file says that the project is using4.7.3 of OpenZeppelin, which is not the latest version
package.json "@openzeppelin/contracts": "^4.7.3", "@openzeppelin/contracts-upgradeable": "^4.7.3",
Instead of calling getChainId()
function to get the current chain id, use an immutable variable to store the chain-id into the bytecode.
unit immutable public chainId; init(...) { assembly { chainId := chainid() } }
Line SmartAccount.sol#L310 defines variable i
to pass to function checkSignature
, instead constant value can be used as variable i
remains unchanged.
contracts/smart-contract-wallet/SmartAccount.sol/checkSignatures(): (v, r, s) = signatureSplit(signatures, 0);
Several critical operations do not trigger events. As a result, it isn't easy to check the behavior of the contracts. Without events, users and blockchain-monitoring systems cannot easily detect suspicious behavior. Ideally, the following critical operations should trigger events:
addDeposit()
withdrawDepositTo()
transfer()
pullTokens()
execute()
executeBatch()
execFromEntryPoint ()
Usually, lines in source code are limited to 80 characters. Reference: https://docs.soliditylang.org/en/v0.8.10/style-guide.html#maximum-line-length
contracts/smart-contract-wallet/SmartAccount: function execFromEntryPoint(address dest, uint value, bytes calldata func, Enum.Operation operation, uint256 gasLimit) external onlyEntryPoint returns (bool success) {
#0 - c4-judge
2023-01-22T15:24:23Z
gzeon-c4 marked the issue as grade-b
#1 - c4-sponsor
2023-02-09T12:40:54Z
livingrockrises marked the issue as sponsor confirmed