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.
What happened
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 max
is 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 transferProxy
:
An attacker exploited this function in transaction 0x1abab4c8…:
This transaction occurred at block 5499035
, and at this point in the blockchain:
balances[_from]
(balances[0xdf31a49…]
) had a balance of0
tokens.balances[_to]
(balances[0xdf31a49…]
) had a balance of0
tokens.balances[msg.sender]
(balances[0xd6a09bd…]
) had a balance of0
tokens.
_value
is 0x8ffff…
and _freeSmt
is 0x70000…1
. Notice how the addition of _from
to _value
perfectly overflows to 0
. 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 balances[_from]
is 0
and _feeSmt + _value
is also 0
— therefore the condition of the if
statement is false
(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]
is0
and thereforebalances[_to] + _value < balances[_to]
isfalse
balances[msg.sender]
is0
and thereforebalances[msg.sender] + _feeSmt < balances[msg.sender]
isfalse
Hence, both conditions are false
and revert()
is not triggered.
Subsequently, balance’s state variable will be modified as such:
balances[_to] += _value;
causes the owner of address_to
to gain a massive number of tokens; andbalances[_from] -= _value + _feeSmt;
causes the owner of_from
to lose0
tokens.
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 batchTransfer
function:
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 uint256(cnt)
.
Since: 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.
About the authors
- Koh Wei Jie (@catallacticised) is a full-stack smart contract developer based in Singapore.
- Raman Shalupau (@ksaitor) runs Crypto Jobs List — the #1 crypto community to find and post blockchain jobs. He is also a full-stack smart contract developer.