Summary
Lack of Checks in executeTransactionFromOutside
function in contract allows anyone to interact with wallet and drain available funds or call other contract.
Vulnerability Details
relavant link - https://github.com/Cyfrin/2024-07-Mondrian-Wallet_v2/blob/main/src/MondrianWallet2.sol#L96-L99
function executeTransactionFromOutside(Transaction memory _transaction) external payable {
_validateTransaction(_transaction);
_executeTransaction(_transaction);
}
Above function take input, pass it to _validateTransaction
then call _executeTransaction
. If we check _validateTransaction
function given below -
function _validateTransaction(Transaction memory _transaction) internal returns (bytes4 magic) {
SystemContractsCaller.systemCallWithPropagatedRevert(
uint32(gasleft()),
address(NONCE_HOLDER_SYSTEM_CONTRACT),
0,
abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce))
);
uint256 totalRequiredBalance = _transaction.totalRequiredBalance();
if (totalRequiredBalance > address(this).balance) {
revert MondrianWallet2__NotEnoughBalance();
}
bytes32 txHash = _transaction.encodeHash();
address signer = ECDSA.recover(txHash, _transaction.signature);
bool isValidSigner = signer == owner();
if (isValidSigner) {
magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC;
} else {
magic = bytes4(0);
}
return magic;
}
it is meant to check and verify the signature of the owner and returns bytes4 magic
value accordingly. But this value is not being checked in executeTransactionFromOutside
and _executeTransaction
is being triggered. That will allow anybody to call functions on the behalf of owner and do whatever they want with user funds.
POC
Impact
Loss of funds
Tools Used
Manual Review, Foundry
Recommendations
Here is the recommendation to fix the code -
function executeTransactionFromOutside(Transaction memory _transaction) external payable {
- _validateTransaction(_transaction);
+ bytes4 magic = _validateTransaction(_transaction);
+ if (magic != ACCOUNT_VALIDATION_SUCCESS_MAGIC) {
+ revert MondrianWallet2__InvalidSignature();
+ }
_executeTransaction(_transaction);
}