OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Missing token validity check in OrderBook::emergencyWithdrawERC20 function, contract with enough balance of any valid ERC20 token can be withdrawable by owner

Missing token validity check in OrderBook::emergencyWithdrawERC20 function, contract with enough balance of any valid ERC20 token can be withdrawable by owner

Description

  • The OrderBook contract allows the owner to withdraw any ERC-20 token, as long as the contract holds a sufficient balance of that token, without verifying the token's validity.

function emergencyWithdrawERC20(address _tokenAddress, uint256 _amount, address _to) external onlyOwner {
@> if (
_tokenAddress == address(iWETH) || _tokenAddress == address(iWBTC) || _tokenAddress == address(iWSOL)
|| _tokenAddress == address(iUSDC)
) {
revert("Cannot withdraw core order book tokens via emergency function");
}
if (_to == address(0)) {
revert InvalidAddress();
}
IERC20 token = IERC20(_tokenAddress);
token.safeTransfer(_to, _amount);
emit EmergencyWithdrawal(_tokenAddress, _amount, _to);
}

Risk

  • Any non-valid and weird ERC20 token contract can transfer token to the OrderBook contract, `Owner` can withdraw the weird token from the contract.

Proof of Concept

  • Run the following command to run test test_emergencyWithdrawERC20_token1

  • Below test demonstrate any random token `token1` transfers amount to OrderBook contract and owner set tokenReciever to recieve the withdrawn tokens.

function setUp() public {
vm.startPrank(owner);
book = new OrderBook(address(weth), address(wbtc), address(wsol), address(usdc), owner);
token1 = new MockERC20();
token1.transfer(bob,1e18);
vm.stopPrank();
}
function test_emergencyWithdrawERC20_token1() public {
vm.startPrank(bob);
token1.transfer(address(book), 1e18);
vm.stopPrank();
assert(token1.balanceOf(address(book)) == 1e18);
assert(token1.balanceOf(bob) == 0);
assert(token1.balanceOf(tokenReciever) == 0);
vm.startPrank(owner);
book.emergencyWithdrawERC20(address(token1), 100, tokenReciever);
vm.stopPrank();
assert(token1.balanceOf(tokenReciever) == 100);
}

Recommended Mitigation

  • Add the token validity check in OrderBook::emergencyWithdrawERC20 function to validate the token before owner trie to withdraw the tokens.

function emergencyWithdrawERC20(address _tokenAddress, uint256 _amount, address _to) external onlyOwner {
if (
_tokenAddress == address(iWETH) || _tokenAddress == address(iWBTC) || _tokenAddress == address(iWSOL)
|| _tokenAddress == address(iUSDC)
) {
revert("Cannot withdraw core order book tokens via emergency function");
}
if (_to == address(0)) {
revert InvalidAddress();
}
+ if(!allowedSellToken[token1]){
+ revert InvalidToken();
+ }
IERC20 token = IERC20(_tokenAddress);
token.safeTransfer(_to, _amount);
emit EmergencyWithdrawal(_tokenAddress, _amount, _to);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
11 days ago
yeahchibyke Lead Judge 10 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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