Missing token validity check in OrderBook::emergencyWithdrawERC20 function, contract with enough balance of any valid ERC20 token can be withdrawable by owner
Description
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
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
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);
}