HIGH severity: Civfund — The root cause is that `uniswapV3MintCallback`  lack of access control so the attacker can call `uniswapV3MintCallback` to transfer the funds approved by other users.. https://twitter.com/HypernativeLabs/status/1677529544062803969 https://twitter.com/BeosinAlert/status/1677548773269213184

Vulnerable code:
function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) override external {
        // Unpack parameters sent from the `liquidate` function
        // NOTE: these are being passed in from some other contract, and cannot necessarily be trusted
        (address borrower, address repayAToken, address seizeAToken) = abi.decode(data, (address, address, address));

        address token0 = IPancakePair(msg.sender).token0();
        address token1 = IPancakePair(msg.sender).token1();
        require(msg.sender == IPancakeFactory(FACTORY).getPair(token0, token1));

        if (repayAToken == seizeAToken) {
            uint amount = amount0 != 0 ? amount0 : amount1; 
            address estuary = amount0 != 0 ? token0 : token1; //@note -> BNB

            // Perform the liquidation
            IERC20(estuary).safeApprove(repayAToken, amount);
            ABep20(repayAToken).liquidateBorrow(borrower, amount, seizeAToken);

            // Redeem aTokens for underlying ERC20
            ABep20(seizeAToken).redeem(IERC20(seizeAToken).balanceOf(address(this)));

            // Compute debt and pay back pair
            IERC20(estuary).transfer(msg.sender, (amount * 1000 / 997) + 1);
            return;
        }

        if (repayAToken == ABNB) {
            uint amount = amount0 != 0 ? amount0 : amount1;
            address estuary = amount0 != 0 ? token1 : token0;

            // Convert WBNB to BNB
            IWBNB(WBNB).withdraw(amount);

            // Perform the liquidation
            ABnb(repayAToken).liquidateBorrow{value: amount}(borrower, seizeAToken);

            // Redeem aTokens for underlying ERC20
            ABep20(seizeAToken).redeem(IERC20(seizeAToken).balanceOf(address(this)));

            // Compute debt and pay back pair
            (uint reserveOut, uint reserveIn) = PancakeLibrary.getReserves(FACTORY, WBNB, estuary);
            IERC20(estuary).transfer(msg.sender, PancakeLibrary.getAmountIn(amount, reserveIn, reserveOut));
            return;
        }

        if (seizeAToken == ABNB) {
            uint amount = amount0 != 0 ? amount0 : amount1;
            address source = amount0 != 0 ? token0 : token1;

            // Perform the liquidation
            IERC20(source).safeApprove(repayAToken, amount);
            ABep20(repayAToken).liquidateBorrow(borrower, amount, seizeAToken);

            // Redeem aTokens for underlying ERC20 or BNB
            ABep20(seizeAToken).redeem(IERC20(seizeAToken).balanceOf(address(this)));

            // Convert BNB to WBNB
            IWBNB(WBNB).deposit{value: address(this).balance}();

            // Compute debt and pay back pair
            (uint reserveOut, uint reserveIn) = PancakeLibrary.getReserves(FACTORY, source, WBNB);
            uint debt = PancakeLibrary.getAmountIn(amount, reserveIn, reserveOut);

            IERC20(WBNB).transfer(msg.sender, debt);
            return;
        }

        uint amount;
        address source;
        address estuary;
        if (amount0 != 0) {
            amount = amount0;
            source = token0;
            estuary = token1;
        } else {
            amount = amount1;
            source = token1;
            estuary = token0;
        }

        // Perform the liquidation
        IERC20(source).safeApprove(repayAToken, amount);
        ABep20(repayAToken).liquidateBorrow(borrower, amount, seizeAToken);

        // Redeem aTokens for underlying ERC20 or BNB
        uint seized_uUnits = ABep20(seizeAToken).balanceOfUnderlying(address(this));
        ABep20(seizeAToken).redeem(IERC20(seizeAToken).balanceOf(address(this)));
        address seizeUToken = ABep20Storage(seizeAToken).underlying();

        // Compute debt
        (uint reserveOut, uint reserveIn) = PancakeLibrary.getReserves(FACTORY, source, estuary);
        uint debt = PancakeLibrary.getAmountIn(amount, reserveIn, reserveOut);

        if (seizeUToken == estuary) {
            // Pay back pair
            IERC20(estuary).transfer(msg.sender, debt);
            return;
        }

        IERC20(seizeUToken).safeApprove(ROUTER, seized_uUnits);

        // Define swapping path
        address;
        path[0] = seizeUToken;
        path[1] = estuary;

        uint256 minReceiveDebt = debt * 95 / 100;
        //                                                  sent, min desired, ,   path, recipient,     deadline
        IPancakeRouter(ROUTER).swapExactTokensForTokens(seized_uUnits, minReceiveDebt, path, address(this), now + 1 minutes);

        IERC20(seizeUToken).safeApprove(ROUTER, 0);

        // Pay back pair
        IERC20(estuary).transfer(msg.sender, debt);
    }

Vulnerability type: Access Control