相关文章
https://new.qq.com/omn/20220327/20220327A08RJY00.html
https://blog.csdn.net/Timmbe/article/details/124581227

https://it.sohu.com/a/559509567_121233548

攻击交易:
https://etherscan.io/tx/0xe0b0c2672b760bef4e2851e91c69c8c0ad135c6987bbf1f43f5846d89e691428
合约代码:
Revest:https://etherscan.io/address/0x2320a28f52334d62622cc2eafa15de55f9987ed9#code
TokenVault:https://etherscan.io/address/0xA81bd16Aa6F6B25e66965A2f842e9C806c0AA11F#code
FNFTHandler:https://etherscan.io/address/0xe952bda8c06481506e4731C4f54CeD2d4ab81659#code

攻击流程:
攻击者调用mintAddressLock函数铸造了2个ID为1027的Token,
随后再次调用mintAddressLock铸造了360000个ID为1028的Token,在mint函数回调中重入调用depositAdditionalToFNFT函数,质押1027并mint一个1028并修改1028对应的fnft.depositAmount(fnftsCreated在mint之后更新)
最后调用withdrawFNFT提取360001个1028中的depositAmount的对应代币.完成攻击

漏洞1、
合约FNFTHandler中mint函数mint后将fnftsCreated 增加,而_mint中存在回调确认函数

同时合约Revest中仅有withdrawFNFT可以防重入,即允许重复创建同一个fnftId,并且因为supply[id] += amount,nft数量多次创建不会重置,但可以更改相同fnftId的nft的属性;

mint 函数缺少重入锁,_mint中存在回调确认函数
function mint(address account, uint id, uint amount, bytes memory data) external override onlyRevestController {
        supply[id] += amount;
        _mint(account, id, amount, data);
        fnftsCreated += 1;
    }
function _mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(account != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);

        _balances[id][account] += amount;
        emit TransferSingle(operator, address(0), account, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
    }

首先应该给mint函数加一个重入锁,这个流程没有遵循(CEI) 检查 状态修改 交互 而这个函数中 将交互 也就是最终的_mint 放在了中间 后面又进行了状态修改,但是即使换了位置还是存在回调函数,所以要加重入锁才行

漏洞2、攻击核心点

在被攻击的 Revest 合约中,用户调用 mintAddressLock 函数来将一定数量的 ERC-20 代币存入 Revest Smart Vault 时,就会创建 FNFT。该 NFT 代表了用户拥有的代币资产数额,后续可以调用 withdrawFNFT 函数将代币赎回。

攻击核心点就在于攻击者利用 ERC1155 标准铸造 NFT 时会调用接受者地址的 onERC1155Received 函数,因此攻击者利用该点回调重入了 Revest 合约中的 depositAdditionalToFNFT 函数,该函数会铸造一个新的 NFT,接着会调用 tokenVault 合约的 handleMultipleDeposits 函数记录新的 NFT 的信息,而 handleMultipleDeposits 函数中缺少了对该新铸造的 NFT 是否存在的判断,故此攻击者利用重入修改了已经铸造过的 NFT 的信息,而用户铸造 NFT 打入 ERC20 资产代币的流程是在重入操作之前的,故此用户无需打入 ERC20 代币就成功铸造了代表自己具有 360001 枚 ERC20 代币资产的 NFT。

总结

本次攻击事件是由于在 tokenVault 合约中的 handleMultipleDeposits 函数中没有判断该新铸造的 NFT 是否存在,故此攻击者利用该点直接修改了已经铸造过的 NFT 的信息,并且在 Revest 合约中关键的函数没有做重入锁的限制,导致了被回调利用。慢雾安全团队建议在进行铸造 NFT 等敏感操作时需增加对 NFT 是否已经存在的判断,且在合约关键函数中必须添加重入锁的限制,避免再次出现此类问题。

 

本分析摘自 https://it.sohu.com/a/559509567_121233548 文章名

Web3安全连载(2) | 避坑指南,一文看懂典型的NFT合约漏洞有哪些?

用户追加质押资产时,根据quantity的不同存在三种情况,第一种情况是为所有FNFT追加抵押,第二种情况是为其中一部分FNFT追加抵押,第三种情况是追加抵押的FNFT数量大于FNFT总量,此时报错返回。该漏洞为第二种情况下造成的重入,具体逻辑如下:

由上述代码可知,追加抵押时需要先把之前的FNFT销毁,之后再铸造新的FNFT。但是在铸造时,由于min()函数中未判断需铸造的FNFT是否已经存在,并且状态变量fnftId自增在_mint()函数后。而_min()中存在ERC-1155中的隐藏外部调用_doSafeTransferAcceptanceCheck(),造成了重入漏洞。具体代码如下:

此处的mint()函数中_mint()包含一个隐藏的外部调用,具体如下: