1.Vulnerabilities
For example, the uint8 type has a total of 8 bits, which can represent the value of 00000000~11111111, converted into decimal, which is the value range of 0~255. At this time, once the result is 256, since there are only 8 digits in total, the 9th digit 1 cannot be displayed, leaving only 00000000, which is the desired 256, but actually 0 is obtained.
For example, the following TimeLock contract:
pragma solidity ^0.4.18;
contract TimeLock {
mapping(address => uint) public balances;
mapping(address => uint) public lockTime;
function deposit() public payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = now + 1 weeks;
}
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease;
}
function withdraw() public {
require(balances[msg.sender] > 0);
require(now > lockTime[msg.sender]);
uint transferValue = balances[msg.sender];
balances[msg.sender] = 0;
msg.sender.transfer(transferValue);
}
}
In the increaseLockTime function, since a free timestamp increment can be input by itself, there is a risk of integer overflow. Just imagine, if the input _secondsToIncrease is added to the original lockTime[msg.sender] ,due to overflow, the value of lockTime[msg.sender] will finally become a very small value, so that in the withdraw function, you can smoothly pass
require(now > lockTime[msg.sender]);
这一行,使得deposit进去的ETH可以提前被取出。
2.Preventive measures
First of all, in version 0.8.0, this problem has been solved at the language level: once an integer overflow occurs, the transaction will be directly reverted. Before version 0.8.0, a SafeMath library of openzeppelin was required.
3.Real cases
On April 22, 2018, hackers launched an attack on the BEC smart contract and took out out of thin air:
57,896,044,618,658,100,000,000,000,000,000,000,000,000,000,000,000,000,000,000.792003956564819968 BEC tokens were sold in the market. If it is 0, the market collapses instantly.
The contract version is ^0.4.16, which is less than version 0.8, and the SafeMath library is not used, so there is an integer overflow problem.
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value; //溢出点,这里存在整数溢出
require(cnt > 0 && cnt <= 20);
require(_value > 0 && balances[msg.sender] >= amount);
balances[msg.sender] = balances[msg.sender].sub(amount);
for (uint i = 0; i < cnt; i++) {
balances[_receivers[i]] = balances[_receivers[i]].add(_value);
Transfer(msg.sender, _receivers[i], _value);
}
return true;
}
The hacker passed in a very large value (here it is 2**255), and overflowed through multiplication, so that the amount (the total number of coins to be transferred) overflowed and became a small number or 0 (here became 0) , so as to bypass the check code of balances[msg.sender] >= amount, so that the malicious transfer of a huge amount of _value can be successful.
Malicious transfer records of actual attacks:

