Abundant Alabaster Toad
High
In DebitaLendOffer-Implementation.sol
and DebitaBorrowOffer-Implementation.sol
.
Removing active order right after external call will allow attacker to remove their own order twice through reentrancy attack.
Under several attack path below, some active orders will be removed from active order list despite still active.
Standard reentrancy attack pattern. Change after unsafe external call.
- In
DLOImplementation.acceptLendingOffer()
andDBOImplementation.acceptBorrowOffer()
, delete order called after unsafe token transfer. https://github.com/sherlock-audit/2024-11-debita-finance-v3/blob/main/Debita-V3-Contracts/contracts/DebitaLendOffer-Implementation.sol#L121-L126 https://github.com/sherlock-audit/2024-11-debita-finance-v3/blob/main/Debita-V3-Contracts/contracts/DebitaBorrowOffer-Implementation.sol#L150-L162 - During creation, user can use any token and any NFT. There is no address check. Here1. Here2
- This open up reentrancy during token transfer right before calling factory to delete Buy Order. Here
- To trigger delete twice, attacker just have to make sure
lendInformation.availableAmount == 0
. Here. This is doable with reentrancy attack and cancel order DebitaLendOfferFactory.deleteOrder()
can be called with same address twice. Delete same address twice will also remove active order at index 0. https://github.com/sherlock-audit/2024-11-debita-finance-v3/blob/main/Debita-V3-Contracts/contracts/DebitaLendOfferFactory.sol#L207-L220
DebitaLendOfferFactory.sol
have several active lending orders.activeOrdersCount > 1
- Assuming Attacker prepare lend order, borrow order so Aggregator contract accept borrow,lending offer include a malicous ERC20/ERC721 token as principle token.
- Attacker can freely call
acceptLendingOffer()
andacceptBorrowOffer()
with their own order through AggregatorV3. - Custom ERC20, ERC721 can reentrancy at specific point, right before delete order. Here
This demonstrate only Lend Order, Borrow Order have similar attack path.
To delete same order twice. You need to follow these basic steps:
- attacker call
DLOFactory.createLendOrder()
to create order with custom token address, with lending amount = 100e18 - attacker craft Aggregator instruction to call order implementation
DLOImplementation.acceptLendingOffer()
with half amount 50e18. - During first call,
availableAmount
is reduced by 50e18. Here - After
lendInformation.availableAmount -= amount;
, the available amount still 50e18lendInformation.availableAmount = 50e18
. - During token transfer phase, reentrancy call
cancelOffer()
immediately. Here - Because
lendInformation.availableAmount > 0
still true, with 50e18 availableAmount. The call only require owner to manually call. This is doable for attacker. - Cancel order reset availableAmount to zero.
lendInformation.availableAmount = 0
DLOImplementation.cancelOffer()
now delete order first time.- Exit reentrancy call. Back to previous Token transfer reentrancy point
DLOImplementation.acceptLendingOffer()
. - The check
if (lendInformation.availableAmount == 0)
read from storage directly, whichavailableAmount
will be zero here. - Delete order is called second time.
- Factory will try to delete order with zero index, which is not attacker order.
First delete, remove attacker order. Second delete, remove user order at index 0. Order before attacker will moved from last place to first place (index 0).
Repeat attack path above multiple times, attacker can remove entire active order list.
This list is required to allow web user access their active order. Without this list, user will have to manually find their order contract address through events. Which is unlikely, leads to reputation lost and website operation down until fixed.
Move all external calls (Token transfer) to the end of function.