Attacker can sell a position which is pending to close.
Title
Attacker can sell a position which is pending to close.
Summary
DelayedOrder.sol#announceLeverageClose function lock the position to close.
But an attacker can unlock the locked position by calling LimitOrder.sol#cancelLimitOrder function.
Thus attacker can sell a position which is pending to close.
Vulnerability Detail
DelayedOrder.sol#announceLeverageClose function is the following.
function announceLeverageClose(uint256 tokenId, uint256 minFillPrice, uint256 keeperFee) external whenNotPaused {
......
// Lock the NFT belonging to this position so that it can't be transferred to someone else.
// Locking doesn't require an approval from the leverage trader.
leverageModule.lock(tokenId);
......
}
From comment, we can see that the token which is pending to close should not be transferred to someone else.
On the other hand, LimitOrder.sol#cancelLimitOrder function is the following.
function cancelLimitOrder(uint256 tokenId) external {
......
// Unlock the ERC721 position NFT to allow for transfers.
ILeverageModule(vault.moduleAddress(FlatcoinModuleKeys._LEVERAGE_MODULE_KEY)).unlock(tokenId);
......
}
So an attacker can unlock the position which is pending to close and sell it to someone else.
Example:
Suppose that user1(attacker) owns tokenId1 position.
Attacker set tokenId1 as pending to LimitClose by calling LimitOrder.sol#announceLimitOrder function.
Next attacker set tokenId1 as pending to LeverageClose by calling DelayedOrder.sol#announceLeverageClose function.
Next attacker unlock tokenId1 by calling LimitOrder.sol#cancelLimitOrder function.
Next attacker sell tokenId1 to user2.
After a short time, the pending order with LeverageClose type is executed by keeper and the margin of the position are sent to user1(attacker).
Impact
Attacker can sell a position which is pending to close.
Thus the buyer of the position will lose fund deposited to buy the position.
dany.armstrong90
high
Attacker can sell a position which is pending to close.
Title
Attacker can sell a position which is pending to close.
Summary
DelayedOrder.sol#announceLeverageClose
function lock the position to close. But an attacker can unlock the locked position by callingLimitOrder.sol#cancelLimitOrder
function. Thus attacker can sell a position which is pending to close.Vulnerability Detail
DelayedOrder.sol#announceLeverageClose
function is the following.From comment, we can see that the token which is pending to close should not be transferred to someone else.
On the other hand,
LimitOrder.sol#cancelLimitOrder
function is the following.So an attacker can unlock the position which is pending to close and sell it to someone else.
Example:
user1
(attacker) ownstokenId1
position.tokenId1
as pending toLimitClose
by callingLimitOrder.sol#announceLimitOrder
function.tokenId1
as pending toLeverageClose
by callingDelayedOrder.sol#announceLeverageClose
function.tokenId1
by callingLimitOrder.sol#cancelLimitOrder
function.tokenId1
touser2
.LeverageClose
type is executed by keeper and the margin of the position are sent touser1
(attacker).Impact
Attacker can sell a position which is pending to close. Thus the buyer of the position will lose fund deposited to buy the position.
Code Snippet
https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/LimitOrder.sol#L94
Tool used
Manual Review
Recommendation
We recommend that check if the position is already locked when locking a position.
Duplicate of #48