All UserOperations
provided by EntryPoint
will succeed in MondrianWallet.validateUserOp()
because it always returns SIG_VALIDATION_SUCESS
, even if the provided UserOperation
signature was created by accounts other than the owner of the wallet.
As per ERC-4337, bundlers collect UserOperation
s which are then packed and ultimately sent to the EntryPoint
contract. The EntryPoint
will then validate each UserOperation
against the sender account (the smart account). To do that, smart accounts have to implement validateUserOp()
, which has to return with a success or failure signal, depending on whether the given UserOperation
had a valid signature.
If the provided signature is not valid, validateUserOp()
needs to return an error code (1
) as uint256
.
MondrianWallet
already makes use of a SIG_VALIDATION_SUCESS
constant to signal a successful validation, however it doesn't take the failure case into account.
In fact, it doesn't even check the return value when trying to recover the signer's address. This means, any UserOperation
that is sent to validateUserOp()
will be considered valid, allowing malicious actors to execute any function on any contract via MondrianWallet
by crafting and sending a dedicated UserOperation
to the network.
The implementation of MondrianWallet.validateUserOp()
can be found here, which will call _validateSignature()
. Whatever _validateSignature()
returns is the return value for validateUserOp()
.
Here's a code snippet from the source:
Notice in the code above that there's no check on ECDSA.recover()
. ECDSA.recover()
returns the address of the account that created the signature. According to the protocol's description, only the owner of the wallet should be able to execute UserOperation
s on the smart account. Since there's no check, any signature will be valid. It's also very obvious that this function always returns SIG_VALIDATION_SUCCESS
.
Any account can execute transactions on behalf of the smart account. This includes functionality provided by the wallet itself (like minting Mondrian paintings), as well as any external contracts and functions that are specified in the UserOperation
.
In the worst case, this can cause loss of user funds and ownership.
Manual review
Foundry to implement test case
Ensure the provided signature is properly validated by checked if the signer of a given UserOperation
is indeed the owner of the smart account.
Attacker: Any account other than the owner()
of the smart account
Victim: The owner()
or deployer of the smart account
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.