sherlock-audit / 2024-03-woofi-swap-judging

3 stars 2 forks source link

hals - `WooCrossChainRouterV4.crossSwap()` doesn't correctly check for slippage #85

Open sherlock-admin2 opened 8 months ago

sherlock-admin2 commented 8 months ago

hals

medium

WooCrossChainRouterV4.crossSwap() doesn't correctly check for slippage

Summary

WooCrossChainRouterV4.crossSwap() doesn't correctly check for slippage, as it deducts external swapping fees after checking for the minimum bridged amount determined by the user.

Vulnerability Detail

  1. Swap asset A in the user's wallet to asset B in WOOFi on the source chain
  2. Then bridging asset B to asset C on the destination chain via Stargate (asset B and asset C are of the same value)
  3. Then swap asset C to asset D in WOOFi on the destination chain and send to the wallet instructed by the user.

Impact

But as can be noticed, an external swap fee is deducted from the bridgeAmount after the swap is done via an external aggregator (1inch aggregator) and after checking that the bridgeAmount is sufficient as per detrmined by the user (> srcInfos.minBridgeAmount), and this might result in the bridgeAmount being less than required by the user srcInfos.minBridgeAmount.

Code Snippet

WooCrossChainRouterV4.crossSwap function/L137-L138

   // Step 2: local swap by 1inch router
            if (srcInfos.fromToken != srcInfos.bridgeToken) {
                TransferHelper.safeApprove(srcInfos.fromToken, address(wooRouter), srcInfos.fromAmount);
                if (src1inch.swapRouter != address(0)) {
                    // external swap via 1inch
                    bridgeAmount = wooRouter.externalSwap(
                        src1inch.swapRouter,
                        src1inch.swapRouter,
                        srcInfos.fromToken,
                        srcInfos.bridgeToken,
                        srcInfos.fromAmount,
                        srcInfos.minBridgeAmount,
                        payable(address(this)),
                        src1inch.data
                    );

                    fee = (bridgeAmount * srcExternalFeeRate) / FEE_BASE;
                } else {

                //some code...
        }

        // Step 3: deduct the swap fee
        bridgeAmount -= fee;

Tool used

Manual Review

Recommendation

Update WooCrossChainRouterV4.crossSwap() to check for the bridgeAmount being greater than the amount determined by the user srcInfos.minBridgeAmount after deducting the fees:

    function crossSwap(
        uint256 refId,
        address payable to,
        SrcInfos memory srcInfos,
        DstInfos calldata dstInfos,
        Src1inch calldata src1inch,
        Dst1inch calldata dst1inch
    ) external payable whenNotPaused nonReentrant {

    //some code...

   // Step 2: local swap by 1inch router
            if (srcInfos.fromToken != srcInfos.bridgeToken) {
                TransferHelper.safeApprove(srcInfos.fromToken, address(wooRouter), srcInfos.fromAmount);
                if (src1inch.swapRouter != address(0)) {
                    // external swap via 1inch
                    bridgeAmount = wooRouter.externalSwap(
                        src1inch.swapRouter,
                        src1inch.swapRouter,
                        srcInfos.fromToken,
                        srcInfos.bridgeToken,
                        srcInfos.fromAmount,
                        srcInfos.minBridgeAmount,
                        payable(address(this)),
                        src1inch.data
                    );

                    fee = (bridgeAmount * srcExternalFeeRate) / FEE_BASE;
                } else {

                //some code...
        }

        // Step 3: deduct the swap fee
        bridgeAmount -= fee;

+       require(bridgeAmount >= srcInfos.minBridgeAmount, "insufficient bridged amount");

        //some code...
sherlock-admin4 commented 7 months ago

The protocol team fixed this issue in the following PRs/commits: https://github.com/woonetwork/WooPoolV2/pull/112/commits/151443bf3c780f4e45796312591c61e1bd188122

WangSecurity commented 7 months ago

Initially, it was selected as a duplicate of 141, but it's not. 141 is invalid and 85 is valid.

sherlock-admin2 commented 7 months ago

Escalate.

Please think about the actual impact.

You've deleted an escalation for this issue.

sherlock-admin2 commented 6 months ago

The Lead Senior Watson signed off on the fix.