Welcome to the Cardano Capture the Flag (CTF) challenge by Invariant0, the former Vacuumlabs Auditing team!
It is a game where Cardano developers and enthusiasts can try to exploit purposely vulnerable smart contracts and learn about the most common security issues and how to prevent them. In a way, you can try the job of auditors on some common Cardano vulnerabilities.
We believe this will provide the community with educational materials needed to write more secure smart contracts.
- hello_world β A sanity check to verify that everything's setup correctly.
- sell_nft β Try to buy two NFTs by paying less than their stated prices!
- vesting β Try to skip the vesting period.
- multisig_treasury β Unlock treasury without all the signatures.
- tipjar β Try to prevent others from tipping into a Tip Jar.
- purchase_offer β Sell an NFT while collecting more money than offerred.
- tipjar_v2 β The Tip Jar was patched. Try to prevent others from tipping into the upgraded Tip Jar.
- multisig_treasury_v2 β The treasury was upgraded. Try to unlock it without all the signatures again.
- lending β Hack a simple peer-to-peer lending protocol.
- multisig_treasury_v3 β The treasury was upgraded again! Let's compromise it once again πͺ.
- king_of_cardano β Become the King of Cardano and prevent others from overthrowing you.
These should be easier and they build on each other; please solve them in order. They are unrelated to the original series.
- bank_00_core_invariant β Find an overlooked core rule and empty the bank.
- bank_01_deposit_vulnerability β Exploit a flaw in the deposit mechanism to steal funds from an account.
- bank_02_mutual_exclusion β Withdraw from a different account by recognizing which transaction fields you can manipulate to your advantage.
- bank_03_increase_balance β Empty the bank by increasing your balance to the clouds.
- bank_04_lifecycle β Focus on every step of a UTxO lifecycle to empty the bank again.
- bank_05_misconfiguration β Config UTxO is a central component; misconfigure and profit.
More bank challenges coming soon!
Each task has its own folder. It consists of three main parts:
- The validators written in Aiken. They are located in the
validators
folder. You can compile them by runningaiken build
in the root directory of the task. Make sure that you use (old) Aiken versionv1.0.26-alpha
for the original series. New bank series uses new Aiken versions. - The off-chain code. We use the
Lucid Evolution library and
Blockfrost API to interact with the Preview testnet. The off-chain
scripts contain:
- Code deploying the smart contracts onto the Cardano Preview testnet.
- Sample interaction with the deployed contract. This is the part you should modify and / or extend to exploit a vulnerability.
- Tests that verify if you successfully exploited the vulnerability or not.
- A README file containing task-specific instructions. The first sample hello_world task contains a bit more info on how exactly to play.
The tasks are more or less of an increasing difficulty. The suggested path is to go from a simple hello_world task up. The tasks' README's also explain more in the beginning, leaving more up to you in the more complex levels.
To be able to run the first task, just 4 steps of setup are needed:
- Install Node.js and Yarn.
- Install dependencies by running
yarn install
in the project root. - Install Aiken.
- Copy the template config file in
common/offchain/config_temp.ts
tocommon/offchain/config.ts
.
That's it and you can now play in the local Lucid Evolution emulator! The emulator is great for testing smart contracts fast.
You can now try the first hello_world task (read its README carefully) and get to the yellow test results.
To get to the green test results and really finish the task, it is required to finish it in the testnet environment. To set this up, there are a few more steps you need to take:
-
Generate a private key and address. We prepared a private key generation code for you, you can run it by:
yarn gen-keys
Put your private key into the
PRIVATE_KEY
constant located in thecommon/offchain/config.ts
.DO NOT REUSE A PRIVATE KEY THAT YOU USE FOR MAINNET! Generate a new one instead!
-
Get Blockfrost API key by registering at https://blockfrost.io/ . Put this API key into the
BLOCKFROST_API_KEY
constant in thecommon/offchain/config.ts
. -
You need some starting ADA in your wallet so you can interact with the Cardano testnet. Request tADA into your address (see the newly created
key.addr
file) from the Cardano Faucet. The environment we use is Preview Testnet. -
You are good to go! To verify that everything is set up correctly, try solving the very first sample task: Hello World. You should see green test results when everything is done correctly.
There are videos in the videos folder. Check them out if you're struggling with making the framework work. Beware, it can contain spoilers, especially the first video that solves the hello_world task entirely. The later videos do not explain the solutions. They are here mostly to demonstrate Catalyst milestones' completion. You should strive to find the solutions yourself.
Sometimes, LucidEvolution errors out when it submits a transaction. This usually happens on the real testnet when you wait for too short between two different transactions from your wallet. It can also happen when running different tasks on testnet too close to each other, as for statistics we submit a successful solution record to the chain when you solve a task on the testnet and we don't wait for that transaction's completion. That might make the following testnet transaction fail if you run it too soon after that.
In the meantime, if you encounter this error, you can try to change the default value of
CONFIRMS_WAIT
in the config to a higher number. This makes it wait
for more confirmations before it follows up with the next transaction, increasing the time and
making the chance of such errors smaller. Additionally, if you run more tasks just after each other
on testnet, such as using ./scripts/run_all.sh
with testnet enabled, we recommend waiting for a
few seconds at least after each task.
We have also encountered transactions in which Lucid Evolution wasn't able to determine the correct
amount of ADA that needed to be placed into the UTxO, resulting in the transaction erroring out with
Not enough ADA leftover to cover minADA
, even if a sufficient amount of ADA was available. To
address this issue, our transactions now deposit a fixed amount of at least 2 ADA into each UTxO.
From our experience, the time required for a transaction validation are ever changing on the preview
testnet. If it takes too long, try to decrease the default value of CONFIRMS_WAIT
in the
config to a lower number. Be careful not to change it too low,
otherwise you might get errors during submissions.
This is why we run the transactions on the Lucid Evolution emulator first. Only if they pass there we try to replicate it on the testnet as well.
For the original series, we use old Aiken version v1.0.26-alpha
and aiken-stdlib
version 1.6.0
(you can see this in aiken.toml
files). As Aiken syntax changed and newer Aiken versions are
unable to compile this,
you need to downgrade Aiken to build those validators. The bank series use new Aiken and stdlib
versions.
Successful solutions are recorded on the testnet. There is a script called scoreboard.ts
that
displays the number of successful solutions in a succinct way. You can run it using the command
yarn scoreboard
.
Join our Discord. You can ask questions, share your solutions and discuss anything (not only) security related. Alternatively, you can share your thoughts and feedback with us at info@invariant0.com as well.
The smart contract code in the examples is purposely vulnerable. DO NOT copy parts of the code into your project, as you may copy a vulnerability, too. Beware that the code may contain more than a single vulnerability.
This project is ever evolving. Please refer to the Changelog to see the changes over time.
Licensed under GPL-3.0. Full license text can be found here.