Logo
blank Skip to main content

Blockchain Vulnerabilities: Vulnerable ERC20 Tokens and How to Avoid Writing Vulnerable Code

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.

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:

Solidity
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:

Solidity
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.

Read also:
Benefits, Nuances to Consider, and Use Cases of NFTs for Business

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:

Solidity
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.

Read also:
Blockchain Attack Vectors: Vulnerabilities of the Most Secure Technology

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.

Read also:
5 Security Tips for Writing Smart Contracts

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.

Related services

Security Testing

Tell us about your project

Send us a request for proposal! We’ll get back to you with details and estimations.

By clicking Send you give consent to processing your data

Book an Exploratory Call

Do not have any specific task for us in mind but our skills seem interesting?

Get a quick Apriorit intro to better understand our team capabilities.

Book time slot

Contact us