How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and how to avoid them (Part 2)
While Part 1 discussed some more high profile or obvious vulnerabilities, this post will be about vulnerabilities that have not been exploited widely yet.
Let’s skip the introduction and jump straight to them:
4. Forcing ether to a contract
selfdestruct does two things.
- It renders the contract useless, effectively deleting the bytecode at that address.
- It sends all the contract’s funds to a target address.
The special case here, is that if the receiving address is a contract, its fallback function does not get executed.
This means that if a contract’s function has a conditional statement that depends on that contract’s balance being below a certain amount, that statement can be potentially bypassed:
Due to the throwing fallback function, normally the contract cannot receive ether. However, if a contract selfdestructs with this contract as a target, the fallback function does not get called. As a result
this.balance becomes greater than 0, and thus the attacker can bypass the
require statement in
Mitigation: Never use a contract’s balance as a guard.
5. Call to the Unknown (DoS with unexpected revert)
This vulnerability appeared in the King of the Ether smart contract.
In this case, an attacker’s contract could first claim leadership by sending enough ether to the insecure contract. Then, the transactions of another player who would attempt to claim leadership would throw due to line 25 in the above snippet.
Although a simple attack, this causes a permanent denial of service to the contract rendering it useless. This can be found in other ponzi scheme contracts that follow the same pattern.
6. Short Address Attack
This attack was discovered by the Golem team and described in this article. This vulnerability allows an attacker to abuse the
transfer function and withdraw a larger amount of ERC20 tokens than he is allowed to.
Note: For simplicity we’ll use words half the normal size.
To explain this bug let’s consider an exchange having a wallet with
10000 tokens and a user with a balance of
32 tokens on that exchange’s wallet. Let’s also consider that this user’s address is
0x12345600 — notice the trailing zeroes — and that they want to withdraw an amount larger than their balance. To do that they would go to the exchange and click the token’s withdraw button and input their address without the trailing zeroes (the exchange does not perform input validation and allows the transaction to go through, even though the attacker’s address length is invalid).
Then, the EVM calculates the input data for the transaction to be executed by concatenating the function’s signature and the arguments.
The ERC20’s transfer function is formulated as
transfer(address to, uint256 amount). The 3 fields would be as follows:
sig : a9059cbb = web3.sha3("transfer(address,uint256)").slice(0,10)
arg1: 123456 = receiving address
arg2: 00000020 = 32 in hexademical (0x20)
Concatenated: a9059cbb 123456 00000020
Transaction input data: 0xa9059cbb12345600000020
Looking closely, the transaction’s length is 2 bytes shorter (4 bytes in the real world with full words). The EVM in this case would pad the transaction with trailing zeroes, resulting in:
// a9059cbb = web3.sha3("transfer(address,uint256)").slice(0,10)
// 12345600 = receiving address
// 00002000 = 8192 in hexademical (0x2000 == 0x20<<8)
That way, even though the attacker’s balance according to the exchange is
32 tokens, they are able to execute a perfectly legitimate transfer for an amount that is much larger. This of course relies on the fact that the sending account (the exchange’s wallet) has enough balance for the transfer.
- Throw if
msg.datahas invalid size
- Exchanges must perform input validation
ALWAYS avoid the use of
block.blockhash for your contract’s business logic as their results are predictable or can be manipulated by miners. More on that here.
What can I do towards securing my Smart Contracts?
This has been mentioned in many places, my personal favorite being here.
Among others, my most important picks are:
- Don’t write fancy code
- Use audited and tested code
- Write as many unit tests as possible
What tools can I use to audit and analyze my code?
First of all, solc performing semantic checking is a huge step towards security as potential mistakes get found at compilation time.
- Securify.ch is a static analysis tool for Smart Contracts.
- Remix also performs static analysis to your code and is able to spot bugs such as uninitialized storage pointers and reentrancy.
- Oyente is another recently announced analysis tool for Smart Contracts
- Hydra is a “framework for cryptoeconomic contract security, decentralized security bounties”
- Porosity is a “decompiler for Blockchain-based Ethereum Smart-Contracts”
- Manticore is a dynamic binary analysis tool with EVM support
- Ethersplay is a Binary Ninja plugin for EVM
Finally, visualizing your code by using tools such as solgraph can help you find potential bugs regarding functions’ visibility.
For a more in-depth read on attacks on smart contracts refer to:
- The Underhanded Solidity Contest [1,2,3]
- A survey of attacks on Ethereum Smart Contracts
- Smart contract best practices — Known attacks
- Onward with Smart Contract Security
You can practice your Smart Contract hacking skills at:
Loom Network is a platform for building highly scalable DPoS sidechains to Ethereum, with a focus on large-scale games and social apps.
Want more info? Start here.
Fan of blockchain gaming? Check out Zombie Battleground, the world’s first PC & mobile card game that runs fully on its own blockchain.
And if you enjoyed this article and want to stay in the loop, go ahead and sign up for our private mailing list.