Smart Contract Security on Celo with sFuzz

Smart Contract Security on Celo with sFuzz https://celo.academy/uploads/default/optimized/2X/b/b1483b6e7adb85a8277db69baa1d8b4b9369c361_2_1024x576.jpeg
none 0.0 0

Introduction

If you hail from the Web2 background, you’ll probably understand the relevance of security and code analysis in software development particularly where the code is meant to handle large volumes of data. Don’t get it twisted, Web3 is not an exception. Further diligence and extra care are required where Web3 comes to play. In 2022, report shows the industry lost around $2Billion to exploiters due to smart contract vulnerabilities and bugs. As Celo developers, it then becomes pertinent to exercise precautions and due diligence while building for the blockchain.

What the post is all about

As an expert in smart contract development, I prioritize security over other aspects which is why I have prepared this guide to awaken your consciousness for security while building on the Celo network. Itemized below is a list of tutorials I have written about using security tools. You can get started with any of them.

Prerequisites

This guide is well-suited for the intermediate-level developer hence the only condition for attempting it is to have experience working with Solidity programming language.

Requirements

Walking you through the steps required for setting up an environment to use sFuzz to detect vulnerabilities in your smart contracts, the following is needed prior to the next stage.

Note: This guide does not include testing smart contracts. It is assumed that you already know how to deploy contracts to the Celo networks, and can test them for correctness. Check Build feature-rich, persistent dapp on celo using wagmi to get started.

  • A Linux-based OS. If you’re a window user, Please refer to setting up WSL.

sFuzz

sFuzz is one of the fuzzing tools for detecting smart contracts’ weaknesses or rather put “vulnerabilities”. Fuzzing also called fuzz testing is an automated method used in software testing that searches for weaknesses by injecting several unexpected, invalid, or malformed inputs into the system to test its security strengths.

Installation

Installation of sFuzz can be done in two ways but in this guide, we will only use the first method.

  1. Using image
  2. Installing from the source code

Preparing and setting up a project

Before installing sFuzz, let’s get our environment ready. We will use Ubuntu - a Linux-based operating system. If you’re on Windows, please follow the guide on the official page to install WSL so you can be able to run Linux OS on your machine. If you wonder which version of Ubuntu I used, Ubuntu v 20.04 was good for me.

Create a fresh project folder. I named mine smart-contract-security-on-celo-with-sfuzz

mkdir smart-contract-security-on-celo-with-sfuzz

Instaling sFuzz using image

To use this method, Docker is required. If you don’t have docker installed, follow the steps below otherwise skip this part. The steps explain how to install and use Docker directly from the Ubuntu terminal without using the Docker desktop. Me, I prefer this method since the Docker desktop consumes a lot of my system resources.

Installing docker on Ubuntu

Docker is an application that eases the processes of building applications all done in containers. Containers are isolated environments for running applications for several reasons such as avoiding dependency clash etc. They’re synonymous with virtual machines, but containers are more resource-friendly, more portable, and are often more dependent on the host operating system.

