Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PositionAction.sol#onCreditFlashLoan may have leftover tokens after conducting leverParams.auxSwap. #87

Open
c4-bot-6 opened this issue Aug 15, 2024 · 2 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working M-33 primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_46_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards selected for report This submission will be included/highlighted in the audit report sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality

Comments

@c4-bot-6
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2024-07-loopfi/blob/main/src/proxy/PositionAction.sol#L512

Vulnerability details

Impact

PositionAction.sol#onCreditFlashLoan may have leftover tokens after conducting leverParams.auxSwap.

Bug Description

First, let's inspect how deposit decreaseLever with swap enabled works:

  1. Borrow loans from flashLender.
  2. Repays CDPVault debt with the borrowed loans.
  3. Withdraws collateral from CDPVault.
  4. Conducts a leverParams.primarySwap and swap collateral to debt token.

Now, step 4 is an EXACT_OUT swap, since it is forced to swap the exact amount of debt tokens used to repay the flashloan. However, after step 4, there may be some collateral tokens left, which is the residualAmount.

If leverParams.auxSwap is not enabled, the collateral token is simply sent back to the recipient. However, if leverParams.auxSwap is enabled, a swap if performed.

The issue here is, the leverParams.auxSwap swap is an EXACT_IN swap, and user would hardcode the amount of inTokens used for this swap. There is no way to know the exact amount of collateral tokens left after step 4, so there must still be some collateral tokens leftover after the leverParams.auxSwap.

These leftover tokens are not sent to anybody, and stuck in the contract.

    function decreaseLever(
        LeverParams calldata leverParams,
        uint256 subCollateral,
        address residualRecipient
    ) external onlyDelegatecall {
        // validate the primary swap
        if (leverParams.primarySwap.swapType != SwapType.EXACT_OUT || leverParams.primarySwap.recipient != self)
            revert PositionAction__decreaseLever_invalidPrimarySwap();

        // validate aux swap if it exists
>       if (leverParams.auxSwap.assetIn != address(0) && (leverParams.auxSwap.swapType != SwapType.EXACT_IN))
            revert PositionAction__decreaseLever_invalidAuxSwap();
        ...
    }

    function onCreditFlashLoan(
        address /*initiator*/,
        uint256 /*amount*/,
        uint256 /*fee*/,
        bytes calldata data
    ) external returns (bytes32) {
        if (msg.sender != address(flashlender)) revert PositionAction__onCreditFlashLoan__invalidSender();
        (
            LeverParams memory leverParams,
            uint256 subCollateral,
            address residualRecipient
        ) = abi.decode(data,(LeverParams, uint256, address));

        uint256 subDebt = leverParams.primarySwap.amount;

        underlyingToken.forceApprove(address(leverParams.vault), subDebt);
        // sub collateral and debt
        ICDPVault(leverParams.vault).modifyCollateralAndDebt(
            leverParams.position,
            address(this),
            address(this),
            0,
            -toInt256(subDebt)
        );

        // withdraw collateral and handle any CDP specific actions
        uint256 withdrawnCollateral = _onDecreaseLever(leverParams, subCollateral);

        bytes memory swapData = _delegateCall(
            address(swapAction),
            abi.encodeWithSelector(
                swapAction.swap.selector,
                leverParams.primarySwap
            )
        );
        uint256 swapAmountIn = abi.decode(swapData, (uint256));

        // swap collateral to stablecoin and calculate the amount leftover
        uint256 residualAmount = withdrawnCollateral - swapAmountIn;

        // send left over collateral that was not needed to payback the flash loan to `residualRecipient`
        if (residualAmount > 0) {

            // perform swap from collateral to arbitrary token if necessary
>           if (leverParams.auxSwap.assetIn != address(0)) {
                _delegateCall(
                    address(swapAction),
                    abi.encodeWithSelector(
                        swapAction.swap.selector,
                        leverParams.auxSwap
                    )
                );
            } else {
                // otherwise just send the collateral to `residualRecipient`
                IERC20(leverParams.primarySwap.assetIn).safeTransfer(residualRecipient, residualAmount);
            }
        }

        underlyingToken.forceApprove(address(flashlender), subDebt);

        return CALLBACK_SUCCESS_CREDIT;
    }

Proof of Concept

N/A

Tools Used

Manual Review

Recommended Mitigation Steps

Send the amount of IERC20(leverParams.primarySwap.assetIn).balance(address(this)) to residualRecipient to make sure there are no leftovers.

Assessed type

Other

@c4-bot-6 c4-bot-6 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Aug 15, 2024
c4-bot-5 added a commit that referenced this issue Aug 15, 2024
@c4-bot-12 c4-bot-12 added 🤖_46_group 🤖_primary AI based primary recommendation 🤖_46_group AI based duplicate group recommendation and removed 🤖_46_group labels Aug 15, 2024
@howlbot-integration howlbot-integration bot added primary issue Highest quality submission among a set of duplicates sufficient quality report This report is of sufficient quality labels Aug 20, 2024
@amarcu amarcu added the sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") label Sep 19, 2024
@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Oct 1, 2024
@c4-judge
Copy link
Contributor

c4-judge commented Oct 1, 2024

koolexcrypto marked the issue as satisfactory

@c4-judge
Copy link
Contributor

c4-judge commented Oct 2, 2024

koolexcrypto marked the issue as selected for report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working M-33 primary issue Highest quality submission among a set of duplicates 🤖_primary AI based primary recommendation 🤖_46_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards selected for report This submission will be included/highlighted in the audit report sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

5 participants