Why you should verify the tokens you own: A deep dive into two vulnerable ERC20 contracts
The prices of two crypto tokens — BeautyChain and SmartMesh — dramatically crashed this week. One or more attackers exploited bugs in the their smart contracts and generated huge amounts of tokens out of thin air, massively diluting token supplies. These events could have been easily avoided. In this post, we describe the attacks, illustrate them with Solidity code examples, and argue that these hacks are a wake-up call for token investors and developers to be more cognisant and diligent about smart contract security.
On 25 April 2018, some cryptocurrency exchanges halted deposits, withdrawals, and tradesof at least two ERC20 tokens: BeautyChain and SmartMesh, citing reports that these tokens had a security vulnerability in their smart contracts.
These contracts had already been attacked to create huge numbers of tokens out of thin air, and they suffered sharp price drops. The Bitcoin-denominated price of one of these tokens, for instance, fell more than 90% in a single hour, before trading was suspended:
This vulnerability, known as an integer overflow, is not new. It is easily preventable as it is commonly listed in smart contract security guides. Moreover, libraries that prevent such attacks are freely available. Yet, vulnerable contracts abound.
One security researcher has already located at least 10 tokens affected by this particular bug. In fact, the same vulnerability had been infamously used by scammers to massively inflate the supply of their token and sell it on exchanges. Had the developers behind these contracts been aware of these well-known exploits and acted in good faith, these attacks might not have occurred.
How the attacks worked
Some Solidity smart contracts are vulnerable to what is known as an integer overflow or underflow. They occur when a variable exceeds the maximum or minimum of the data type it uses. When this happens, the value wraps around the other end of the minimum or maximum range respectively.
For instance, the following Solidity function always returns true:
Mathematically, this is unintuitive, since the sum of two positive numbers is always greater than the first. Yet, since the value of
2^256 - 1, which is the upper limit of the
uint data type, the result of
x + max wraps around to
0, and becomes
499 instead of
500 + 2^256 - 1.
An integer underflow follows the same principle, but in reverse.
In the next two sections of this post, we illustrate how an integer overflow and underflow allowed the exploits to occur.
The SmartMesh token contract bug
Note: this section was edited on 27th April to make the code walkthrough more accurate.
The vulnerable function in the SmartMesh token contract is
An attacker exploited this function in transaction 0x1abab4c8…:
This transaction occurred at block
5499035, and at this point in the blockchain:
balances[0xdf31a49…]) had a balance of
balances[0xdf31a49…]) had a balance of
balances[0xd6a09bd…]) had a balance of
0x70000…1. Notice how the addition of
_value perfectly overflows to
0x8ffff…f + 0x70000…1 is 0. This will be helpful to understand in a minute.
Now, let’s look at the first if statement (line 5). Because
_feeSmt + _value is also
0 — therefore the condition of the
if statement is
0 < 0 is false) and
revert() is not triggered. This check passes — good for the hacker, bad for everyone else.
Next, let’s analyse the second
if statement (line 14):
if (balances[_to] + _value < balances[_to] || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert();
The contract author may have intended to perform an overflow check here. However, at this point, no overflow happens:
balances[_to] + _value < balances[_to]is
balances[msg.sender] + _feeSmt < balances[msg.sender]is
Hence, both conditions are
revert() is not triggered.
Subsequently, balance’s state variable will be modified as such:
balances[_to] += _value;causes the owner of address
_toto gain a massive number of tokens; and
balances[_from] -= _value + _feeSmt;causes the owner of
The BeautyChain token contract bug
While the authors of the BeautyChain token contract used the SafeMath library, whose functions prevent overflow exploits if used correctly, they did not do so in one particular line of the
uint256 amount = uint256(cnt) * _value;
The full function is as follows:
In the transaction used to trigger this exploit, an overflow occurs when
_value is multiplied with
cnt == 2 and
_value == 0x8000000000000000000000000000000000000000000000000000000000000000
amount overflowed and became exactly
0. This means that the second
require() statement did not throw an exception, and the
balance state variable was massively incremented in the
for loop. Whoever exploited this was very smart to realise that a particular combination of inputs would cause the
require statements or the SafeMath functions to not throw any exceptions, yet allow the exploit to occur. In contrast, a careless auditor would have simply assumed that the mere presence of SafeMath library functions in
batchTransfer made it secure.
A wake-up call
A common refrain in this space is that if you don’t own your private keys, you don’t own your coins. Unfortunately, this advice is not enough when it comes to tokens. This is because tokens are only as secure as the smart contracts that define them. Remember: don’t trust, but verify. You not only have to keep your private keys safe, but you also have to verify that the code which underlies your tokens is free from vulnerabilities like those described above.
Cryptocurrency exchanges are now in a tough spot, as the only way to be sure that customers’ funds are safe is to audit every single line of code for every single tradable token. This is tedious but necessary. Nor is this situation ideal for investors. The prospect of losing money because of an undiscovered and subtle bug is unsettling. Yet laypersons are unequipped to audit smart contracts, so they have little choice but to trust the developers. Ironically, financial disintermediation and trustlessness is precisely what cryptocurrencies promise, but this is the reality of the situation.
These events should serve as a wake-up call to everyone in the space. Smart contract developers must be competent and diligent to secure investors’ funds, and investors have to be fully aware of the pitfalls of poorly implemented tokens. The only way to be sure is, unfortunately, to verify the code yourself.
A brief note regarding authorship: we independently identified the bug in the SmartMesh token when we first learned about it, but the BeautyChain token bug was discovered earlier on by someone else, who then wrote about it in Mandarin. All content above, however, is in our own words.