Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Outcome of signature verification is not checked in `executeTransactionFromTheOutside`, any signed transactions will be executed

Summary

executeTransactionFromTheOutside does not check the outcome of signature verification. Consequently, any signed transaction can be executed through this function, not only those that were signed by the actual owner of the contract.

Vulnerability Details

executeTransactionFromTheOutside is a function that anyone can call to submit transactions for execution via the wallet. Importantly, only those submitted transactions are supposed to be executed that have been properly validated and have been signed by the owner of the wallet.

Transaction validation happens via a call to _validateTransaction which, among other things, performs signature verification and returns a bytes4 magic value that signifies the outcome of the signature verification. executeTransactionFromTheOutside, however, ignores this return value and proceeds to execute the submitted transaction irrespectively of the validity of the signature.

Essentially, any signed transactions will be executed even if the signature is not valid - provided of course, that they do not revert at other verification steps, like a balance check.

The following test demonstrates that a transaction signed with a dummy private key (i.e. not a valid signature, not a signature from the owner) will get executed by executeTransactionFromTheOutside.

// @note needs a modified helper, see below
function testZkExecuteTransactionSignedByAnyone() public {
// Arrange
address anyUser = makeAddr("anyUser");
address dest = address(usdc);
uint256 value = 0;
bytes memory functionData = abi.encodeWithSelector(ERC20Mock.mint.selector, anyUser, AMOUNT);
Transaction memory transaction =
_createUnsignedTransaction(mondrianWallet.owner(), 113, dest, value, functionData);
// Act
vm.startPrank(anyUser);
transaction = _signTransaction(transaction); // if trx is not signed at all, next line would revert with [FAIL. Reason: ECDSAInvalidSignatureLength(0)]
mondrianWallet.executeTransactionFromOutside(transaction);
vm.stopPrank();
// Assert
assertEq(usdc.balanceOf(anyUser), AMOUNT);
}
function _signTransactionWithDummyKey(Transaction memory transaction) internal view returns (Transaction memory) {
bytes32 unsignedTransactionHash = MemoryTransactionHelper.encodeHash(transaction);
uint8 v;
bytes32 r;
bytes32 s;
uint256 DUMMY_PRIVATE_KEY = 0x0;
//uint256 ANVIL_DEFAULT_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
(v, r, s) = vm.sign(DUMMY_PRIVATE_KEY, unsignedTransactionHash);
Transaction memory signedTransaction = transaction;
signedTransaction.signature = abi.encodePacked(r, s, v);
return signedTransaction;
}

Impact

Any transaction that has been signed and is otherwise valid will be executed, even if the signature is invalid (is not coming from the owner). Accordingly, malicious users can drain any and all funds from the wallet.

Tools Used

Manual review, Foundry.

Recommendations

Ensure that the outcome of the signature verification is checked and that the transaction flow procceds only if the signature is valid. Perform the following modifications:

function executeTransactionFromOutside(Transaction memory _transaction) external payable {
- _validateTransaction(_transaction);
+ bytes4 magic = _validateTransaction(_transaction);
+ if (magic != ACCOUNT_VALIDATION_SUCCESS_MAGIC) {
+ revert MondrianWallet2__InvalidSignature();
+ }
_executeTransaction(_transaction);
}
Updates

Lead Judging Commences

bube Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Missing validation in executeTransactionFromOutside

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.