Sending Ether Without Getting Hacked

WebSculpt
4 min readSep 16, 2021

--

Sending Ether in a safe and secure way can be a confusing topic, especially when the resources a developer uses to learn this stuff can span across different versions of Solidity and multiple Ethereum upgrades.

Photo by DrawKit Illustrations on Unsplash

Are your notes up-to-date? Let’s discuss Withdrawing Ether from a Smart Contract and how to attack it with Re-entrancy.

Sending Ether: The current standard is to avoid using transfer()

Solidity’s transfer() was used because gas costs were assumed to stay the same, and a contract’s fallback() function used to consume less than 2300 gas.

Gas costs are subject to change.

A contract that is using transfer() or send() is depending on the forwarded gas amount of: 2300.

The alternative is to use call() which does not depend on a set gas cost. Instead call() can forward all gas or use a set amount of gas.

How to receive Ether?

Think about how a contract will receive the Ether that you are sending.

As of version 0.8.3 a contract receiving Ether must have at least one of these functions:

  • receive() external payable
  • fallback() external payable

receive() is called if msg.data is empty, otherwise fallback() is called.

Note that a reentrancy attack can occur from either the receive() or the fallback() function of the attacker, as these are what your contract will call when you actually send the Ether.

Which method to use when sending Ether?

call() is currently what you should be using, here is an example:

Taking security seriously.

In a reentrancy attack, the attacker can withdraw from your contract while also withdrawing from your contract in their fallback() or receive() — this is where it gets harder to understand.

If you want to send Ether directly to a contract address, the fallback (or receive) function of the destination contract will be called.

The attacker can use their own fallback() against your contract, so your contract needs to handle all state changes before calling external contracts.

Here is an example of a vulnerable withdraw function:

Note that in the snippet of code above, the balance is not set to 0 until after the call() is sent. If the attacking contract were to call withdrawFromAttackee() in their fallback() … they would be withdrawing more Ether than they had in their balance.

The Attack

  • Attacker initiates a withdrawal from Attackee
  • Attackee sends Ether to Attacker (the withdrawal)
  • Attacker’s fallback is called
  • The Attacker’s fallback initiates a withdrawal from Attackee

Preventing the Attack

What is the real problem with the vulnerable code?

It is making the call that sends the Ether before setting the balance to 0 — this is an easy mistake for Solidity new-comers to make. The order that code is executed is very important in Solidity.

Checks-effects-interactions pattern

The simplest defense for a reentrancy attack is a common pattern. The checks-effects-interactions pattern outlines the order in which you should perform tasks inside a function.

  • Checks: handle all checks, assertions, or requires
  • Effects: then resolve all effects to the state of the contract (like setting balances to 0)
  • Interactions: calls to other contracts (like sending Ether)

By following this pattern, the Attacker cannot initiate a secondary withdrawal from the Attackee before the Attacker’s balance is zero-ed out.

Want more security?

Check out the ReentrancyGuard by OpenZeppelin … I will get into this and the idea of a mutex in another post.

Want to try it yourself?

You can try this out in Remix.

Deploy 1st Contract

  • Copy the code into Remix and Deploy the Attackee Contract
  • This is the Contract that will be drained by the Attack
  • It needs to be funded
  • It has a way to withdraw
  • It has a way to view the balance
Solidity Reentrancy example 1 of 4 — Remix
Remix Deploy — Attackee

Give it some Ether

  • After it is deployed, type a number into the VALUE field
  • Select ‘ether’ from the VALUE drop-down
  • Click the red button to deposit the VALUE into the contract
  • Click the ‘getBalanceFro…’ button to see the current balance
Solidity Reentrancy example 2 of 4
Remix Deploy —Fund Attackee

Now we need to deploy the Attacker contract

  • Set CONTRACT drop-down to Attacker
  • Copy the Attackee address
  • Paste into the Deploy field
  • Click the Deploy button
Solidity Reentrancy example 3 of 4
Remix Deploy — Attacker

Perform the attack

  • Type a number into the VALUE field
  • Hit ‘performAttack’ (red button on the Attacker)
  • Hit both of the ‘getBalance’ buttons
  • You will see that the Attackee has been drained and the
  • Attacker now has all the Ether
Solidity Reentrancy example 4 of 4
Remix Deploy — Perform Attack

How to support more posts like this

If you want to see more of this content, please donate ETH or ERC-20 Tokens here: 0xd6355A6b745985342ebf168e1EC965dc612704b1

--

--

WebSculpt

Blockchain Development, coding on Ethereum. Condensed notes for learning to code in Solidity faster.