DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: high
Valid

Every order can be cancelled once `asset.orderId` will hit 65000

Summary

There is possibility to cancel last order when there are too many orders in orderbook. However wrong variable is used to track number of open orders.
Possibility to cancel orders opens when asset.orderId hits 65000. It will be hitted when there are at least 65000 open orders and 0 cancelled/matched orders (because of orderId re-use). But asset.orderId states that ONCE this number of open orders was reached, because it never decreases.

Here is vulnerability: everyone can cancel any last order, and thus remove all the orders from orderbook. And can keep orderbook empty forever.

Vulnerability Details

Explanation consists of 2 parts: 1) why every order can be cancelled once orderId reaches 65000; 2) How attacker can create dust order at almost no cost; 3) Economic analysis of attack

  1. Why every order can be cancelled once orderId reaches 65000.

Cancelling will enable once s.asset[asset].orderId will reach 65000

function cancelOrderFarFromOracle(
address asset,
O orderType,
uint16 lastOrderId,
uint16 numOrdersToCancel
) external onlyValidAsset(asset) nonReentrant {
@> if (s.asset[asset].orderId < 65000) {
revert Errors.OrderIdCountTooLow();
}
if (numOrdersToCancel > 1000) {
revert Errors.CannotCancelMoreThan1000Orders();
}
... *performs cancel of order*
}

But this value never decreases, only increases on adding new order when there are no cancelled/matched orderIds left
https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/libraries/LibOrders.sol#L233

  1. How attacker can create dust order at almost no cost.

According to issue with title "Order submission can left opposite order with dust amount", attacker has ability to create such orders, increasing s.asset[asset].orderId to 65000

Attack scenario:

  1. Create limit bidOrder with for example 10 ETH amount, set price low enough to not match sell orders

  2. Create market askOrder with amount 10 ETH - 1 wei, set price of bidOrder from step 1. As a result attacker will get back 10 ETH - 1 wei (he is owner of matched bid order), and leave bidOrder with 1 wei in orderBook

  3. Repeat steps 1 - 2 at most 65000 times

  4. Economic analysis of attack.

Approximation of cost for such attack:

  1. There needs to be at most 65000 orders created.

  2. Create bid costs 98000 gas at the worst case

  3. Create ask costs 92000 gas at the worst case

  4. Current gas price is 6 GWEI

  5. (98000 + 92000) * 65000 * 6 GWEI = 78_000_000_000 GWEI = 74.1 ETH = 122k USD.
    At price 122k USD protocol will be colluded with dust orders. AND protocol won't be able to operate normally because anyone can remove last orders and therefore to keep orderbook empty

Impact

Malicious user will be able to keep orderbook empty forever.

Tools Used

Manual Review

Recommendations

Introduce new variable to struct Asset to keep track of current number of open orders. And use it in function cancelOrderFarFromOracle() instead of variable s.asset[asset].orderId

Updates

Lead Judging Commences

0xnevi Lead Judge about 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-436

Support

FAQs

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