Giter Club home page Giter Club logo

ctfcli's Introduction

ctfcli

ctfcli is a tool to manage Capture The Flag events and challenges.

ctfcli provides challenge specifications and templates to make it easier to generate challenges of different categories. It also provides an integration with the CTFd REST API to allow for command line uploading of challenges and integration with CI/CD build systems.

ctfcli features tab completion, a REPL interface (thanks to Python-Fire) and plugin support for custom commands.

WIP: ctfcli is an alpha project and changes will happen. Be sure to pin versions and read the CHANGELOG when updating.

Installation and Usage

ctfcli can be installed with pipx as an executable command:

pipx install ctfcli

Alternatively, you can always install it with pip as a python module:

pip install ctfcli

To install the development version of ctfcli directly from the repository you can use:

pip install git+https://github.com/CTFd/ctfcli.git

1. Create an Event

Ctfcli turns the current folder into a CTF event git repo. It asks for the base url of the CTFd instance you're working with and an access token.

❯ ctf init
Please enter CTFd instance URL: https://demo.ctfd.io
Please enter CTFd Admin Access Token: d41d8cd98f00b204e9800998ecf8427e
Do you want to continue with https://demo.ctfd.io and d41d8cd98f00b204e9800998ecf8427e [Y/n]: y
Initialized empty Git repository in /Users/user/Downloads/event/.git/

This will create the .ctf folder with the config file that will specify the URL, access token, and keep a record of all the challenges dedicated for this event.

2. Add challenges

Events are made up of challenges. Challenges can be made from a subdirectory or pulled from another repository. GIT-enabled challenges are pulled into the event repo, and a reference is kept in the .ctf/config file.

❯ ctf challenge add [REPO | FOLDER]
Local folder:
❯ ctf challenge add crypto/stuff
GIT repository:
❯ ctf challenge add https://github.com/challenge.git
Cloning into 'challenge'...
[...]
GIT repository to a specific subfolder:
❯ ctf challenge add https://github.com/challenge.git crypto
Cloning into 'crypto/challenge'...
[...]

3. Install challenges

Installing a challenge will create the challenge in your CTFd instance using the API.

❯ ctf challenge install [challenge]
❯ ctf challenge install buffer_overflow
Found buffer_overflow/challenge.yml
Loaded buffer_overflow
Installing buffer_overflow
Success!

4. Sync challenges

Syncing a challenge will update the challenge in your CTFd instance using the API. Any changes made in the challenge.yml file will be reflected in your instance.

❯ ctf challenge sync [challenge]
❯ ctf challenge sync buffer_overflow
Found buffer_overflow/challenge.yml
Loaded buffer_overflow
Syncing buffer_overflow
Success!

5. Deploy services

Deploying a challenge will automatically create the challenge service (by default in your CTFd instance). You can also use a different deployment handler to deploy the service via SSH to your own server, or a separate docker registry.

The challenge will also be automatically installed or synced. Obtained connection info will be added to your challenge.yml file.

❯ ctf challenge deploy [challenge]
❯ ctf challenge deploy web-1
Deploying challenge service 'web-1' (web-1/challenge.yml) with CloudDeploymentHandler ...
Challenge service deployed at: https://web-1-example-instance.chals.io
Updating challenge 'web-1'
Success!

6. Verify challenges

Verifying a challenge will check if the local version of the challenge is the same as one installed in your CTFd instance.

