OrderBook

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

No token validation check in `buyOrder` function.

The contract allows any ERC20 token to be passed into createSellOrder and then blindly calls safeTransfer during buyOrder. There’s no check to ensure order.tokenToSell is valid or approved by the protocol.

Description

In the createSellOrder function, there was a check to ensure that only the accepted tokens could be used to interact with the protocol. This check need to be also be in the buyOrder function to avoid weird ERC20 token interacting with the protocol

The buyOrder function did not validate which tokens were accepted to interact with the protocol, this means fake or non-standard tokens could interact and break the functionality of the contract.

function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
@> // There should be a check for allowedSellTokens
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
order.isActive = false;
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell);
totalFees += protocolFee;
emit OrderFilled(_orderId, msg.sender, order.seller);
}

Risk

Likelihood:

The Likelihood of this vulnerability is that whenever a buyer inputs a bad/weird ERC20 contract address and interacts with the function

Impact:

it will affect the functionality or break of the protocols intentions.

Proof of Concept

  1. Create a malicious ERC20 token that always reverts when it interact with the transferFrom function

  2. Introduce the token as _tokenToSell in the createSellOrder function executed by the seller and bypass Token Filtering in createSellOrder

  3. To test buyOrder(), we temporarily comment out or bypass this validation in a test-only fork of the contract so we can create an order with a malicious token.

  4. Create and Fund Actors in the Test

  5. Approve and List Malicious Token and simulate the Buyer Interaction and Expect a Revert.

function testBuyOrderFailsDueToFakeToken() public {
vm.prank(seller);
maltoken.approve(address(book), 100 ether);
book.createSellOrder(
address(maltoken),
10 ether,
50 ether, // price in fake "USDC"
2 days
);
vm.stopPrank();
vm.startPrank(buyer);
usdc.approve(address(book), type(uint256).max);
vm.expectRevert("This token is non-transferable");
book.buyOrder(1);
vm.stopPrank();
}

Recommended Mitigation

To prevent malicious tokens from being used in the buyOrder() flow, the contract should enforce strict validation of tokenToSell against a trusted token whitelist. Specifically, insert the following check at the beginning of the buyOrder() function

+ if (!allowedSellToken[_tokenToSell]) revert InvalidToken();
Updates

Lead Judging Commences

yeahchibyke Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

gustavoo4eva Submitter
5 months ago
yeahchibyke Lead Judge
5 months ago
yeahchibyke Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!