HardhatFoundry
30,000 USDC
View results
Submission Details
Severity: high
Valid

Module type argument is malleable in Module Enable Mode flow

Summary

The module type argument is not checked or included in the signature, allowing its malleability and allowing potential installations of a different type of module than intended.

Vulnerability Details

The Nexus smart account supports a "Module Enable Mode" flow in which a user operation may include additional information to install a module before validating a user operation in validateUserOp().

097: function validateUserOp(
098: PackedUserOperation calldata op,
099: bytes32 userOpHash,
100: uint256 missingAccountFunds
101: ) external virtual payPrefund(missingAccountFunds) onlyEntryPoint returns (uint256 validationData) {
102: address validator = op.nonce.getValidator();
103: if (!op.nonce.isModuleEnableMode()) {
104: // Check if validator is not enabled. If not, return VALIDATION_FAILED.
105: if (!_isValidatorInstalled(validator)) return VALIDATION_FAILED;
106: validationData = IValidator(validator).validateUserOp(op, userOpHash);
107: } else {
108: PackedUserOperation memory userOp = op;
109: userOp.signature = _enableMode(validator, op.signature);
110: validationData = IValidator(validator).validateUserOp(userOp, userOpHash);
111: }
112: }

When isModuleEnableMode() is true, the implementation calls _enableMode() with the address of the validator and the signature of the user operation.

161: function _enableMode(address module, bytes calldata packedData) internal returns (bytes calldata userOpSignature) {
162: uint256 moduleType;
163: bytes calldata moduleInitData;
164: bytes calldata enableModeSignature;
165:
166: (moduleType, moduleInitData, enableModeSignature, userOpSignature) = packedData.parseEnableModeData();
167:
168: _checkEnableModeSignature(
169: _getEnableModeDataHash(module, moduleInitData),
170: enableModeSignature
171: );
172: _installModule(moduleType, module, moduleInitData);
173: }

As we can see, the signature is first parsed using parseEnableModeData() which extracts the type of the module (moduleType), its init data (moduleInitData), and the inner signature corresponding to this action (enableModeSignature). It then checks if the signature is valid using _checkEnableModeSignature() and proceeds to install and setup the module using _installModule().

However, the digest used to validate the signature doesn't include the module type in its payload:

388: function _getEnableModeDataHash(address module, bytes calldata initData) internal view returns (bytes32 digest) {
389: digest = _hashTypedData(
390: keccak256(
391: abi.encode(
392: MODULE_ENABLE_MODE_TYPE_HASH,
393: module,
394: keccak256(initData)
395: )
396: )
397: );
398: }

This means that the moduleType argument is not validated using the bundled signature, allowing its malleability.

Impact

The type of a module used during the "Module Enable Mode" flow can be maliciously manipulated by third parties, allowing the installation of a different type of module than intended.

Furthermore, the logic assumes the installed module is of the validator type, since it doesn't check that a validator with the given address is installed, as opposed to the path where isModuleEnableMode() is false (line 105).

Tools Used

None.

Recommendations

Either add the module type to the signature, or verify that the module type belongs to the validator category.

Updates

Lead Judging Commences

0xnevi Lead Judge
11 months ago
0xnevi Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

adriro Submitter
10 months ago
0xnevi Lead Judge
10 months ago
adriro Submitter
10 months ago
0xnevi Lead Judge
10 months ago
0xnevi Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-enable-mode-malleable-module-type

Support

FAQs

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