OrderBook

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

Insufficient Token Validation in setAllowedSellToken Function

Insufficient Token Validation in setAllowedSellToken Function

Description

The setAllowedSellToken function allows the owner to add or remove tokens from the allowed sell token whitelist. However, the function only performs basic validation by checking if the token address is not null and not the USDC token itself.

The function lacks comprehensive validation to ensure that the added token is a legitimate ERC20 token with safe characteristics. This could allow malicious or problematic tokens to be added to the whitelist, potentially leading to various security issues.

// Root cause in the codebase with @> marks to highlight the relevant section
function setAllowedSellToken(address _token, bool _isAllowed) external onlyOwner {
if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken(); // Cannot allow null or USDC itself
@> allowedSellToken[_token] = _isAllowed; // No validation of token legitimacy
emit TokenAllowed(_token, _isAllowed);
}

Risk

Likelihood:

  • The owner could accidentally or maliciously add tokens with dangerous callbacks (like ERC-777 tokens) that could lead to reentrancy attacks

  • The owner could add tokens with non-standard decimals or malicious transfer logic that could break the order book functionality

  • The owner could add tokens with pause functionality that could be exploited to freeze user funds

Impact:

  • Reentrancy attacks could drain funds from the contract if malicious tokens with callbacks are added

  • Order book operations could fail or behave unexpectedly if tokens with non-standard behavior are added

  • Users could lose access to their funds if pauseable tokens are added and later paused

Proof of Concept

The following example demonstrates how a malicious token with dangerous callbacks could be added to the whitelist and potentially exploited. The malicious token implements a callback mechanism in its transferFrom function that could trigger reentrancy attacks when users interact with the order book.

// Malicious token with dangerous callback
contract MaliciousToken is IERC20 {
bool public callbackTriggered = false;
function transferFrom(address from, address to, uint256 amount) external override returns (bool) {
if (!callbackTriggered) {
callbackTriggered = true;
// Reentrancy attack: call back into the order book
OrderBook(msg.sender).buyOrder(1); // This could cause issues
}
return true;
}
// Other ERC20 functions...
}
// Owner adds malicious token
orderBook.setAllowedSellToken(address(maliciousToken), true);
// User creates order with malicious token
orderBook.createSellOrder(address(maliciousToken), 1000, 1000, 1 days);
// This could trigger reentrancy during transferFrom

Recommended Mitigation

To prevent malicious tokens from being added to the whitelist, the function should implement comprehensive validation checks. The mitigation includes validating that the token is a legitimate ERC20, checking for reasonable decimals, and testing for dangerous callbacks that could lead to reentrancy attacks.

- function setAllowedSellToken(address _token, bool _isAllowed) external onlyOwner {
- if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken();
- allowedSellToken[_token] = _isAllowed;
- emit TokenAllowed(_token, _isAllowed);
- }
+ function setAllowedSellToken(address _token, bool _isAllowed) external onlyOwner {
+ if (_token == address(0) || _token == address(iUSDC)) revert InvalidToken();
+
+ // Validate token is a legitimate ERC20
+ try IERC20(_token).totalSupply() returns (uint256) {
+ // Check decimals are reasonable (between 0 and 18)
+ try IERC20Metadata(_token).decimals() returns (uint8 decimals) {
+ if (decimals > 18) revert InvalidToken();
+ } catch {
+ // If decimals() not available, assume standard 18
+ }
// Check for dangerous callbacks by testing transfer
try IERC20(_token).transfer(address(0), 0) returns (bool) {
// Transfer succeeded, token seems safe
} catch {
revert InvalidToken(); // Token has dangerous callbacks
}
} catch {
revert InvalidToken(); // Not a valid ERC20
}
allowedSellToken[_token] = _isAllowed;
emit TokenAllowed(_token, _isAllowed);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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