LyraDepositWrapper
Total Losses
$1.0M+
Date
Network
Categories
data validation bridgesStep-by-step
This exploit was a combination of user error and a vulnerability in the LyraDepositWrapper contract. A MEV bot was able to drain $1 million in USDC after a user accidentally transferred the funds directly to the contract address instead of using its intended deposit function.
- User Error (Pre-condition): A user, funded from the FalconX exchange, mistakenly sent $1,000,000 USDC directly to the
LyraDepositWrappercontract address. This action did not trigger any contract logic and left the funds in the contract’s balance. - Vulnerability Discovery: A MEV bot detected this balance in the contract.
- Exploitation: The bot called the
depositToLyra()function with:amount:0socketVault: The attacker’s own address.
- Arbitrary Approval: The function’s logic bypassed the initial token transfer because the amount was zero, but proceeded to grant an unlimited USDC approval to the attacker’s address (
socketVault). - Draining Funds: With the approval granted, the attacker immediately called
transferFromon the USDC contract to pull the entire $1M balance from theLyraDepositWrapperto their own wallet.
Detailed Description
The vulnerability existed within the depositToLyra function, which lacked validation checks. The function was designed to transfer tokens from a user, approve a socketVault for bridging, and then initiate the deposit.
function depositToLyra(
address token,
address socketVault,
bool isSCW,
uint256 amount,
uint256 gasLimit,
address connector
) external payable {
// If `amount` is 0, transferFrom does not revert.
IERC20(token).transferFrom(msg.sender, address(this), amount);
// The `socketVault` parameter is not validated and can be any address.
IERC20(token).approve(socketVault, type(uint256).max);
address recipient = _getL2Receiver(isSCW);
ISocketVault(socketVault).depositToAppChain{value: msg.value}(recipient, amount, gasLimit, connector);
}
LyraDepositWrapper.sol
The attack was possible due to unvalidated approval, The function immediately proceeded to IERC20(token).approve(socketVault, type(uint256).max). Since the socketVault parameter was controlled by the caller and not validated against a whitelist of trusted addresses, the attacker could simply provide their own address.
LYRA_DEPOSIT_WRAPPER.depositToLyra{value: 0}(
address(USDC),
ATTACKER, // socketVault: The address to grant approval to.
false,
0, // amount: Bypasses the transferFrom.
1,
address(WETH)
);
LyraDepositWrapper.attack.sol
The final step was a simple transferFrom call to drain the $1 million that the user had mistakenly deposited.
Possible mitigations
-
Validating Inputs: Implement strict checks to validate the
socketVaultand token addresses are trusted, and requireamount > 0. This prevents both arbitrary approval targets and the zero-amount bypass. -
Use Scoped Approvals: Replace infinite approvals with approvals for only the specific amount being deposited or reset to zero at the end of the call.