Skip to content
This repository has been archived by the owner on Apr 28, 2024. It is now read-only.

0x52 - Creditor can maliciously burn UniV3 position to permanently lock funds #78

Open
sherlock-admin2 opened this issue Oct 23, 2023 · 2 comments
Assignees
Labels
Has Duplicates A valid issue with 1+ other issues describing the same vulnerability High A valid High severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed

Comments

@sherlock-admin2
Copy link
Contributor

sherlock-admin2 commented Oct 23, 2023

0x52

high

Creditor can maliciously burn UniV3 position to permanently lock funds

Summary

LP NFT's are always controlled by the lender. Since they maintain control, malicious lenders have the ability to burn their NFT. Once a specific tokenID is burned the ownerOf(tokenID) call will always revert. This is problematic as all methodologies to repay (even emergency) require querying the ownerOf() every single token. Since this call would revert for the burned token, the position would be permanently locked.

Vulnerability Detail

NonfungiblePositionManager

function ownerOf(uint256 tokenId) public view virtual override returns (address) {
    return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}

When querying a nonexistent token, ownerOf will revert. Now assuming the NFT is burnt we can see how every method for repayment is now lost.

LiquidityManager.sol#L306-L308

        address creditor = underlyingPositionManager.ownerOf(loan.tokenId);
        // Increase liquidity and transfer liquidity owner reward
        _increaseLiquidity(cache.saleToken, cache.holdToken, loan, amount0, amount1);

If the user is being liquidated or repaying themselves the above lines are called for each loan. This causes all calls of this nature to revert.

LiquidityBorrowingManager.sol#L727-L732

    for (uint256 i; i < loans.length; ) {
        LoanInfo memory loan = loans[i];
        // Get the owner address of the loan's token ID using the underlyingPositionManager contract.
        address creditor = underlyingPositionManager.ownerOf(loan.tokenId);
        // Check if the owner of the loan's token ID is equal to the `msg.sender`.
        if (creditor == msg.sender) {

The only other option to recover funds would be for each of the other lenders to call for an emergency withdrawal. The problem is that this pathway will also always revert. It cycles through each loan causing it to query ownerOf() for each token. As we know this reverts. The final result is that once this happens, there is no way possible to close the position.

Impact

Creditor can maliciously lock all funds

Code Snippet

LiquidityBorrowingManager.sol#L532-L674

Tool used

Manual Review

Recommendation

I would recommend storing each initial creditor when a loan is opened. Add try-catch blocks to each ownerOf() call. If the call reverts then use the initial creditor, otherwise use the current owner.

@fann95
Copy link

fann95 commented Oct 31, 2023

Fixed: RealWagmi/wagmi-leverage@788c72b

@IAm0x52
Copy link

IAm0x52 commented Nov 17, 2023

Fix looks good. Burned tokens no longer cause a revert and tokens are sent to borrower as profit

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Has Duplicates A valid issue with 1+ other issues describing the same vulnerability High A valid High severity issue Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed
Projects
None yet
Development

No branches or pull requests

3 participants