To get docker running on Ubuntu, perform the following steps. Also, you might want to ensure the Ubuntu server is well set up by following the steps highlighted here

  • Update your packages. This is the first thing you’d want to do before performing any installation on Ubuntu

    sudo apt update
    
  • Install prerequisite packages to allow apt use packages over HTTPS:

    sudo apt install apt-transport-https ca-certificates curl software-properties-common
    
  • We need the CGP key for official Docker repository

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    ```
    
  • The next step is to add the Docker repository we imported to apt sources

    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
  • We’ve got a few additions, so let’s update our packages so they can be recognized

    sudo apt update
    
  • Now we can register that we want to install Docker. Note we are getting it directly from the Docker repository. This is different from installing via Ubuntu

    apt-cache policy docker-ce
    
  • You should see the registration success message in the terminal. The next step is to install Docker in Ubuntu. This command should start the Docker daemon, and enable it to start on boot.

    sudo apt install docker-ce
    
  • We will confirm if Docker is running

    sudo systemctl status docker
    
  • The output in the terminal should like this

    docker.service - Docker Application Container Engine
      Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
      Active: active (running) since Fri 2022-04-01 21:30:25 UTC; 22s ago
    TriggeredBy: ● docker.socket
        Docs: https://docs.docker.com
    Main PID: 7854 (dockerd)
       Tasks: 7
      Memory: 38.3M
         CPU: 340ms
      CGroup: /system.slice/docker.service
              └─7854 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
    
  • Next is to add our username to the docker group so we can have access to run docker otherwise, we have to use sudo command each time we want to run docker commands.

    sudo usermod -aG docker <Add your username here>
    

    Ex. sudo usermod -aG docker bobelr . At this point, you will need to log out of the Ubuntu server and log in again to effect the changes we made.

  • Now type this command

    su - <username previously created>
    
  • Lastly, to confirm if the username is added to the Docker group, run

    group
    

Possible error

in the course of running the Docker command, you may encounter an error of this type

System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

Solution

To solve it, do the following:

sudo apt-get install cgroupfs-mount
sudo cgroupfs-mount
sudo service docker start

This will start the Docker daemon in the background. To confirm if Docker is running, and that we can access Docker to download images, run the following command.

docker run hello-world

Now that Docker is successfully installed, let’s go back to installing sFuzz using the Docker image.

  • Inside the project directory we created earlier, create a contracts folder where the Solidity files will be kept. So I have smart-contract-security-on-celo-with-sfuzz/contracts/

    mkdir contracts/
    
  • Create and add a contract file under the contracts folder. Name it BountyHunt.sol

  • Paste the sample code below

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.0;
    
    contract BountyHunt {
      mapping(address => uint) public bountyAmount;
      uint public totalBountyAmount;
    
      modifier preventTheft {
        _;
        if (address(this).balance < totalBountyAmount) revert();
      }
    
      function grantBounty(address beneficiary, uint amount) public payable preventTheft {
        bountyAmount[beneficiary] += amount;
        totalBountyAmount += amount;
      }
    
      function claimBounty() public preventTheft {
        uint balance = bountyAmount[msg.sender];
        (bool sent,) = msg.sender.call{value: balance}("");
        if (sent) {
          totalBountyAmount -= balance;
          bountyAmount[msg.sender] = 0;
        }
      }
    
      function transferBounty(address to, uint value) public preventTheft {
        if (bountyAmount[msg.sender] >= value) {
          bountyAmount[to] += value;
          bountyAmount[msg.sender] -= value;
        }
      }
    }
    

What the code does

This is a sample BountyHunt contract for approving and claiming bounties. The contract was written with some bug intentions.

The contract holds two state variables declared as follows:

  • The public bountyAmount variable. A key-value pair mapping bounty hunters to their earnings.

    mapping(address => uint) public bountyAmount;
    
  • totalBountyAmount variable tracks the total available bounty payouts at any point in time.

    uint public totalBountyAmount;
    
  • Modifier preventTheft is a check to ensure the total available bounty payable to hunters does not exceed the balance Celo balance of the contract.

    modifier preventTheft {
       _;
       if (address(this).balance < totalBountyAmount) revert();
     }
    
  • The grantBounty function accepts a beneficiary of an address type and an amount as inputs. It awards the amount value to the beneficiary account.

    function grantBounty(address beneficiary, uint amount) public payable preventTheft {
       bountyAmount[beneficiary] += amount;
       totalBountyAmount += amount;
    }
    
  • ClaimBounty() accepts no argument, takes the preventTheft modifier and simply allows the caller to claim bounty reward.

    Tip: We didn’t implement any check-effect pattern here, hence pay attention to the risk.

    function claimBounty() public preventTheft {
      uint balance = bountyAmount[msg.sender];
      (bool sent,) = msg.sender.call{value: balance}("");
      if (sent) {
          totalBountyAmount -= balance;
          bountyAmount[msg.sender] = 0;
      }
    }
    
  • transferBounty() accepts an address and unsigned integer type allowing anyone to call it and transfer the bounty of value to any address of their choice.

      function transferBounty(address to, uint value) public preventTheft {
         if (bountyAmount[msg.sender] >= value) {
            bountyAmount[to] += value;
            bountyAmount[msg.sender] -= value;
       }
    }
    

Note: The above code is intended for this tutorial and is not meant for production.

  • Pull the docker image

    docker pull sfuzz/ethfuzzer
    

  • The next step is to start the container. But we will need to map the path to the contracts folder to the container so that when docker is invoked, it already loaded all of the Solidity files in the contracts. We will perform all of these in one step.

    docker run -it -v /replace-with-path-to-the-contract-folder/:/home/contracts/ sfuzz/ethfuzzer
    

Note: You have to use the absolute path to the file you want to map to the container. To get an absolute path to a file in Ubuntu, use either of the following methods. Either of the commands will print the path to the terminal

  1. Using the find command
    $ find $PWD -type f -name <fileName>
    
  2. The realpath command
    realpath <fileName>
    
  3. Using the readlink command
    readlink -f <fileName>
    

Example:

Editing the command docker run -it -v /path/to/contracts/folder/:/home/contracts/ sfuzz/ethfuzzer in my terminal, I have the path replaced as:

docker run -it -v /home/bobelr/celosage/smart-contract-security-on-celo-with-sfuzz/:/home/contracts/ sfuzz/ethfuzzer

To test the code in the contract file residing in the folder we mapped to the container, we have to run:

cd /home/ && ./fuzzer -g -r 0 -d 120 && chmod +x fuzzMe && ./fuzzMe

The command switches to the root user and invokes sfuzz on the contracts. You should see the result shown in the terminal that looks like the one shown in the image below.

Output

fuzzerresult

For image src, click on the image

The image displays the overall progress of the process being run.
sFuzz will generate a set of output files under the contract folders where the result of the tests are kept. It gives you the full details and extra data of what’s happening to your contracts relative to the tasks it was set to perform.

sFuzz is able to detect the following set of vulnerabilities.

  • Gasless send
  • Integer overflow
  • Integer underflow
  • Exception disorder
  • Reentrancy
  • Timestamp dependency
  • Blocknumber dependency
  • Dangerous delegatecall
  • Freezing ether

Conclusion

From the set of vulnerabilities itemized above, if your contract is prone to any of them, sFuzz alerts you, so you can make proper amendments before deploying to the live network. Moreso, be aware that using sFuzz does not guarantee that your dApp is 100% safe. There are a couple of known and undiscovered bugs. Simplicity reduces the risk of attack. Modular programming enforces code readability. Use the best practices and approaches to writing secure smart contracts.

Next step

As a developer targeting the Celo blockchain, start using security tools to scrutinize your contracts. If you have questions you wish to ask, please leave a comment or contact us on Discord. We’ll be happy to hear from you. To learn more about Celo and how to build your dream project, please refer to the Celo documentation and the Celo Developers resources.

About the Author​

Isaac Jesse , aka Bobelr is a smart contract/Web3 developer. He has been in the field since 2018, worked as an ambassador with several projects like Algorand and so on as a content producer. He has also contributed to Web3 projects as a developer.

References

18 Likes

The code examples is it a complete smart contract and what do you mean by advanced in smart contracts?

7 Likes

Welcome to Celo academy @Kola

5 Likes

Fantastic news! Your proposal has landed in this week’s top voted list. As you begin your project journey, remember to align with our community and technical guidelines, ensuring a high quality platform for our developers. Congratulations! :mortar_board: :seedling:

8 Likes

Thank you @Celo_Academy You’re an amazing leader. Keep up the good spirit. :+1:

13 Likes

Your proposal looks interesting. :+1:

4 Likes

Thank you again @OtakIsreal

12 Likes

looking forward to this

1 Like

Looking forward to this tutorial

i’ll review this in 1-2 days @bobelr

2 Likes

Okay. Fine

10 Likes

@bobelr I went through your tutorial, good job.

A few suggestions.

  • Add a github repo for your code.
  • add some explanations on the smart contract you used for the tutorial.
  • Add an About the Author section at the end of the tutorial.

Lemme know when they have been implemented

5 Likes

@4undRaiser All fixed. You may check now. Thanks.

11 Likes

@bobelr check your dms

4 Likes

@4undRaiser Kind reminder. I have completed the fix.

7 Likes

Glad to know I actually voted for this tutorial. I like how this tutorial talk on smart contract security on Celo with sFuzz seems to be a comprehensive and well-written guide. It covers important topics such as the relevance of security in Web3 development, the vulnerabilities and risks associated with smart contracts, and the installation and usage of sFuzz for detecting vulnerabilities. The tutorial also provides a sample code for a BountyHunt contract and explains its functionality. Overall, it appears to be a valuable resource for intermediate-level developers interested in enhancing the security of their smart contracts on the Celo network. Nice one @bobelr

3 Likes

Thank you for reading

12 Likes

Welcomr bro, It’s always good to read your tutorial here.

@bobelr good job with the code explanation and github link. you can move to publish now

3 Likes

Thank you brother

13 Likes