A malicious user can initiate an arbitrary external call in the role of other user's smart wallet, which is equivalent to stealing a smart wallet and sending a transaction with it.
This can lead to fund being stolen from the smart wallet directly, may also lead to authority, funds, function problems of other contracts.
Proof of Concept
If a smart wallet has not been deployed or is being deployed, a hacker can launch an attack by deploying the wallet directly or front-running the deployment.
The following lists some possible attack scenarios.
If a hacker wants to steal USDT from a smart wallet:
The hacker invokes the ERC20 - approve function of the smart contract wallet.
The hacker invokes the ERC20 - transferFrom function of the USDT contract to steal all the USDT from the smart wallet.
The principle of step 2 is:
The smart contract wallet itself does not implement the ERC20 - approve function, so the invoking will go into FallbackManager.sol#fallback.
Because in step 1, the hacker has already set the fallback handler to the USDT contract address when deploying the smart wallet, so eventually the smart wallet will call the approve function of USDT contract in FallbackManager.sol#fallback.
If a hacker wants to steal some ERC721 assets from a smart wallet:
Similarly, the hacker just need to set the fallback handler to the ERC721 contract address in step 1, and invokes ERC721 - setApprovalForAll or ERC721 - transferFrom in step 2.
If a smart wallet manages the assets or functions of another contract:
Similarly, the hacker just need to set the fallback handler to the target contract address in step 1, and invokes the specific function in step 2.
Lines of code
https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccountFactory.sol#L33 https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166 https://github.com/code-423n4/2023-01-biconomy/blob/53c8c3823175aeb26dee5529eeefa81240a406ba/scw-contracts/contracts/smart-contract-wallet/base/FallbackManager.sol#L32
Vulnerability details
Impact
A malicious user can initiate an arbitrary external call in the role of other user's smart wallet, which is equivalent to stealing a smart wallet and sending a transaction with it. This can lead to fund being stolen from the smart wallet directly, may also lead to authority, funds, function problems of other contracts.
Proof of Concept
If a smart wallet has not been deployed or is being deployed, a hacker can launch an attack by deploying the wallet directly or front-running the deployment.
The following lists some possible attack scenarios.
If a hacker wants to steal USDT from a smart wallet:
_handler
to the USDT contract address.ERC20 - approve
function of the smart contract wallet.ERC20 - transferFrom
function of the USDT contract to steal all the USDT from the smart wallet.The principle of step 2 is: The smart contract wallet itself does not implement the
ERC20 - approve
function, so the invoking will go into FallbackManager.sol#fallback. Because in step 1, the hacker has already set the fallback handler to the USDT contract address when deploying the smart wallet, so eventually the smart wallet will call theapprove
function of USDT contract in FallbackManager.sol#fallback.If a hacker wants to steal some ERC721 assets from a smart wallet: Similarly, the hacker just need to set the fallback handler to the ERC721 contract address in step 1, and invokes
ERC721 - setApprovalForAll
orERC721 - transferFrom
in step 2.If a smart wallet manages the assets or functions of another contract: Similarly, the hacker just need to set the fallback handler to the target contract address in step 1, and invokes the specific function in step 2.
Tools Used
Manual
Recommended Mitigation Steps
I recommend removing the input parameter
_handler
from SmartAccountFactory.sol#deployCounterFactualWallet and SmartAccountFactory.sol#deployWallet, and using a fixed verified DefaultCallbackHandler contract instead.