๐Ÿฏ
Honeypot

Branch Info

Author: Temirzhan Yussupov Source code: https://github.com/scaffold-eth/scaffold-eth-examples/tree/honeypot-example Intended audience: Intermediate Topics: Scaffold-eth basics, Solidity, Security

About The Project

This little side quest will allow you to explore the concept of "honeypot". Honeypots are smart contracts designed to look like an easy target for hacking while in fact they are not.
They look vulnerable to an unsophisticated attacker, but if he tries to โ€œbreak itโ€ he will lose his money instead.

Getting Started

Prerequisites

In order to understand how honeypots work, you should firstly understand what is a reentrancy attack. If you do not know what it is, please have a look at this and this.

Installation

Let's start our environment for tinkering and exploring how honeypots work.
  1. 1.
    Clone the repo first
git clone https://github.com/scaffold-eth/scaffold-eth-examples.git honeypot-example
cd honeypot-example
git checkout honeypot-example
  1. 1.
    Install dependencies
yarn install
  1. 1.
    Start your React frontend
yarn start
  1. 1.
    Spin up your local blockchain using Hardhatโ€‹
yarn chain
  1. 1.
    Deploy your smart contracts to a local blockchain
yarn deploy
Pro Tip: Use tmux to easily start all commands in a single terminal window!
This is how it looks like in my terminal:
Could not load image
image
If everything worked fine, you have to have something like this opened in your browser:
Could not load image

Smart contracts

Let's navigate to packages/hardhat/contracts folder and check out what contracts we have there.

Bank.sol

This smart contract will be deployed as a bait for a hacker.
Try to read the source code of this contract and if you are familiar with a reentrancy attack, you probably should note that this contract is indeed vulnerable.
function withdraw(uint _amount) public {
require(_amount <= balances[msg.sender], "Insufficient funds");
โ€‹
(bool sent, ) = msg.sender.call{value: _amount}("");
require(sent, "Failed to send Ether");
โ€‹
balances[msg.sender] -= _amount;
โ€‹
honeyPot.log(msg.sender, _amount, "Withdraw");
}
In a withdraw method, the bank smart contract firstly sends funds to the user and only after that deducts this amount from balances mapping.
This is exactly the reason for possibility to exploit reentrancy.
However, we have another interesting line at the end of the smart contract:
honeyPot.log(msg.sender, _amount, "Withdraw");
Just keep that in mind. We will return to it in a second.

Honeypot.sol

Let's now investigate our main smart contract there. This one will catch our hacker and reveal its address!
function log(address _caller, uint _amount, string memory _action) public {
if (equal(_action, "Withdraw")) {
revert("It's a trap");
}
}
The method that interests us the most is called log. It simply compares our _action to a value and if it is equal to "Withdraw", we revert our entire operation.
Why we use revert there? If you do know yet, all operations on Ethereum are atomic. It means that if at some moment some operation on chains of operations fails, the entire transaction is reverted and nothing changes on the blockchain.
Suppose that contract A calls contract B, and contract B calls contract C. Even if functions executed in contract A and B somehow mutated the state of blockchain, they will not be applied until we are sure that contract C function is executed correctly and there are no errors.
This is exactly the trick we are going to use to fool a hacker.

Attack.sol

This contract is a typical exploitation contract for a reentrancy attack. It contains fallback function that causes "vulnerable" contract to send us funds again and again..
fallback() external payable {
if (address(bank).balance >= target) {
bank.withdraw(target);
}
}
Once we withdraw money from a bank, our fallback will be called again because bank will send us some funds. This recursive nature will force bank to give us all its money... Or maybe not? ๐Ÿ‘น

Why the attack fails?

So you remember that our Bank has some unusual function call in the end of a withdraw method.
honeyPot.log(msg.sender, _amount, "Withdraw");
So how does it help us to prevent the attack?
Suppose that our Bank has 0.03 ETH locked in it. I am an evil hacker that wants to hack it. I deploy my Attack.sol contract and call my attack function along with 0.01 ETH sent with it.
We force bank to execute its withdraw again and again. However, at some point balance of the bank becomes less than 0.01 ETH and reentrancy comes to an end.
After that, the following lines are executed:
balances[msg.sender] -= _amount;
โ€‹
honeyPot.log(msg.sender, _amount, "Withdraw");
Hacker is almost successful but log function reverts the entire chain of operations and no money was stolen! However, we now know the address of a hacker ๐Ÿ˜Ž

Practice

Let's use our awesome frontend provided by scaffold-eth to make sure our assumption works fine.
Run two different sessions. One will be for a simple user and one will be for an evil hacker.
Pro tip: Instead of opening anonymous window in a browser, you can use containers provided by Firefox to have two separate sessions in a single browser window.
This is how it looks like for me:
Could not load image
My first tab is for a simple user and second tab is for a hacker.
Let's send some funds to a bank as a simple user using deposit function.
Could not load image
Our bank now has 0.04 ETH locked in it. Let's now try to steal it as a hacker. Navigate to a second tab and enter the Attack mode.
Could not load image
Now let's execute attack function and send 0.01 ETH along with our function call.
However, after we do it, we get an error saying that we Failed to send the Ether.
Could not load image
Now let's get rid of this log line in our Bank.sol to make sure that without it the attack works just fine.
honeyPot.log(msg.sender, _amount, "Withdraw");
Save your new contract and yarn deploy it again.
Let's now again deposit 0.03 ETH into a bank and try to attack from an attacker perspective by just sending 0.01 ETH.
Could not load image
In this case, we are able to steal all the money from a bank! This indeed proves that our log function from a Honeypot contract saves us from being hacked.

Additional resources

Honeypot is a concept that is used in a wild quite often. Here are some resources to learn more about this technique.

Contact

Join the telegram support chat ๐Ÿ’ฌ to ask questions and find others building with ๐Ÿ— scaffold-eth!
Copy link
On this page
Branch Info
About The Project
Getting Started
Prerequisites
Installation
Smart contracts
Why the attack fails?
Practice
Additional resources
Contact