OrderBook

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

Centralized governance of allowed tokens

Description

The contract enforces a list of allowed tokens via a mapping(address => bool) but this list is fully controlled by a centralized owner address, with no governance, timelock, or multisig restrictions.

@> addAllowedToken() and removeAllowedToken() functions are callable only by the owner.

This means a risk of centralized abuse or mismanagement, especially in live environments where token trust and user confidence are critical. A malicious owner can:

  • Add malicious tokens to the system

  • Remove valid tokens arbitrarily

  • Disrupt trading for users

While this is not a technical vulnerability, this design flaw can create trust issues and exposes the protocol to external governance criticism or rug pull accusations.


Risk


Likelihood:

  • This happens anytime token management decisions are left to a single address without review.

  • Highly likely in low-audited protocols or during active governance changes.


Impact:

  • Unexpected interruption of protocol availability

  • Loss of user trust

  • Integration of malicious ERC20 tokens

  • Negative reputation or legal risks


Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOrderBook {
function addAllowedToken(address token) external;
function removeAllowedToken(address token) external;
function allowedTokens(address token) external view returns (bool);
}
contract MaliciousOwnerExploit {
IOrderBook public orderbook;
address public fakeToken;
address public usdc;
constructor(address _orderbook, address _fakeToken, address _usdc) {
orderbook = IOrderBook(_orderbook);
fakeToken = _fakeToken; // Could be a broken or malicious ERC20
usdc = _usdc; // Legit token commonly used by users
}
function executeExploit() external {
// As contract owner:
// Step 1: Add a malicious token that reverts or returns fake decimals
orderbook.addAllowedToken(fakeToken);
// Step 2: Remove a critical token (example, USDC) from the allowed list
orderbook.removeAllowedToken(usdc);
}
function simulateUserImpact() external view returns (bool, bool) {
// Simulate a user's view:
bool isFakeAllowed = orderbook.allowedTokens(fakeToken);
bool isUSDCAllowed = orderbook.allowedTokens(usdc);
return (isFakeAllowed, isUSDCAllowed); // Returns: (true, false)
}
}


Recommended Mitigation


Implement access control via governance mechanisms such as:

A Timelock

// Use OpenZeppelin TimelockController to delay allowlist changes
// Give time for community or multisig to respond

or a Multisig

// Wrap add/removeAllowedToken() behind a Gnosis Safe multisig or DAO role
// Prevent unilateral control

These changes enhance transparency and minimize the risk of incorrect use.

- remove this code
function addAllowedToken(address token) external onlyOwner {
allowedTokens[token] = true;
}
function removeAllowedToken(address token) external onlyOwner {
allowedTokens[token] = false;
}
+ add this code
// Add governance interface for allowlist management
function addAllowedToken(address token) external onlyGovernance {
allowedTokens[token] = true;
}
function removeAllowedToken(address token) external onlyGovernance {
allowedTokens[token] = false;
}
modifier onlyGovernance() {
require(msg.sender == governance || isAuthorizedMultisig(msg.sender), "Not authorized");
_;
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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