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.
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
Why every order can be cancelled once orderId reaches 65000.
Cancelling will enable once s.asset[asset].orderId will reach 65000
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
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:
Create limit bidOrder with for example 10 ETH amount, set price low enough to not match sell orders
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
Repeat steps 1 - 2 at most 65000 times
Economic analysis of attack.
Approximation of cost for such attack:
There needs to be at most 65000 orders created.
Create bid costs 98000 gas at the worst case
Create ask costs 92000 gas at the worst case
Current gas price is 6 GWEI
(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
Malicious user will be able to keep orderbook empty forever.
Manual Review
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
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.