Try   HackMD

Create a bug bounty for a project that uses OpenZeppelin contracts

In a previous tutorial you learned how to create a bug bounty for a simple smart contract project on Bug Buster from scratch. For the sake of simplicity, a project with no external dependencies was chosen, but real projects commonly depend on one or more external dependencies.

As you may remember, Bug Buster expects bounties to be submitted in a particular format. All the necessary files (such as source code, binaries, etc.) must be bundled as an archive file with tar and compressed with xz. The final product should be a file with the .tar.xz extension.

Therefore, you may conclude that for projects with external dependencies you should have only to include the dependencies' source code to the bundle. This logic is right, but there is a limit to the size of bundles that can be sent on-chain, which is around 95KiB, according to tests on Optimism Sepolia and Mainnet.

Built-ins: OpenZeppelin

In order to avoid bundles from surpassing the size limit, Bug Buster already includes commonly used artifacts in its execution environment. For smart contract projects, Bug Buster provides the OpenZeppelin smart contract library out-of-the-box. The library contains ERC interfaces and implementations, as well as other utility libraries and contracts.

In this new tutorial you will learn how to prepare a project that depends on OpenZeppelin to be submitted as a bug bounty on Bug Buster.

Creating the project

As we did in the first tutorial, let's create the project from scratch.

  1. Create a directory called counter
mkdir counter
cd counter
  1. Create a forge project in it
forge init
  1. Let's delete the files script/Counter.s.sol and tests/Counter.t.sol which are created by default, but will not be used in this tutorial.
rm script/Counter.s.sol test/Counter.t.sol
  1. By default, forge creates a smart contract on the src folder called Counter, which has a state variable called number, whose value can be set by calling the function setNumber and can be incremented by 1 calling the function increment. See the code below.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}
  1. Install OpenZeppelin executing the command below.
forge install OpenZeppelin/openzeppelin-contracts --no-commit
  1. Let's now use the Ownable contract from OpenZeppelin to restrict the setNumber and increment functions to the contract owner. For the sake of simplicity, we will assign the contract deployer as the initial owner. Substitute the contract's code for the one below.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract Counter is Ownable {
    uint256 public number;

    constructor() Ownable(msg.sender) {}

    function setNumber(uint256 newNumber) public onlyOwner {
        number = newNumber;
    }

    function increment() public onlyOwner {
        number++;
    }
}
  1. Edit the foundry.toml file to add the remappings config.
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
remappings = [
  "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
]
  1. it's time to create the assertions script (start.sh) file. This script will deploy the Counter contract and define the conditions to unlock the reward.
#!/usr/bin/env bash
source ./setup-exec-env.sh

>&2 echo "Deploying and registering project contracts..."
COUNTER=$(deploy_and_register src/Counter.sol Counter)

>&2 echo "Deploying exploit contract..."
EXPLOIT=$(deploy src/Exploit.sol Exploit)

>&2 echo "Running exploit..."
send "$EXPLOIT" 'run(address)' "$REGISTRY"

>&2 echo "Verifying contracts after exploit execution..."
number=$(cast call "$COUNTER" 'increment()(uint256)')

if [ "$number" -eq 0 ]
then
    >&2 echo "No exploit found."
    exit 1
else
    >&2 echo "Valid exploit!"
    exit 0
fi

Set the execution permission for this file using the following command:

chmod +x start.sh

  1. Download setup-exec-env.sh, which is the script that prepares Bug Buster's execution environment. Keep it with no modifications! :wink:
wget https://gist.githubusercontent.com/claudioantonio/9cfcbec91de8aef1376c56d34432386f/raw/a8bffe442b94190ad46fe5ff53b2911ea0afa8ee/setup-exec-env.sh
chmod +x setup-exec-env.sh
  1. Also download the Register contract and its interface which will allow easy access the Counter contract address.
cd src
wget https://raw.githubusercontent.com/crypto-bug-hunters/bug-buster/refs/tags/v0.10.0-alpha.1/tests/bounties/src/adder/src/Registry.sol
wget https://raw.githubusercontent.com/crypto-bug-hunters/bug-buster/refs/tags/v0.10.0-alpha.1/tests/bounties/src/adder/src/IRegistry.sol
  1. At this point, it is important to build the project to check if everything is working as expected.
forge build

Configuring remappings and creating the bundle

  1. Considering the project is compiling successfully locally, let's edit the foundry.toml file to change the remapping configuration in order to make the project to use Bug Buster's built-in installation for OpenZeppelin.
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
remappings = [
  "@openzeppelin/contracts/=/usr/share/forge-lib/openzeppelin-contracts/contracts",
]
  1. Now, you have everything set up to create your bounty's bundle. Just run the command below from the root folder to create the tar.xz file.
jq -r '.files|keys[]' cache/solidity-files-cache.json | \
xargs tar -cJf counter-bounty.tar.xz setup-exec-env.sh start.sh foundry.toml

Submitting the bug bounty

  1. Access Bug Buster in Testnet: https://preview.bugbuster.app/
  2. Click on Explore bounties.
  3. Click on Create bounty, and fill in the required form fields.
    • For the Token Address field, enter the address for any existing ERC-20 contract on Optimism Sepolia. For example, USDC address on Optimism Sepolia is 0x5fd84259d66Cd46123540766Be93DFE6D43130D7.
    • Upload the counter-bounty.tar.xz file.
  4. When ready, click on the Create button.
    • Your wallet will ask you to confirm the transaction. Do it and wait until the notification with your transaction confirmation arrives.
  5. Once the transaction is confirmed, navigate back to the Explore bounties page and your recently created bounty must be listed there (sometimes it takes 2-3 seconds to appear).

Testing the bug bounty

Now it's time to test if your bug bounty is running properly inside Bug Buster's execution environment!

  1. Click on your bounty card in the Explore bounties page.
  2. Confirm that your wallet is connected.
  3. Click on Submit exploit and, in the script tab, paste the code below that just calls the increment function.
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.27;

import {IRegistry} from "src/IRegistry.sol";
import {Counter} from "src/Counter.sol";

contract Exploit {
    function run(IRegistry registry) external {
        Counter counter = Counter(registry.get("Counter"));

        counter.increment();
    }
}
  1. Click on Test and wait until the execution ends. The expected result is a transaction reverted because the increment function was called by an account which is not the Counter contract owner.
server returned an error response: error code 3: execution reverted, data: 
"0x118cdaa7000000000000000000000000cf7ed3acca5a467e9e704c703e8d87f634fb0fc9"

Thank you!

Thanks for following this tutorial and learn more about Bug Buster!

If you have any comments, suggestions and/or doubts, please don't hesitate to reach out through our Telegram development community or our X account.