How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and how to avoid them (Part 2)

Georgios Konstantopoulos
Loom Network
Published in
5 min readJan 17, 2018

--

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

Solidity’s selfdestruct does two things.

  1. It renders the contract useless, effectively deleting the bytecode at that address.
  2. 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 onlyNonZeroBalance

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 is0x12345600 — 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

The vulnerability

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:

0xa9059cbb1234560000002000// 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.

Mitigations:

  • Throw if msg.data has invalid size
  • Exchanges must perform input validation
Reddit comment describing validating msg.data size

Bonus:

ALWAYS avoid the use of now and 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.

https://github.com/raineorshine/solgraph

Note: Devcon3 Day 2 was filled with contract security talks, I definitely recommend watching them [1,2].

For a more in-depth read on attacks on smart contracts refer to:

You can practice your Smart Contract hacking skills at:

Loom Network is the multichain interop platform for scaling high-performance dapps — already live in production, audited, and battle-tested.

Deploy your dapp to Loom’s Basechain once and reach the widest possible user base across all major blockchains today.

New to Loom? Start here.

Want to stake your LOOM tokens and help secure Basechain? Find out how.

Like what we’re doing here? Stay in the loop by signing up for our private mailing list.

--

--