OrderBook

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

first flight

Root + Impact


// Order structure
struct Order {
address seller;
address token;
uint256 tokenAmount;
uint256 usdcAmount;
uint256 deadline; // Critical vulnerability related to this field
bool filled;
}
// Storage
mapping(uint256 => Order) public orders;
uint256 public nextOrderId;
// Core Functions
function createOrder(address token, uint256 tokenAmount, uint256 usdcAmount, uint256 deadline)
function fulfillOrder(uint256 orderId) // Vulnerable function
function cancelOrder(uint256 orderId)

Vulnerability Context:
The fulfillOrder function (lines XX-XX) lacked critical deadline validation, allowing order fulfillment after expiration. This violated the core protocol invariant that orders should only be executable before their deadline.

Fix Implementation:


// Before (vulnerable)
function fulfillOrder(uint256 orderId) external {
// ...existing checks...
// MISSING DEADLINE CHECK
// ...token transfers...
}
// After (fixed)
function fulfillOrder(uint256 orderId) external {
// ...existing checks...
require(block.timestamp <= order.deadline, "Order expired"); // Security patch
// ...token transfers...
}

Related Code Sections:

  1. Order creation sets deadlines:



function createOrder(... uint256 deadline) {
require(deadline > block.timestamp, "Invalid deadline");
// ...stores deadline in Order struct
}
  1. Cancellation handles expiration:


function cancelOrder(uint256 orderId) {
// Allows cancellation after deadline
require(block.timestamp > orders[orderId].deadline || ..., "Active order");
}

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

// Vulnerable Code Snippet (simplified)
function fulfillOrder(uint256 orderId) external {
Order storage order = orders[orderId];
require(!order.filled, "Order already filled");
require(order.seller != address(0), "Invalid order");
// ⚠️ MISSING: require(block.timestamp <= order.deadline, "Order expired");
// Proceed with token transfers...
USDC.safeTransferFrom(msg.sender, order.seller, order.usdcAmount);
IERC20(order.token).safeTransfer(msg.sender, order.tokenAmount);
order.filled = true;
}
require(block.timestamp <= order.deadline, "Order expired");

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

// Test Scenario: Attacker exploits expired order
function test_expiredOrderExploit() public {
// 1. Seller creates legitimate order
uint256 orderId = orderBook.createOrder(
address(wETH),
1 ether, // 1 wETH
2000e6, // 2000 USDC (1 ETH = $2000)
block.timestamp + 1 days
);
// 2. Fast-forward past deadline
vm.warp(block.timestamp + 2 days);
// 3. ETH price surges 50% ($2000 → $3000)
// Attacker spots expired order still active
address attacker = makeAddr("arbitrageur");
deal(address(USDC), attacker, 2000e6);
// 4. Attacker fulfills expired order
vm.prank(attacker);
orderBook.fulfillOrder(orderId);
// 5. Verify damage:
// - Seller receives only $2000 for $3000 worth of ETH
// - Attacker gains 1 ETH at 33% discount
assertEq(wETH.balanceOf(attacker), 1 ether);
assertEq(USDC.balanceOf(seller), 2000e6); // Should be 3000e6 at market
}
// OrderBook.sol - Vulnerable segment
function fulfillOrder(uint256 orderId) external {
Order storage order = orders[orderId];
require(!order.filled, "Filled");
require(order.seller != address(0), "Invalid order");
// MISSING EXPIRATION CHECK HERE //
USDC.safeTransferFrom(msg.sender, order.seller, order.usdcAmount);
IERC20(order.token).safeTransfer(msg.sender, order.tokenAmount);
order.filled = true;
}

Recommended Mitigation

diff --git a/OrderBook.sol b/OrderBook.sol
index abc123..def456 100644
--- a/OrderBook.sol
+++ b/OrderBook.sol
@@ -XXX, +XXX, @@ @@
function fulfillOrder(uint256 orderId) external {
Order storage order = orders[orderId];
require(!order.filled, "Order already filled");
require(order.seller != address(0), "Invalid order");
+ require(block.timestamp <= order.deadline, "Order expired"); // Critical security fix
USDC.safeTransferFrom(msg.sender, order.seller, order.usdcAmount);
IERC20(order.token).safeTransfer(msg.sender, order.tokenAmount);
Updates

Lead Judging Commences

yeahchibyke Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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