❯ ctf challenge verify [challenge]
❯ ctf challenge verify buffer_overflow
Verifying challenges  [------------------------------------]    0%
Verifying challenges  [####################################]  100%
Success! All challenges verified!
Challenges in sync:
 - buffer_overflow

7. Mirror changes

Mirroring a challenge is the reverse operation to syncing. It will update the local version of the challenge with details of the one installed in your CTFd instance. It will also issue a warning if you have any remote challenges that are not tracked locally.

❯ ctf challenge mirror [challenge]
❯ ctf challenge verify buffer_overflow
Mirorring challenges  [------------------------------------]    0%
Mirorring challenges  [####################################]  100%
Success! All challenges mirrored!

Operations on all challenges

You can perform operations on all challenges defined in your config by simply skipping the challenge parameter.

  • ctf challenge install
  • ctf challenge sync
  • ctf challenge deploy
  • ctf challenge verify
  • ctf challenge mirror

Challenge Templates

ctfcli contains pre-made challenge templates to make it faster to create CTF challenges with safe defaults.

ctf challenge new
                ├── binary
                ├── crypto
                ├── programming
                └── web
❯ ctf challenge new binary
/Users/user/.virtualenvs/ctfcli/lib/python3.7/site-packages/ctfcli-0.0.1-py3.7.egg/ctfcli/templates/binary/default
name [Hello]: buffer_overflow

❯ ls -1 buffer_overflow
Makefile
README.md
WRITEUP.md
challenge.yml
dist/
src/

Contributions welcome on improving the challenge templates to make CTF challenges better for everyone!

Challenge Specification

ctfcli provides a challenge specification (challenge.yml) that outlines the major details of a challenge.

Every challenge generated by or processed by ctfcli should have a challenge.yml file.

The specification format has already been tested and used with CTFd in production events but comments, suggestions, and PRs are welcome on the format of challenge.yml.

Plugins

ctfcli plugins are essentially additions to the command line interface via dynamic class modifications. See the plugin documentation page for a simple example.

ctfcli is an alpha project! The plugin interface is likely to change!

Sub-Repos as alternative to Sub-Trees

ctfcli manages git-based challenges by using the built-in git subtree mechanism. While it works most of the time, it's been proven to have disadvantages and tends to create problems and merge conflicts.

As an alternative, we're currently experimenting with the git git subrepo extension. This functionality can be enabled by adding a use_subrepo = True property to the [config] section inside a ctfcli project config.

Subrepo has to be installed separately, and is not backwards compatible with the default subtree. Once challenges have been added by using either method, they will not work properly if you change it, and you will have to add the challenges again.

ctfcli's People

Contributors

coldheat avatar ctxhamza avatar dependabot[bot] avatar ifelawal avatar milymilo avatar pl4nty avatar ppaslan avatar reteps avatar riccardotornesello avatar roerohan avatar rustybower avatar sebastianpc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ctfcli's Issues

ctf challenge verify

We should have a way to have ctfcli verify that a given installed challenge matches the local state. This could help in situations where perhaps an admin deletes files or changes a description.

Visible and Hidden states reversed

When creating a new challenge (blank) the state of the challenge is by default set to "Hidden".

Upon adding and installing the challenge and opening CTFd the challenge will be "Visible" and not "Hidden" as per the challenge.yml.
When changing the state in the challenge.yml to "Visible" (and syncing the change) the challenge will be set to "Hidden" in CTFd.

`ctf challenge deploy` with registry succeeds but faults with TypeError

ctf challenge deploy chal registry://myregistry fails with TypeError: cannot unpack non-iterable NoneType object, because it tries to unpack data that isn't returned when deploying to a registry:

status, domain, port = DEPLOY_HANDLERS[url.scheme](

def registry(challenge, host):
# Build image
image_name = build_image(challenge=challenge)
print(f"Built {image_name}")
url = urlparse(host)
tag = f"{url.netloc}{url.path}"
subprocess.call(["docker", "tag", image_name, tag])
subprocess.call(["docker", "push", tag])

Show debug information better

We should have a command or switches that will show what we are reading/loading.

Some useful information:

  • Target URL
  • API Key
  • Location of the .ctf/config file
  • Contents of the .ctf/config file

Healthcheck parameter

We can include a healthcheck field in challenge.yml that specifies a script for us to execute to check if a challenge is viable. We can pass it the connection info in CTFd as well.

Adding challenges through external repo fails

Hi,

I may be wrong but it's not possible to add a git repo with ctf challenge add {repo_url} at first. This command relies on git subtree which is a shell script. this script calls ensure_clean to check the HEAD reference but our repo is empty at this point.

I would add a git commit --allow-empty -m "Project created" after the git init in main.py.

Explore subtree and storing branch information

git subtree was something being research when ctfcli was being implemented but it was discounted for one reason or another. I think it's worth revisiting that discussion as well as seeing how we can vary challenge repos by branch. We would need to record branch information in order to support git subtrees anyway.

ctf challenge test

We should have a ctf challenge test command. Some ideas:

  • Test that Dockerfiles build
  • Test the created image
  • Test chal is solveable

Install and Sync all commands

Add a command to install and Sync commands that allow you install all challenges in a specified directory. Allowing it to loop through the directories recursively would be nice too. That way I can store my challenges in different directories based on category.

IE: I would run ctf challenge install Challenges

where the challenges folder looks like this.
Challenges
Crypto
rsa1
challenge.yml
rsa2
challenge.yml
Binary
intoverflow
challenge.yml
bufferoverflow
challenge.yml
...

json decode error on "ctf challenge install"

ctfcli is installed with pip and initialized. Then a new challenge is created with ctf challenge new web and given the name "Hello". On ctf challenge install Hello the following result appears:

Found Hello/challenge.yml
Loaded Hello
Traceback (most recent call last):
  File "/usr/local/bin/ctf", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.8/site-packages/ctfcli/__main__.py", line 74, in main
    fire.Fire(CTFCLI)
  File "/usr/local/lib/python3.8/site-packages/fire/core.py", line 138, in Fire
    component_trace = _Fire(component, args, parsed_flag_args, context, name)
  File "/usr/local/lib/python3.8/site-packages/fire/core.py", line 466, in _Fire
    component, remaining_args = _CallAndUpdateTrace(
  File "/usr/local/lib/python3.8/site-packages/fire/core.py", line 675, in _CallAndUpdateTrace
    component = fn(*varargs, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/ctfcli/cli/challenges.py", line 94, in install
    installed_challenges = load_installed_challenges()
  File "/usr/local/lib/python3.8/site-packages/ctfcli/utils/challenge.py", line 28, in load_installed_challenges
    return s.get("/api/v1/challenges?view=admin", json=True).json()["data"]
  File "/usr/local/lib/python3.8/site-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/local/Cellar/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/local/Cellar/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/Cellar/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

image field plans?

What are the current plans for the image field in the chal spec? One thing to keep in mind for deployment is that some chals might want multiple containers e.g. a sql db + webserver, so perhaps specifying a single image is not enough. For the CTF we recently ran, we had a docker-compose file for each challenge.

Unable to install challenges with the same name (but different category)

The function install() in ctfcli/cli/challenge.py implements a check to see if there is already a challenge with the same name. This check does this as follows:

if c["name"] == challenge["name"]:
    click.secho(
        "Already found existing challenge with same name. Perhaps you meant sync instead of install?",
        fg="red",
    )

As a result, ctfcli refuses to install challenges with the same name regardless of the category.

I have been using a modified variant of ctfcli that determines if a challenge is already present based on the challenge name and challenge category.

if c["name"] == challenge["name"] and c["category"] == challenge["category"]:
    click.secho(
        "Already found existing challenge with same name. Perhaps you meant sync instead of install?",
        fg="red",
    )

The same check is also present in the function sync_challenge() in ctfcli/utils/challenge.py and should be changed in similar fashion to sync the correct challenge.

I guess this is the intended/expected behaviour?

Add a finalize command

The challenge.yml file should only be created once a challenge is done. So there should probably be a ctf challenge finalize kind of command. It can parse the relevant sections that it needs and then as the author for other data.

Feedback suggested that manually generating the yml file was unnecessary work.

Pages Support

I think we may want to consider adding the ability to manage pages from ctfcli. It would help because our editor interface isn't great and this might be a good way to make that easier.

ctf challenge update not working properly

ctf challenge update isn't working properly in some situations for unknown reasons. We need to come up with a better strategy on gathering and updating challenges gathered from multiple locations.

Writeups should have their own folder

I think writeups should have their own folder instead of being just WRITEUP.md in the root folder. This would make it more organized and also let writeup content not pollute other content.

I am completely rewriting ctfcli for a personal project and I wanted to share it here

I am making an "all in one" setup script using docker-compose and kubernetes with some bash and python tying it all together to make a CTFd based pentesting/malware analysis/threat actor analysis/learning project thing-a-ma-bob for my community to learn with but I needed to integrate ctfcli into the overall schema and ended up rewriting it...

I am integrating as many open source challenges as I can into the repository, it is a repository manager with ctfd/git operations that interfaces with the CTFd server. It has a masterlist yaml file that is a representation of the entire challenge set.

I have yet to fully morph the API calls to ctfd into a more abstract schema but I think you should just see it.

I have gotten the masterlist for the challenge repository working, it has read/write methods that turn code to yaml, I am now working on the getter/setter/listing methods, I am going to also get the templating and "build a challenge on the command line" thing going,

The root of the repository is here
https://github.com/mister-hai/sandboxy/tree/master/data/CTFd

the tool itself is here
https://github.com/mister-hai/sandboxy/tree/master/data/CTFd/ctfcli

And the modified open source challenges are in the "challenges" folder

I didnt know how to inform anyone of this properly, I just an trying to contribute to both of our communities and provide a platform for accelerated learning. I am trying very hard to document everything as well as I would have appreciated when I was learning the beginner stuff... I still am learning newb stuff lmao!

Thank you for your time!

Support existing git repositories

We are a monthly event and we already host our challenges in a git repo even when not using CTFd: https://github.com/montrehack/challenges

It would be nice if ctfcli would have detected that it was already in a git tree and wouldn't do anything about it. For now, I'm going to create a distinct repo for our event but I wanted to let you know of that use case.

This tool is a very nice addition to the CTFd ecosystem by the way! Thanks!

Add ability to use CTFd config variables in challenges

We should in theory be able to use CTFd configuration variables in things like challenge descriptions and such. I think this is would be a really useful idea.

Perhaps something like {{ ctf_name }} or {{ ctf_description }}inside of challenge descriptions.

I think we can also have a variables section within the .ctf/config section as well.

Files not being parsed relative to challenge.yml file

I am still experiencing this error when running the version when I installed this using pip.

This is the pip and pip3 info.

$ pip3 show ctfcli
Name: ctfcli
Version: 0.0.4
Summary: Tool for creating and running Capture The Flag competitions
Home-page: UNKNOWN
Author: Kevin Chung
Author-email: [email protected]
License: Apache 2.0
Location: /home/user/.local/lib/python3.6/site-packages
Requires: requests, colorama, appdirs, fire, cookiecutter, Pygments, click, pyyaml
Required-by: 

And the error I get is as follows.

$ ctf challenge sync Over_The_Top
Found Over_The_Top/challenge.yml
Loaded Over the Top
Syncing Over the Top
Traceback (most recent call last):
  File "/home/user/.local/bin/ctf", line 8, in <module>
    sys.exit(main())
  File "/home/user/.local/lib/python3.6/site-packages/ctfcli/__main__.py", line 74, in main
    fire.Fire(CTFCLI)
  File "/home/user/.local/lib/python3.6/site-packages/fire/core.py", line 138, in Fire
    component_trace = _Fire(component, args, parsed_flag_args, context, name)
  File "/home/user/.local/lib/python3.6/site-packages/fire/core.py", line 471, in _Fire
    target=component.__name__)
  File "/home/user/.local/lib/python3.6/site-packages/fire/core.py", line 675, in _CallAndUpdateTrace
    component = fn(*varargs, **kwargs)
  File "/home/user/.local/lib/python3.6/site-packages/ctfcli/cli/challenges.py", line 134, in sync
    sync_challenge(challenge=challenge)
  File "/home/user/.local/lib/python3.6/site-packages/ctfcli/utils/challenge.py", line 95, in sync_challenge
    files.append(("file", open(f, "rb")))
FileNotFoundError: [Errno 2] No such file or directory: 'src/main.c'

If I make a directory and file in the the folder I am running the file from, the command works just fine. But if I do not have it in that directory, it fails. It should be running it from path that is relative to the challenge.yml location.

Currently I am bypassing this (and recursivly searching folders) using this command.

find . -type d -exec echo '(cd {} && ctf challenge sync)' ';' | sh

Generate challenge README from challenge.yml

We currently generate the README with nothing in it. We can actually generate it from the challenge.yml. We will need some way to promote the filling in of challenge.yml before pushing though as the current flow it's a little easy to miss a step. Maybe with some kind of git hooks.

Renaming challenge breaks subtree

If you rename a challenge directory or move it it will break the git subtree integration. We may need to look into a mv or rename functionality.

Install challenge KeyError: 'data'

Getting the following error when trying to use ctfcli. This only began to happen once I upgraded to ctfd 3.4.1 and does not occur on ctfd 3.4.0.

Traceback (most recent call last):
  File "../../utils/ctf", line 11, in <module>
    load_entry_point('ctfcli==0.0.9', 'console_scripts', 'ctf')()
  File "/home/user/.local/lib/python3.8/site-packages/ctfcli/__main__.py", line 78, in main
    fire.Fire(CTFCLI)
  File "/home/user/.local/lib/python3.8/site-packages/fire/core.py", line 138, in Fire
    component_trace = _Fire(component, args, parsed_flag_args, context, name)
  File "/home/user/.local/lib/python3.8/site-packages/fire/core.py", line 466, in _Fire
    component, remaining_args = _CallAndUpdateTrace(
  File "/home/user/.local/lib/python3.8/site-packages/fire/core.py", line 675, in _CallAndUpdateTrace
    component = fn(*varargs, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/ctfcli/cli/challenges.py", line 143, in install
    installed_challenges = load_installed_challenges()
  File "/home/user/.local/lib/python3.8/site-packages/ctfcli/utils/challenge.py", line 28, in load_installed_challenges
    return s.get("/api/v1/challenges?view=admin", json=True).json()["data"]
KeyError: 'data'

Use of markdown in challenge description

Hi!

Maybe i'm missing something but I couldn't find how to use the standard markdown syntax in the description field of the challenge file. If I try to use multiline syntaxe for yaml it seems that every special char is escaped.

Could you provide an example ?
Thanks

Add a topics field

Add a field where we can specify what topics a challenge is about. This should probably also be aligned with CTFd as well. Perhaps we have a set of internal topics.

Ability to set CTFd config

We need a way to share CTFd configuration between events. It's too easy to forget that a configuration setting needs to be set.

Add templates as a new type of subcommand

We need a specific templates folder so that we can create new custom templates without having to add them to the main repo. Should be very similar to the plugins subcommand.

Something like:

ctf templates dir
ctf templates install <url>
ctf templates list
ctf templates uninstall <name>

Remote image pull before deploy

We should have some way to be able to store and use saved copies of images so that we aren't always rebuilding images on deploy.

Essentially if we build once, we should be able to store that image and then reuse it.

Add ability to not upload tags/files/etc

Syncing or installing is an all or nothing procedure. Instead we shouold have switches/options to be able to choose what data to not include during installation.

Expand linting

  • Lint that Dockerfiles EXPOSE a port
  • Lint that if your challenge spec doesn't provide an image that you don't provide a Dockerfile
  • Lint that all files being searched for are provided

Synchronisation should not change visibility status

When updating challenges with the cli tool, the challenges are set to state "visible", regardless of their previous state.

Expected behavior:
Challenge visibility state stays hidden when being hidden beforehand.

Actual behavior:
Challenge is set to "visible", even if being hidden before.

Add a command to store in different folder

Add a command to allow you to create the git folder in a different place than the default /home/user/.git

The reasoning is that some of the .git folders have other files in it that may conflict with the install such as secret files. Additionally, I may want to run multiple instances on the same computer.

A decent solution may be creating individual folders in the .git folder for each individual project. IE /home/user/.git/ctf1 /home/user/.git/ctf2 etc.

Issue in challenge type dynamic.

The spec does not have the following fields for challenges having type dynamic:

  • Initial Value
  • Decay Limit
  • Final Value

Hence, you can only set challenges of standard type.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.