Attack transaction:
https://etherscan.io/tx/0xe0b0c2672b760bef4e2851e91c69c8c0ad135c6987bbf1f43f5846d89e691428
Contract code:
Revest: https://etherscan.io/address/0x2320a28f52334d62622cc 2eafa15de55f9987ed9#code
TokenVault:https://etherscan.io/address/0xA81bd16Aa6F6B25e66965A2f842e9C806c0AA11F#code
FNFTHandler:https ://etherscan.io/address/0xe952bda8c06481506e4731C4f54CeD2d4ab81659#code
mint
The function mint in the contract FNFTHandler will fnftsCreated
increase, and _mint
there is a callback confirmation function in it.
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);
}
At the same time, there is only anti-reentry in the contract Revest withdrawFNFT
, which allows repeated creation of the same fnftId, and because supply[id] += amount
the number of nft created multiple times will not be reset, but the properties of the nft with the same fnftId can be changed;
The attacker calls mintAddressLock
the function to mint 2 Tokens with an ID of 1027, and then calls again to mint 360,000 Tokens with an ID of 1028. In the function callback, the attacker calls the depositAdditionalToFNFT function
again , pledges 1027 and one 1028, and modifies the value corresponding to 1028 ( in mint Update later ) Finally call to extract the corresponding tokens in 360001 1028. Complete the attackmintAddressLock
mint
mint
fnft.depositAmount
fnftsCreated
withdrawFNFT
depositAmount