hardhat-ctf
v0.0.6
Published
Hardhat plugin for building solidity capture the flag (CTF) challenges.
Downloads
2
Maintainers
Readme
hardhat-ctf
Hardhat plugin for building solidity capture the flag (CTF) challenges.
What
This is a framework to implement a solidity CTF challenge.
Check the example folder for a sample challenge built using this plugin.
This plugin can be helpful when you:
- Don't want the flag to be available in the contract source code / bytecode.
- Want people to solve it in a competitive CTF environment where they get points for flags.
- Don't want people to be able to cheat by querying the RPC node for other people's transactions.
- Want to deploy the RPC server in a scalable stateless way in order to mitigate porential DDOS attacks.
- Want to provide a clean execution environment each time trying to solve the challenge (can be important when dealing with nonces or deploying contracts to specific addresses).
It achieves it by creating a temporary Hardhat Network for each websocket connection, initializing the challenge contracts, and allows the challenge author to hook into the RPC responses in order to inject the flag without it being on-chain.
Installation
npm install hardhat-ctf
Import the plugin in your hardhat.config.js
:
require("hardhat-ctf");
Or if you are using TypeScript, in your hardhat.config.ts
:
import "hardhat-ctf";
Tasks
This plugin provides the ctf-node
task, which allows you run a websocket hardhat network that creates a temporary hardhat network for each websocket connection, and can hook into the RPC responses to inject the flag.
This plugin provides the ctf-try
task, which allows you to try and solve the challenge.
Once you solved it locally, you can run the ctf-try
task with the --submit
flag to send the solution to the remote CTF node in order to obtain the real flag.
Configuration
This plugin extends the HardhatUserConfig
object with
ctfResponseHook
and ctfRemoteNode
fields.
The ctfResponseHook
field is only relevant for the npx hardhat ctf-node
task, while the ctfRemoteNode
is only relevant for the npx hardhat ctf-try --submit
task.
This is an example of how to set it:
import { ethers } from "ethers";
import { EthereumProvider, JsonRpcRequest } from "hardhat/types";
import { JsonRpcResponse, SuccessfulJsonRpcResponse } from "hardhat/internal/util/jsonrpc";
const config: HardhatUserConfig = {
solidity: "0.8.4",
ctfRemoteNode: "ws://ctf.example.com:8545",
ctfResponseHook: async (provider: EthereumProvider, rpcReq: JsonRpcRequest, rpcResp: JsonRpcResponse) => {
if (
rpcReq.method === "eth_call"
&& rpcReq?.params?.[0]?.to == "0x5fbdb2315678afecb367f032d93f642f64180aa3"
&& (rpcReq?.params?.[0]?.data || '').startsWith('0x95fdc999')
&& (rpcResp as SuccessfulJsonRpcResponse).result == ethers.utils.defaultAbiCoder.encode(["string"], ["CTF{mock_flag}"])
) {
(rpcResp as SuccessfulJsonRpcResponse).result = ethers.utils.defaultAbiCoder.encode(["string"], [process.env.CTF_FLAG ?? "Use the CTF_FLAG environment variable when running 'npx hardhat ctf-node'"]);
}
return rpcResp
},
};
Usage
Check out the example folder for an example project using this plugin to create a CTF challenge.
On the server running the CTF node which validates solutions and gives the flag to succesful contestants, run:
npx hardhat ctf-node
Then, edit the hardhat.config.ts
file to point the config's ctfRemoteNode
field to your server running ctf-node
, and give the folder to anyone who wants to try solving the challenge.