avatarAlex Roan

Summary

This context discusses security vulnerabilities in Solidity smart contracts, focusing on reentrancy attacks and owner theft, and provides methods to prevent them.

Abstract

The provided context is a detailed article about securing smart contracts written in Solidity, the programming language used for Ethereum blockchain. The article begins by explaining the concept of tunnel vision in development and the importance of addressing potential security issues. It then delves into two specific vulnerabilities: reentrancy attacks and owner theft.

Reentrancy attacks occur when a smart contract calls or sends Ether to an external address, inherently vulnerable to reentrancy. This type of attack involves a malicious contract manipulating the logic of a target contract to drain it of Ether. The article provides a real-life example of this attack in the DAO hack of 2016. To prevent reentrancy attacks, the article suggests using the transfer() function to send Ether, following the Checks→Effects→Interactions pattern, and implementing a contract lock, also known as a mutex.

Owner theft is another vulnerability discussed in the article, which occurs when a function modifier, such as onlyOwner, is not used correctly. This modifier allows developers to identify who can call each function in a contract based on their privilege. If a contract has a public function to change the owner without the onlyOwner modifier, anyone can call this function and become the owner of the contract. The article recommends using the correct modifiers for functions, getting a second opinion, and using industry-standard libraries to prevent owner theft.

Bullet points

  • The article discusses security vulnerabilities in Solidity smart contracts.
  • Reentrancy attacks and owner theft are the two main vulnerabilities discussed.
  • Reentrancy attacks occur when a smart contract calls or sends Ether to an external address.
  • The DAO hack of 2016 is a real-life example of a reentrancy attack.
  • To prevent reentrancy attacks, use the transfer() function, follow the Checks→Effects→Interactions pattern, and implement a contract lock.
  • Owner theft occurs when a function modifier, such as onlyOwner, is not used correctly.
  • To prevent owner theft, use the correct modifiers for functions, get a second opinion, and use industry-standard libraries.

How To Secure Your Smart Contracts

Reentrancy and Owner Theft Attacks

Photo by chris panas on Unsplash

Prerequisites: A basic understanding of the Ethereum Blockchain and Smart Contracts.

Introduction

Tunnel vision, a pitfall that every developer has experienced. When developing new code for a specific purpose, it’s easy to become so focussed on solving a particular problem that we miss something important.

This is especially true when transitioning from one technology to another. For example: If you come from a background in javascript, it’s unlikely you’d have much concern for overflow exploitation, yet in Solidity, it needs to be addressed.

We’re going to go through some weaknesses that are inherent in Solidity: Reentrancy Hacks and Owner Logic Theft.

Reentrancy Attacks

What

Smart contracts will often need to call or send ether to an external address. This type of operation is inherently vulnerable to reentrancy.

To perform a reentrancy attack, the attacker deploys a malicious contract to the network. This contract intends to manipulate the logic of the target contract into sending Ether to it, thus invoking its fallback function. The fallback function then recalls the target contract during the execution, to drain the target contract of Ether.

Photo by Bret Kavanaugh on Unsplash

How

Take the following function inside a vulnerable target contract:

function withdraw() external {
uint amount = balances[msg.sender];
require(msg.sender.call.value(amount)());
balances[msg.sender] = 0;
}

Assuming balances is a mapping of addresses to integer values, the function performs the following actions:

  • Retrieve the amount of Ether to send to the caller.
  • Send that amount to the caller
  • Set the balance of the caller to be zero

So long as the withdraw function is being called by a non-contract address, the logic is fine. However, if the sender is a contract address, this function can be exploited to drain the contract.

Here is a simple fallback function within our malicious contract:

function() external payable {
while(calls < 10){
calls++;
reentrancyContract.withdraw();
}
}

Assume that calls is a state variable defined as 0 in the constructor. When the target contract attempts to send Ether to the malicious address where this contract resides, this fallback function is called. If calls is less than 10, the withdraw() function on the target contract will be called again. This repeats recursively until it is called 10 times, draining the target contract of 10 times more than intended.

This happened in real life. In 2016, the DAO hack caused huge shockwaves throughout Ethereum and the Blockchain industry. Its consequences are still being felt today.

Preventing Reentrancy Attacks

There are several ways to protect your contracts from reentrancy attack.

The first is to use transfer() to send Ether. In previous versions of Solidity, contracts needed to use the call() method, in which no gas limit was set by default. transfer() on the other hand currently has a limit of 2,300 gas units, which is about enough to emit an Event. With that limit, any complex recursive execution would run out of gas almost immediately.

Use the Checks→Effects→Interactions pattern. When writing code for sensitive operations, make sure you initially:

  1. Run checks: require() statements.
  2. Then make the changes that affect state variables: balance -= withdrawAmount;.
  3. Then finally, perform the interaction: address.transfer(withdrawAmount).

Another technique is to lock the contract, also known as a mutex. Use a state variable to determine if a contract function is currently being executed. If the code is locked, no function can be called until the lock is released.

Owner Logic Theft

What

Function modifiers allow developers to identify who can call each function in a contract depending on their privilege. Where the keywords public and private are common to developers of other languages, Solidity also allows for custom modifiers.

A common modifier pattern is onlyOwner. This is where a state variable owner records the address which deployed the contract. Any functions with this modifier require that the caller address be equal to the owner.

The custom modifier looks like this:

modifier onlyOwner {
    require(msg.sender == owner);
    _;
}

Functions which use this modifier can be declared like this:

function somethingImportant() public onlyOwner { … }

Becoming the owner of a contract is powerful, as the owner usually has the highest privileges to interact with the contract.

Photo by Shane Avery on Unsplash

How

OnlyOwner contracts will often have a function to change the owner:

function setOwner(address _newOwner) public {
    owner = _newOwner;
}

The problem here is that this function is public, meaning anyone can call this function and therefore become the owner of the contract. An additional modifier of onlyOwner should be applied to the function declaration.

Preventing Owner Theft

Follow good practices and always use the correct modifiers for functions. This goes without saying, but as mentioned earlier, tunnel vision can sometimes contribute to letting issues like these slip through the net.

Get a second opinion. There are plenty of communities and companies that perform smart contract audits. If you’re deploying sensitive contracts, I highly recommend that you utilise these services.

Use industry-standard libraries. OpenZeppellin has a suite of libraries and contracts which are thoroughly audited by the community before being released.

Conclusion

Reentrancy is the most high profile exploit because of the DAO hack. Steps have been taken to mitigate the risk, as the introduction of transfer() , but you still need to be wary of it and code accordingly.

Always use best practices, industry standards and libraries accepted by the community. Inheriting contracts scrutinized by many developers means you can have more confidence that they work as designed.

Use static analysis tools and audit your code.

Read part 2 which discusses Arithmetic Under and Overflow vulnerabilities and how to mitigate them.

Learn More

If you enjoyed this post and want to learn more about Smart Contract security, Blockchain Development or the Blockchain Space in general, I highly recommend signing up to the Blockgeeks platform. They have courses on a wide range of topics in the industry, from Coding to Marketing to Trading. It has proven to be an invaluable tool for my development in the Blockchain space.

Follow me for more articles on Blockchain development like this one.

Get Best Software Deals Directly In Your Inbox

Blockchain
Ethereum
Solidity
Smart Contracts
Programming
Recommended from ReadMedium