攻击交易:
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
合约FNFTHandler中mint
函数mint后将fnftsCreated
增加,而_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);
}
同时合约Revest中仅有withdrawFNFT
可以防重入,即允许重复创建同一个fnftId,并且因为supply[id] += amount
,nft数量多次创建不会重置,但可以更改相同fnftId的nft的属性;
攻击者调用mintAddressLock
函数铸造了2个ID为1027的Token,
随后再次调用mintAddressLock
铸造了360000个ID为1028的Token,在mint
函数回调中重入调用depositAdditionalToFNFT函数,质押1027并mint
一个1028并修改1028对应的fnft.depositAmount
(fnftsCreated
在mint之后更新)
最后调用withdrawFNFT
提取360001个1028中的depositAmount
的对应代币.完成攻击