How To Secure Your Blockchain Smart Contracts

Reentrancy and Owner Theft Attacks


Alex Roan

3 years ago | 4 min read

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


Tunnel vision is 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


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. The intention of this contract is to manipulate the logic of the target contract into sending Ether to it, thus invoking its own 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
Photo by Bret Kavanaugh on Unsplash


Take the following function inside a vulnerable target contract:

function withdraw() external {    uint amount = balances[msg.sender];    require(;    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 are 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 are 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 actually 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. 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


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.


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. Obviously 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.


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.


Created by

Alex Roan







Related Articles