HardhatFoundry
30,000 USDC
View results
Submission Details
Severity: low
Invalid

K1Validator does not check and update the nonce of the Account , which allow signature replay attacks , which leads to drain all the funds of the account

https://github.com/Cyfrin/2024-07-biconomy/blob/9590f25cd63f7ad2c54feb618036984774f3879d/contracts/modules/validators/K1Validator.sol#L84-L93

Summary

The K1Validator is responsible for validating userOps by invoking the validateUserOp function. However, the validator does not verify the nonce of the accounts, making it vulnerable to signature replay attacks. An attacker can reuse the same PackedUserOperation multiple times, resulting in the draining of all funds from the affected account.

Vulnerability Details

In the K1Validator, the validateUserOp function is intended to ensure that the owner of the account is the signer of the userOp. This function employs SignatureCheckerLib to verify the signer. However, it comes with a critical warning:

/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.

The validateUserOp function must verify that the nonce in the userOp matches the current nonce of the account. Subsequently, the nonce should be updated to prevent replay attacks. Failure to perform this verification allows attackers to submit a signed userOp repeatedly, thus exploiting the vulnerability.
as shown here :

function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external view returns (uint256) {
address owner = smartAccountOwners[userOp.sender];
if (
-> owner.isValidSignatureNow(ECDSA.toEthSignedMessageHash(userOpHash), userOp.signature) ||
-> owner.isValidSignatureNow(userOpHash, userOp.signature)
) {
return VALIDATION_SUCCESS;
}
return VALIDATION_FAILED;
}

the function validateUserOp , only checks that the message got signed by the owner , and does not validate the nonce

Impact

A malicious user can exploit this vulnerability by submitting a signed userOp that transfers tokens to their account. By repeating this process, they can drain all the funds from the compromised account.

Tools Used

vscode , manual review

Recommendations

  1. Nonce Verification: Ensure that the validateUserOp function checks the nonce in the userOp against the current nonce of the account.

  2. Nonce Update: After a successful validation, update the nonce to prevent reuse.

consider add a mapping called accountToNonce which stores the nonce of each account that has installed the K1Validator

function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external view returns (uint256) {
address owner = smartAccountOwners[userOp.sender];
+ require (userOp.nonce == accountToNonce[userOp.sender]);
if (
owner.isValidSignatureNow(ECDSA.toEthSignedMessageHash(userOpHash), userOp.signature) ||
owner.isValidSignatureNow(userOpHash, userOp.signature)
) {
return VALIDATION_SUCCESS;
}
return VALIDATION_FAILED;
}

Implementing these measures will safeguard against signature replay attacks and secure the funds within the accounts.

Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

finding-validateUserOp-nonce

Invalid, `validateUserOp` can only be called via the `EntryPoint` contract, wherein the [nonce is appropriately updated and checked](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L650-L652)

Support

FAQs

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