Soma is a cross-platform CTF problem container manager.
Downloading and running a problem is as easy as running three commands.
$ soma add https://github.com/PLUS-POSTECH/simple-bof.git
$ soma build simple-bof
$ soma run simple-bof 31337
CTF problems often contain public files. You can also fetch them easily with soma.
# this will copy public files to the current working directory
$ soma fetch simple-bof
Add soma.toml
that describes your problem in your project's root directory. The config file below shows an example of it.
name = "simple-bof"
[binary]
os = "ubuntu:16.04"
cmd = "./simple-bof"
[[binary.executable]]
path = "build/simple-bof"
public = true
[[binary.readonly]]
path = "flag"
That's all! Soma gets enough information to run your binary from these 12 lines of configuration.
Soma will use reasonable default value for the other things not specified such as default working directory, file permissions, fork daemon, and standard stream buffering. Of course they can be manually configured if needed :)
If your project contains more than one problems, you can put soma-list.toml
that lists each problem directory.
problems = [
# these subdirectories contain soma.toml files
"problem1",
"sub-dir/problem2"
]
Soma requires Docker to be installed on the system.
Soma is in its alpha stage. Currently, it supports running a binary file under a socat
fork server.
We hope to add more scenarios to it, notably a web problem setup which uses PHP with MySQL and a Python-based setup.
Issues related to 0.1.0 release are marked with 0.1.0
milestone.
Use cargo install soma-ctf
if you have Rust toolchain on your system.
This command will download the latest released version from crates.io and build the executable file.
$ cargo install soma-ctf
Or, download the precompiled binary from the release page.
If you want to try the cutting-edge master branch, you can also build your own binary from the source. In detail, clone this git repository and run cargo install
.
$ git clone https://github.com/PLUS-POSTECH/soma.git
$ cd soma
$ cargo install
Add / Create | Remove | |
---|---|---|
Repository | add | remove |
Image | build | clean |
Container | run | stop |
Additionally, update
Soma repository has soma.toml
or soma-list.toml
in its top level directory and can contain one or more problems. To use Soma, start by adding problem repositories. We will use soma-bata-list
as an example throughout this document.
You can add a repository with add
subcommand:
$ soma add https://github.com/PLUS-POSTECH/soma-bata-list.git
add
subcommand takes a repository source as an argument, which is https://github.com/PLUS-POSTECH/soma-bata-list.git
in the command above. Soma supports the following repository sources:
Git repository (only HTTPS without authentication is supported for now)
e.g., https://github.com/PLUS-POSTECH/soma-bata-list.git
Local file system directory
e.g., /home/linux-user/soma-repo
By default, Soma will parse the name of the repository from the repository source string. For example, https://github.com/PLUS-POSTECH/soma-bata-list.git
will return soma-bata-list
and /home/linux-user/soma-repo
will return soma-repo
as a default repository name. If you want to use another name, you can use --name [NAME]
flag. This flag will make Soma register the repository under the given name.
Also, note that there are a few requirements for repository names. See "Name rules" section for more details.
Soma uses Docker to manage problem images and containers. An image is a snapshot of a problem environment, and a container is an instance of an image. In order to run a problem, you should build the problem image first.
Soma will automatically generate Dockerfile from the problem manifest when you execute the build command. You can build a problem image by executing the following command:
$ soma build r0pbaby
# or
$ soma build soma-bata-list.r0pbaby
As you can see, there are two different ways to select a problem. See "Problem query" section for more details.
When the problem image is ready, you can run the problem container! The problem container can be started with the following command:
$ soma run r0pbaby 13337
# or
$ soma run soma-bata-list.r0pbaby 13337
Here, 13337
indicates the port number which binds to the problem container. This port number will expose the problem to the host network. In this example, r0pbaby
is accessible through your.host.address:13337
. Try nc localhost 13337
on your machine to start solving the problem.
run
command requires a port number for now, but we are planning to support automatic port binding in the future (see #64).
CTF problems often provide a few attachments (usually problem binaries). There is a dedicated subcommand to fetch these files to your current working directory:
$ soma fetch r0pbaby
# or
$ soma fetch soma-bata-list.r0pbaby
After finish solving a problem, stop and remove the problem container by invoking:
$ soma stop r0pbaby
# or
$ soma stop soma-bata-list.r0pbaby
You may want to remove existing problem images for several reasons (e.g., free disk space, remove repository). You can clean up existing images by:
$ soma clean r0pbaby
# or
$ soma clean soma-bata-list.r0pbaby
Before trying to remove the image, check if any associated container exists. Soma will reject the command if there is a running container from the image.
If you want to remove a registered repository, use the following command:
$ soma remove soma-bata-list
There should be no problem image or container associated to the repository when you use this command. Use clean
and stop
command to remove them if necessary. Auto pruning for your convenience will be implemented in the future (see #115).
You can update and sync repositories with update
command:
$ soma update soma-bata-list
Note that problem containers that are already running are untouched by this command. You might want to stop, build, and run the problem again after updating a repository.
There are two ways to identify a problem. You can specify only the name of a problem (e.g., r0pbaby
) or the full name of a problem containing what repository it is in (e.g., soma-bata-list.r0pbaby
).
If the problem name is unique (i.e., the problem name is used only in a single repository), you can use the former method. Otherwise, you must use the latter or Soma will give you an error message that the problem name is found among multiple repositories.
All repository and problem names should follow the docker name component rules with no .
(i.e., ^[a-z0-9]+((?:_|__|[-]*)[a-z0-9]+)*$
, see Docker regexp definitions for more details). This measure is to prevent Soma from behaving bad when malicious input is provided. We chose docker name component rules as repository and problem names are substrings of Docker image name. And 'no .
' rule is added because Soma utilizes .
as a name separator.
In order to support Soma, a repository should have soma.toml
or soma-list.toml
in its top level directory. soma-list.toml
lists each subdirectory that contains a soma.toml
file, and soma.toml
is the name of Soma problem manifest file.
If your repository contains only one problem in its top level directory, soma.toml
can be used directly without soma-list.toml
.
soma.toml
syntaxEach problem in a repository needs a manifest file, soma.toml
, in its directory. The problem manifest file contains necessary information for Soma to manage problems. We will discuss the syntax of soma.toml
section by section with this example.
name = "simple-bof"
work_dir = "/home/simple-bof"
[binary]
os = "ubuntu:16.04"
cmd = "./simple-bof"
[[binary.executable]]
path = "build/simple-bof"
public = true
[[binary.readonly]]
path = "flag"
target_path = "/you_cannot_guess_this_very_secret_flag_name"
Manifest root contains two metadata for the problem.
name = "simple-bof"
work_dir = "/home/simple-bof"
name
fieldThe name
field of the root section defines the name of the problem. Soma will recognize this field (not the directory name) as the problem name.
work_dir
field (optional)The work_dir
field of the root section contains the path of the working directory inside the problem image. Default value for this field is the home directory of the user whose name is same with the name of the problem (for the example above, "/home/simple-bof"
).
[binary]
sectionThe [binary]
section contains information required to use binary subconfiguration. Binary subconfiguration supports a scenario which runs an executable and pipes standard input and output through a TCP connection with a fork daemon; this is one of the most common setups in CTF competitions.
[binary]
os = "ubuntu:16.04"
cmd = "./simple-bof"
[[binary.executable]]
path = "build/simple-bof"
public = true
[[binary.readonly]]
path = "flag"
target_path = "/you_cannot_guess_this_very_secret_flag_name"
os
fieldThe os
field indicates what OS flavor is used by the problem. This field is currently redirected into the parent image name of Dockerfile
. However, it will only allow pre-selected choices in the future.
cmd
fieldThe cmd
field defines how to run the problem binary. The specified binary will be executed through socat daemon.
[[binary.executable]]
, [[binary.readonly]]
sections contain file entries of the subconfiguration.
[[binary.readwrite]]
, [[binary.with-permissions]]
, and [[binary.fetchonly]]
are reserved for a future implementation (see #50 and #84).
path
fieldThe path
field contains a relative path to the file from the problem directory. Supporting external sources such as URL is planned in the future (see #114).
target_path
field (optional)The target_path
field specifies where the file should be copied inside the problem image. This field defaults to work_dir
in the root section.
public
field (optional)File entries with public
field set to true
will be copied to the current working directory when users invoke fetch
subcommand. This field has a default value of false
.
Other subconfigurations for common CTF setups such as apache-php7
, python-uwsgi
, or mysql
are planned to be supported in a future release (see #50). Subconfiguration syntax is designed to support multi-configuration problem in the future, which will be handled similarly to Docker compose.
soma-list.toml
syntaxproblems
fieldsoma-list.toml
contains a single field problems
which is an array of relative paths to each problem directory.
problems = [
# these subdirectories contain soma.toml files
"simple-bof",
"hard/complicated-bof"
]
openssl
(Required by openssl-sys
crate).clippy
and rustfmt
.
rustup component add clippy rustfmt
hooks
directory to .git/hooks
.Soma is written with Rust and utilizes Cargo as a building and testing system.
You can test, build, and run with the following command.
$ cargo test
$ cargo build
$ cargo run
Licensed under either of
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.