Blockchain technology has managed to change how we imagine a safe transfer of money and data. However, this technology isnโt 100 percent free from exploits and vulnerabilities. In this series, we talk about some of the most recently discovered blockchain vulnerabilities that have been handled by the community.
This post is dedicated to the recently discovered ERC-20 batchOverflow vulnerability, the development mistakes behind vulnerable ERC20 tokens, and the measures you should take to avoid repeating the same mistakes.
Contents
ERC20 token vulnerability
In April 2018, an incident that was later named the batchOverflow exploit took place. During the exploit, vulnerable ERC20 contracts of such ERC coins as BeautyChain (BEC) and MeshBox (MESH) were exploited to generate absurd numbers of tokens from thin air.
A few days after the batchOverflow exploit was discovered, security firm PeckShield identified several vulnerabilities in multiple Ethereum tokens. The list of affected coins includes:
- Aurora Dao (AURA)
- BeautyChain (BEC)
- UG Token (UGT)
- Smart Billions (SMART)
- FirstCoin (FRST)
- GG Token (GG)
- CNY Token (CNY)
- CNYTokenPlus (CNYt+)
- UselessEthereumToken (UET)
- Hexagon (HXG)
- Education (EDU)
- Smart Mesh (SMT)
- MTC
- SCA
These vulnerabilities were discovered shortly after the batchOverflow exploit took place. In order to find them, researchers explored suspicious transactions left by attackers. The main sign of suspicious transactions was an unusually high number of transferred tokens that sometimes was even larger than a tokenโs total supply. To prevent speculation, several major exchanges completely shut down deposits and withdrawals of ERC20 tokens. These exchanges were:
- OKEx
- Poloniex
- Changelly
- Huobi Pro
As of now, most of the issues related to the breach have been resolved and exchanges are functioning normally.
Two main problems with ERC20 tokens
When looking for weak spots in ERC20 tokens, researchers discovered several exploits and assigned different names to each of them. In reality, though, there are only two main problems that plague all of these tokens:
- Overflow vulnerabilities
- Unprotected functions
Letโs take a closer look at each of these problems.
Overflow vulnerabilities
Overflow vulnerabilities are based on exploiting an ERC20 token standard vulnerability called integer overflow or underflow. This problem happens when the result of a math operation is outside the range that can be represented by a variable.
In the case of smart contracts in Ethereum, if you subtract anything from zero, youโll get a very large value. If you add two large values together, the result will wrap around and will be close to zero.
For instance, letโs look at the Smart Mesh token (SMT). This token has a function called transferProxy:
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeSmt,
ย ย ย ย uint8 _v,bytes32 _r, bytes32 _s) public transferAllowed(_from) returns (bool){
ย ย ย ย if(balances[_from] < _feeSmt + _value) revert(); //This line can be bypassed with large values of _value and _feeSmart
ย ย ย ย uint256 nonce = nonces[_from];
ย ย ย ย bytes32 h = keccak256(_from,_to,_value,_feeSmt,nonce);
ย ย ย ย if(_from != ecrecover(h,_v,_r,_s)) revert();
ย ย ย ย if(balances[_to] + _value < balances[_to]
ย ย ย ย ย ย ย ย || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert();
ย ย ย balances[_to] += _value; //Since _value is large, the account receives a lot of tokens
ย ย ย ย Transfer(_from, _to, _value);
ย ย ย balances[msg.sender] += _feeSmt; //Same as with _value, _feeSmt is large so the account receives a lot of tokens.
ย ย ย Transfer(_from, msg.sender, _feeSmt);
ย ย ย ย balances[_from] -= _value + _feeSmt;
ย ย ย ย nonces[_from] = nonce + 1;
ย ย ย ย return true;
}
In this case, the vulnerable code is in line 206. The addition in this line lacks proper checks for overflow. We can set large values of _value and _feeSmart so that their sum will overflow and the result will be less than the accountโs balance and the condition will pass, adding absurdly large values to the balance of the set accounts. In particular, this exploit was used in this transaction:
SMT.transferProxy(0xaf31a499a5a8358b74564f1e2214b31bc34eb46f,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 0xaf31a499a5a8358b74564f1e2214b31bc34eb46f,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, //_value
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 0x7000000000000000000000000000000000000000000000000000000000000001, //_feeSmart
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 27,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 0x87790587c256045860b8fe624e5807a658424fad18c2348460e40ecf10fc8799,
ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย 0x6c879b1e8a0a62f23b47aa57a3369d416dd783966bd1dda0394c04163a98d8d8)
//Note: here, _value + _feeSmart = 0 because of an overflow.
As the result of this transaction, the accounts msg.sender and _to will receive a ridiculously large number of tokens โโ 115792089237316195423570985008687907853269984665640564039457584007913129639936 (a 78-digit number!) to be exact. The most unfortunate part is that the issue could have been easily avoided. All that programmers had to do was check that particular line for overflow.
Unprotected function vulnerability
The second type of ERC20 token security problem is unprotected functions. This kind of ERC20 vulnerability occurs only when a developer forgets to add an appropriate modifier that restricts access to a function. As a result, some critical core functions can be left exposed for any random user to call freely.
For example, in the development of Ethereum smart contracts, itโs common practice to limit access to certain functions to a single account. That account is usually called the owner.
To make this restriction possible, developers typically use a modifier such as onlyOwner:
contract Owned {
address public owner;
function Owned() {
owner = msg.sender;
}
function setOwner(address _owner) onlyOwner returns (bool success) {
owner = _owner;
return true;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
}
contract TestContract is Owned {
...
function RetrieveAllTokens(address _addr) onlyOwner { //will fail if called by anyone other than the owner.
//transfers all tokens to the address.
}
}
However, if a developer forgets to add the modifier, anyone can call the function.
And this is exactly what happened to the AURA token. While the functions within the contract are restricted correctly with an ownerOnly modifier, the function that sets the owner isnโt. As a result, anyone can call the setOwner function and set a random owner to the contract. Fortunately, at this point the owner account can do nothing more than a regular user, so this issue is left safely unpatched.
How to avoid writing vulnerable code
As you can see, in most cases these ERC20 functionality exploits are accidental programming oversights. And even though these oversights are well known and easy to avoid, you can still find vulnerable contracts on the Ethereum network.
Is it possible to avoid adding to the pile? Can you avoid writing vulnerable code when working with ERC20 tokens? Of course itโs possible and of course you can. We have several tips that can help you ensure a high level of code safety:
- Explicitly mark visibility in functions and state variables to avoid leaving anything unprotected.
- Prevent overflows and underflows by using such libraries as SafeMath by OpenZeppelin.
- Beware of rounding in integer divisions. Solidity always rounds down (so 5/2 = 2, not 2.5).
- Let users pull tokens (in bonuses, games, airdrops, and so on) rather than forcefully sending (pushing) them to save gas and avoid denial of service attacks.
- Use the newest Solidity constructs:
- Use require and assert properly so that your code may be formally verified by an automated analyzer.
- Use selfdestruct instead of suicide and keccak256 instead of sha3.
- Set up a test contract on a public testnet like Ropsten.
- Provide a bug bounty and open your contracts for testing by the community.
- Get a formal security audit of your contract.
These easy steps will prevent you from creating vulnerable code and will help you improve your codeโs security.
Conclusion
The recent batchOverflow exploit showed that a single mistake in code can cause a serious security problem. But even though ERC-20 tokens are vulnerable to overflows, all that developers need to do is to take additional measures to prevent overflows and underflows and double-check their code.
At Apriorit, one of our specialties is cybersecurity and data protection. We have a team of cybersecurity professionals who will gladly assist you in building a secure blockchain solution. Feel free to contact us by using the form below.
In the next post on blockchain vulnerability, you can read a detailed explanation of the FOMO3D exploit.