You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
function mintNFT(uint256numberOfNfts) publicpayable {
require(totalSupply() <16384, 'Sale has already ended');
require(numberOfNfts >0, 'numberOfNfts cannot be 0');
require(numberOfNfts <=20, 'You may not buy more than 20 NFTs at once');
require(totalSupply().add(numberOfNfts) <=16384, 'Exceeds NFT supply');
require(getNFTPrice().mul(numberOfNfts) ==msg.value, 'Value sent is not correct');
for (uint256 i =0; i < numberOfNfts; i++) {
uint256 mintIndex =totalSupply(); // get number of NFTs issued so far_safeMint(msg.sender, mintIndex); // mint the next one
}
}
function _safeMint(addressto, uint256tokenId) internalvirtualoverride {
// Mint one NFT and assign it to address(to).require(!_exists(tokenId), 'ERC721: token already minted');
_data =_mint(to, tokenId); // mint NFT and assign it to address to
_totalSupply++; // increment totalSupply() by oneif (to.isContract()) {
// Confirm that NFT was recorded properly by calling// the function onERC721Received() at address(to).// The arguments to the function are not important here.// If onERC721Received is implemented correctly at address(to) then// the function returns _ERC721_RECEIVED if all is well.bytes4memory retval =IERC721Receiver(to).onERC721Received(to, address(0), tokenId, _data);
require(retval == _ERC721_RECEIVED, 'NFT Rejected by receiver');
}
}
让我们证明 _safeMint 根本不安全(尽管它的名字是安全)。
A) 假设已经铸造了16370个NFT,那么 totalSupply()=16370。请解释恶意合约如何导致超过16384个NFT被伪造。攻击者最多可以造出多少个NFT?
function _safeMint(addressto, uint256tokenId) internalvirtualoverride {
// Mint one NFT and assign it to address(to).require(!_exists(tokenId), 'ERC721: token already minted');
_data =_mint(to, tokenId); // mint NFT and assign it to address toif (to.isContract()) {
// Confirm that NFT was recorded properly by calling// the function onERC721Received() at address(to).// The arguments to the function are not important here.// If onERC721Received is implemented correctly at address(to) then// the function returns _ERC721_RECEIVED if all is well.bytes4memory retval =IERC721Receiver(to).onERC721Received(to, address(0), tokenId, _data);
require(retval == _ERC721_RECEIVED, 'NFT Rejected by receiver');
}
_totalSupply++; // increment totalSupply() by one
}
for (uint256 i =0; i < numberOfNfts; i++) {
uint256 mintIndex =totalSupply(); // get number of NFTs issued so far_safeMint(msg.sender, mintIndex); // mint the next one
}
问题4. [16 分]: Hashmasks 重入缺陷
在第8课和第3节中,我们讨论了 solidity 重入缺陷。在这个问题中,我们将看一个有趣的现实世界的例子。考虑下面16384个NFT中使用的 solidity 代码片段。通过调用此NFT合约上的
mintNFT()
函数,用户一次最多可以铸造20个NFT。您可以假设所有内部变量都由构造函数正确初始化(未显示)。让我们证明
_safeMint
根本不安全(尽管它的名字是安全)。A) 假设已经铸造了16370个NFT,那么 totalSupply()=16370。请解释恶意合约如何导致超过16384个NFT被伪造。攻击者最多可以造出多少个NFT?
提示:如果在调用地址
onERC721Received
是恶意的,结果会怎样?请仔细检查铸币回路,并考虑重入缺陷。答: 在已经 mint 16370 个NFT基础上,调用 mingNFT 可传入的最大 numberOfNfts 为 14 可以通过 mintNFT 开始五行的限制,当上述合约在调用地址$14+13+\dots+2+1=105$ 。
to
上的onERC721Received
函数时,这个函数可以再次调用上述 mingNFT 函数,此时,在原来已经 mint 一个的基础上,传入的 numberOfNfts 为 13 个可以通过 mintNFT 的限制,然后重复同样的过程,依次可以 mint 12, 11 直到 1,最后在函数内部,已经没有其他限制,故这些数量的 NFT 均可以被 mint,所以理论上总共可以 mint 的数量为B) 假设现在总供给的价值是16370,请写出实施对(a)部分进行攻击的恶意Solidity合约代码。
答:
其中
attack
设置为payable
是因为需要通过攻击合约调用 mintNFT 函数,需要发送一定数量的以太,可以选择在部署后先发送一定数量的以太到攻击者合约中,也可以将attack
设置成payable
,在攻击的交易中发送以太到实验:在 Rinkeby 上部署,攻击者合约地址为 0xf1eb80Bb66A70E44d42B3ceC0bC18Ec28B5F2Ea8,实际攻击的交易:https://rinkeby.etherscan.io/tx/0xb90496fd8789c3d1800df1bd3a571d019fb6158cbd521a9d05e57ad62460d15f,这个部署的合约中,NFT的价格设置为 1 wei,所以理论上只要发送 105 wei 到攻击这合约中,但是保险起见,发送了150wei,最后也可以看到攻击这合约中还剩下 45 wei。
C) 你会在前一页的代码中添加或更改哪一行Solidity来防止你的攻击?请注意,单个交易不应该铸造超过20个NFT。
答: 可以将
_safeMint
方法中,_totalSupply++;
这一行放到验证 NFT 的调用之后:这样,当合约被重入攻击时,由于
_totalSupply
还没有增加,因此在第二次进入mintNFT
函数时mintIndex
的值是第一次 mint 的值,会导致触发'ERC721: token already minted'
这个错误,有效保证合约安全。验证交易: https://rinkeby.etherscan.io/tx/0xa5f70a226c5fd64132eee800f8902ddb9b4ff562ff7f37820d11746fbde52acb
感谢 discord yyczz#5837 对于这个问题的指导。
The text was updated successfully, but these errors were encountered: