Excited by the opportunity to host a one-of-a-kind CTF at the Denver Sui Builder House, together with my colleagues from OtterSec we created a framework that makes crafting CTF challenges a breeze. In the upcoming article, we'll take a deep dive into the world of the Sui blockchain, explore its mechanics, and discover how we can leverage specific features to build and run CTF challenges. Without further ado, let's buckle up and get started!
OtterSec and Sui Foundation announce the SuiTF Challenge for Denver #SuiBuilderHouse attendees! pic.twitter.com/ONT4oXOWgx
— OtterSec (@osec_io) February 28, 2023
Below are the links to participate along with the prizes!
— OtterSec (@osec_io) February 28, 2023
Winners will be KYC’d to ensure they have attended the Denver Builder House.
Good luck! pic.twitter.com/57CPljmuSS
If you are new to the world of blockchain, chances are that the word "SUI" doesn't sound familiar, but for those in the loop, it's a term that has been making quite a buzz lately.
But anyway, let's start at the very beginning...
SUI (pronounced "swee") is a Layer-1 proof-of-stake blockchain with smart contract capabilities.
The project is the brainchild of the Mysten Labs team - a company founded by former employees of Meta (Facebook), who also contributed to the Libra Project.
SUI operates in epochs (1 epoch = 24 hours).
Each epoch is managed by a committee of validators, and this committee changes from one epoch to another.
This setup facilitates the delegation of stakes to validators, which helps strengthen voting power and earn a share of the fees.
Now, all validators are, of course, orchestrated by a Consensus Engine, which in this case is Bullshark
+ Narwhal
as the mempool.
For a more detailed explanation, I recommend referring to the official documentation.
sender's address
, gas input
, gas price
, maximum gas budget
, epoch
, type + associated data
, authenticator
, and expiration
Alright, now that we have a general idea about this blockchain, it's time to address the elephant in the room - who is MOVE?
MOVE is the programming language used to create smart contracts. It also traces its roots back to the offices of Meta, but for SUI specifically, there have been a few modifications:
When you're just getting started with Sui, one of the first things you do is download sui-cli (Sui Command Line Interface). This tool enables you to perform a variety of operations, for example:
sui client new-address ed25519
sui move new demo_package
sui move build
sui client publish $PATH_TO_PACKAGE/demo_package --gas 0x1337 --gas-budget 30000
sui client call --function function_name --module sui --package 0x31337 --args $OBJECT_ID "[1, 3, 3, 7]" --gas-budget 30000
sui start
sui client call --package 0x2 --module sui_system --function request_remove_validator --args 0x5 --gas-budget 10000
sui client objects
sui move test
One operation that is particularly interesting is the testing operation. Tests are designed to help programmers test a specific functionality of the program. There are three annotations
to the Move source language: #[test]
#[test_only]
#[expected_failure]
.
However, unit tests do not support certain features that programmers may wish to test (e.g. there is no way in Move unit tests to query if a specific event has been emitted) and that is because unit tests don't use an Adapter.
Because of that, the Expected Value Tests framework was created, it helps with writing tests that need to check for events, and expect specific states:
Alright, now let's now open Pandora's box, and for that, we'll direct our attention to sui/crates/sui-transactional-test-runner/src.
This is a library that implements a SuiTestAdapter
, which is later sent to move/testing-infra/transactional-test-runner/src/framework.rs along with a source file.
The run_test_impl
function handles parsing the source program and extracting tasks delimited by the //#
identifier. Once the tasks are extracted, they are executed in order.
They are handled in the handle_command function.
Quite interesting, isn't it?
Essentially, this adapter provides access to all the primary functionalities required for running and interacting with a smart contract.
Why don't we try to harness its potential and transform it into a CTF framework instead of just a simple Test framework?
Spoiler alert, that's exactly what we did! https://github.com/otter-sec/sui-ctf-framework/blob/main/src/lib.rs ðŸ¤
Using only 4 of the predefined tasks, we created a lightweight framework, which creates a SuiTestAdapter
object along with an arbitrary number of accounts.
Once the adapter is created, we can use it to publish the Challenge smart contract & Solution smart contract under the challenge and solution accounts.
With the modules published, we can call any entry public fun
from the Solution module which is supposed to solve the challenge.
Lastly, the view_object
function allows you to either debug or check if certain objects were altered according to the intended solution.
Alright, let's cut to the chase and dive into a real challenge.
At justCTF 2023, an online Jeopardy-style CTF where OtterSec served as one of the sponsors, participants were faced with a sui challenge called "baby_otter.
This particular challenge provides a prime illustration of how the sui-ctf-framework
seamlessly integrates with a CTF scenario. challenge source
A quick glance at the source code of this problem reveals two main components: a Server and a Client.
The server can be found in the framework
folder, while the client resides in the framework-solve
folder.
Both the server and the client are Rust applications, so their source code can be found in the src
subfolder.
Let's analyze them one by one.
named_addresses
(challenge and solution) and two addresses
(challenger and solver) are initialized, followed by a call to the sui_ctf_framework::initialize
function, which returns an adapter
.publish
the challenge smart contract as bytecode
(CompiledModule).
It then waits for the connected client to send the compiled bytecode of the solution smart contract, which will be published as well.
sui_ctf_framework::call_function
function, thus calling the public function solve
of the solution contract baby_otter_solution
.is_owner
function of the baby_otter_challenge
smart contract.
If it doesn't return an error, it means the task has been solved, and the flag will be sent to the client
baby_otter_solution.mv
and sends it to the server.There you have it, a simple method to run a challenge where the author defines a move module representing the task, and it's up to the participants to implement a move module representing the solution to the task.
Now, since we've come this far, let's briefly see what the challenge module is all about.
The file framework/chall/sources/baby_otter_challenge.move
defines an init
function, which is called during the contract publish operation.
This function publicly exposes an object named Status
that holds the challenge status (solved or unsolved).
Furthermore, we can observe two peculiar local functions named gt
and hh
, along with two public entry functions named request_ownership
and is_owner
.
The is_owner
function, which we recall from the server source, appears to simply check if Status.solved == true
.
On the other hand, the request_ownership
function takes an input called ownership_code : vector
, which is passed to the hh
function.
The result of this function is then compared to the value 1725720156
. If they match, Status.solved
will be set to true otherwise, it remains false.
So the objective of the challenge is to determin a special code which generates the value 1725720156
This can be achieved in three different ways:
CRC32
algorithm.
This realization could have been achieved either through experience and recognizing certain constants that give away the implementation,
or simply by passing these two functions to ChatGPT
, which is capable of recognizing the implementation:
Move Prover
and then send it to fedex@osec.io will receive some cool swag 😎
I hope it has been a pleasant read, and that you've learned at least one new thing today.
I got plenty of bookmarked ideas, but unfortunately time doesn't allow me to share them all
Until next time, take care and God bless! 👋