Summary
A missing validation allows the installation of potentially unsupported types of fallback handlers.
Vulnerability Details
When installed, fallback handlers are configured with a specific call type. This is the same type of argument used in the execution mode of ERC-7579.
275: function _installFallbackHandler(address handler, bytes calldata params) internal virtual withRegistry(handler, MODULE_TYPE_FALLBACK) {
276: if (!IFallback(handler).isModuleType(MODULE_TYPE_FALLBACK)) revert MismatchModuleTypeId(MODULE_TYPE_FALLBACK);
277:
278: bytes4 selector = bytes4(params[0:4]);
279:
280:
281: CallType calltype = CallType.wrap(bytes1(params[4]));
This parameter is then used to determine the type of call to execute the fallback handlers in fallback()
:
072: fallback() external payable override(Receiver) receiverFallback {
073: FallbackHandler storage $fallbackHandler = _getAccountStorage().fallbacks[msg.sig];
074: address handler = $fallbackHandler.handler;
075: CallType calltype = $fallbackHandler.calltype;
076: require(handler != address(0), MissingFallbackHandler(msg.sig));
077:
078: if (calltype == CALLTYPE_STATIC) {
079: assembly {
080: calldatacopy(0, 0, calldatasize())
081:
082:
083:
084: mstore(calldatasize(), shl(96, caller()))
085:
086: if iszero(staticcall(gas(), handler, 0, add(calldatasize(), 20), 0, 0)) {
087: returndatacopy(0, 0, returndatasize())
088: revert(0, returndatasize())
089: }
090: returndatacopy(0, 0, returndatasize())
091: return(0, returndatasize())
092: }
093: }
094: if (calltype == CALLTYPE_SINGLE) {
095: assembly {
096: calldatacopy(0, 0, calldatasize())
097:
098:
099:
100: mstore(calldatasize(), shl(96, caller()))
101:
102: if iszero(call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)) {
103: returndatacopy(0, 0, returndatasize())
104: revert(0, returndatasize())
105: }
106: returndatacopy(0, 0, returndatasize())
107: return(0, returndatasize())
108: }
109: }
110: }
As we can see, the implementation only supports CALLTYPE_STATIC
and CALLTYPE_SINGLE
. However, the call type is not validated to be either of these values when the fallback handler is installed in _installFallbackHandler()
, allowing the configuration of an unsupported type.
Additionally, the implementation of fallback()
doesn't revert if calltype
is neither CALLTYPE_STATIC
nor CALLTYPE_SINGLE
, leading to a "successful" execution from the caller's perspective, even though nothing is actually executed.
Impact
Unsupported fallback handlers can be installed in the Nexus smart account. When executed, these will silently succeed.
Tools Used
None.
Recommendations
When installing a fallback handler in _installFallbackHandler()
ensure that calltype == CALLTYPE_STATIC || calltype == CALLTYPE_SINGLE
.
// Extract the call type from the provided parameters.
CallType calltype = CallType.wrap(bytes1(params[4]));
+ require(calltype == CALLTYPE_STATIC || calltype == CALLTYPE_SINGLE);