This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

CTFd-chall-manager

CTFd-chall-manager is the official chall-manager integration on CTFd.

1 - Get started

Ready to use quickstart to use chall-manager on CTFd.

1.1 - Setup

Learn how to install the plugin in your CTFd.

Goal

This tutorial will guide you through the installation and configuration of the CTFd-chall-manager plugin to use chall-manager.

Prerequisites

Ensure you have chall-manager running before starting this tutorial. You can find the relevant documentation for setup instructions.

Install the plugin

If you are not using the docker-compose.yml file, you need to clone the repository into CTFd/CTFd/plugins/ctfd-chall-manager.

# Clone the CTFd repository
git clone https://github.com/CTFd/CTFd

# Clone the plugin repository
git clone https://github.com/ctfer-io/ctfd-chall-manager CTFd/CTFd/plugins/ctfd-chall-manager

# Start Redis with Docker
docker run -d -p 6379:6379 redis:<version>

## Configure plugin to use redis serveur
export REDIS_URL=redis://localhost:6379

# Start CTFd
cd CTFd
python3 -m venv venv 
source venv/bin/activate 
pip3 install -r requirements.txt
python3 serve.py 
  
# Clone the plugin repository
git clone https://github.com/ctfer-io/ctfd-chall-manager

# Create Docker network
docker network create testing

# Start Redis with Docker
docker run -d --name redis-svc --network testing redis:<version>

# Start CTFd with Docker
docker run -d -p 8000:8000 -e REDIS_URL=redis://redis-svc:6379 -v ./ctfd-chall-manager:/opt/CTFd/CTFd/plugins/ctfd-chall-manager --network testing ctfd/ctfd:<version>
  

After completing this step, you should be able to access the plugin settings configuration in the CTFd UI.

The plugin is visible in the UI

If the plugin does not appear, verify your container volume mounts, then check the CTFd logs for import module entries, such as:

The plugin is visible in the logs

Configure the plugin to use chall-manager

To connect the plugin to chall-manager, go to the plugin settings.

The default configuration is:

Default plugin configuration

Adjust the plugin settings to match your environment, ensuring CTFd can communicate with chall-manager. For instance:

CTFd can successfully reach chall-manager

What’s next?

Congratulations! At this point, your setup is ready to use chall-manager for your CTF events.

1.2 - Create a challenge

Create a challenge and understand basic configuration.

Goal

In this tutorial, we will create a dynamic_iac challenge, a new challenge type introduced by the plugin.
If you are unfamiliar with the new attributes of the dynamic_iac challenge type, please refer to the related design.
For guidance on maintenance operations (e.g., modifying challenge attributes), please refer to the relevant guides. For details on the Infra-as-Code scenario, consult the appropriate documentation.

Create the Challenge

For this example, we will create a challenge where each player gets their own instance. The instance will cost user 2 mana units, must be destroyed after 10 minutes without maximum due date, and using the scenario from the no-sdk examples.

Here are the basic CTFd settings:

Key Value
Name example
Category example
Message example
Initial Value 500
Decay Function Logarithmic
Decay 10
Minimum Value 10

First, configure the scope. Since we want each player to have their own instance, disable the global scope.

Next, set the mana cost. Players will need to spend 2 mana to deploy their own instance of the challenge.

As mentioned, we want instances to be destroyed after 10 minutes of usage (600 seconds), without any due date.

Let the Until value empty, and configure de Timeout value at 600.

Then, provide the scenario archive. For this example, we’ll use demo-deploy.zip from the no-sdk examples.

Finally, click Create to set up the challenge.

What’s Next?

Congratulations! Your CTF installation is now configured to use the chall-manager for this challenge.

1.3 - Play the challenge

Visualize what the player will see during your CTF.

Goal

In this tutorial we will see all actions a user has access to in order to control its instances.

Prerequisites

At this step, we assume that you are a CTF player, the infrastructure is already configured and you understand the key concepts, if not please refer to associated design.

Differents challenges mode

You can combine the values Until and Timeout to have 4 modes according to your needs:

Players can control all combinaisons, but each has these specificities.

For all challenges, the default view (instance is not booted) will display a button to launch the instance, the mana cost and the remaining mana for current user.

To start the instance, click on the button.

None

This mode allows you to use your instance with no duration limit. Its deployment can cost you mana, but if you want to regain it, you need to destroy the instance. If you want to reset your instance (either because it is soft-locked or you corrupted it), you can restart it.

Until

This mode allows you to use the same actions as None mode, but the instance will be destroyed by the Janitor at a due date. Don’t worry, your mana will be automatically regained if the instance is janitored.

Timeout

This mode allows you to use the same actions as None and Until, but the instance will be destroyed by the Janitor n seconds after their start.

If you see that the Janitor will destroy you instance soon, you can renew the instance (reset the timer).

Both

With Timeout and Until, the plugin will display the timeout mode buttons, but Chall-Manager will take care of restricting or not the possibility of renewing the challenge based on the design.

What’s next ?

Congrat’s ! If you’ve made it this far, you’ll probably want to make a challenge for your event, and the plugin documentation doesn’t include explanations on how to do this, so please refer to the associated documentation.

Otherwise, you can learn more about the plugin’s design or advanced user guides here:

2 - Guides

Step by step guides for advanced usage.

2.1 - Settings

Manage the plugin settings.

Goal

This guide assumes you are a CTF administrator and you understand the key concepts. Before or during your event, you may need to configure or update the plugin. At the moment, you can configure the total amount of mana for Source or and the chall-manager API URL.

Configure with environment variables

To configure the plugin at CTFd startup, you can use the next environment variables:

Variable Default Description
PLUGIN_SETTINGS_CM_API_URL http://localhost:9090/api/v1 URL of Chall-Manager API
PLUGIN_SETTINGS_CM_MANA_TOTAL 0 Maximum mana that source are allowed to use

Configure in UI

To configure or perform an update, Go to CTFd Admin Panel > Plugins > chall-manager > Settings, (1) select the text input, edit it, then (2) submit form, as shown below:

2.2 - Challenge

Manage your dynamic_iac challenge.

Goal

Here, we assume that you are a CTF administrator, the infrastructure is already configured and you understand the key concepts. During or before your event you may need to change challenge attributes.

For all updates, go to the CTFd Admin Panel and edit the challenge (https://CTFD_URL/admin/challenges).

Change the global scope

When you arrive on the modification page, the value displayed is the one configured at the challenge level.

By editing this value, you trigger instances destruction, see workflow belows:

flowchart LR
    A[Update] --> B
    B[Update on CTFd backend] --> C 
    C{challenge global ?} 
    C -->|True|F[Destroy all instances]
    C -->|False| E[Destroy global instance] 
    E --> H[Send update payload to CM]
    F --> H

Change the mana cost

When you arrive on the modification page, the value displayed is the one configured at the challenge level.

By editing this value, you do not edit the existing coupons of this challenge. Also, you can organize sales periods.

Change Timeout

When you arrive on the modification page, the value displayed is the one configured at the challenge level.

You can change or reset this value, Chall-Manager will update all the computed until for instances.

Change Until

When you arrive on the modification page, the value displayed is the one configured at the challenge level.

You can change or reset this value, Chall-Manager will update all the computed until for instances.

Change the scenario

When you arrive on the modification page, you can download the current scenario archive.

By editing this value, you need to provide an update strategy.

The update can be long, depends on the update gap and the strategy.

2.3 - Panel

Prepare your event or perform batch deployment.

Goal

Here, we assume that you are a CTF administrator, the infrastructure is already configured and you understand the key concepts.

During your event you may need to deploy instances for users we will see several use cases. If mana is enabled, 1 coupon will be created for each user.

Deploy instances for users

There may be several reasons why administrators create instances for users. Here, we’ll imagine that the CTF hasn’t started yet, that it’s a CTF in team mode and that 10 teams are expected. For the 10 teams, we want to deploy 1 instance of the “generated-flag” challenge.

Go to CTFd Admin Panel > Plugins > chall-manager > Panel such as:

By clicking on the button, you will send several requests to CTFd. To make the user experience easier, we add a progress bar.

Once the progress bar is done (this mean instances are created on chall-manager), you can go the instances monitoring panel.

You can use this method during your event to deploy a hidden challenge (deploy instances before the challenge is release) for instance.

Deploy a global scope instance

Here, we’ll imagine that you did configure a challenge with global scope enabled. For the global challenge, you must put the pattern at 0.

Like the previous use case, you will get a progress bar while the instance are getting deployed.

2.4 - Instances

Monitor and Manage instances.

Goal

This guide assumes you are a CTF administrator with a properly configured infrastructure and an understanding of key concepts.

During your event, you may need to monitor or manage instances associated with Sources.

How to do it

Monitoring

To monitor instances, go to the plugin settings in the CTFd Admin Panel > Plugins > chall-manager > Instances section.

Administration

You can Renew or Destroy an individual instance by clicking the corresponding button in the instance row.

To perform actions on multiple instances, (1) select the instances using the checkboxes, (2) click the associated button, (3) then confirm your choice, as shown below:

2.5 - Mana

Monitor mana usage.

Goal

Here, we assume that you are a CTF administrator, the infrastructure is already configured and you understand the key concepts.

During your event you may need to monitor the consumed mana for Source.

How to do it

Access the plugin settings configuration in the CTFd Admin Panel > Plugins > chall-manager > Mana

3 - Design

Understand the CTFd integration of chall-manager.

3.1 - Architecture

Learn how we design the plugin.

Concept

As explained in the chall-manager documentation, we avoid exposing its API to prevent the risk of direct resource manipulation by players.

CTFd inherently provides functionalities like authentication, team management, scoring, and flag handling. By adding our plugin, CTFd can serve as both a challenge management platform for administrators and a request manager that acts as a proxy with user authentication, mana limitations, and more.

Overview

The basic architecture is straightforward: we have created new API endpoints for both administrators and users. These endpoints mainly handle CRUD operations on challenge instances.

API

AdminInstance

This endpoint allows administrators to perform CRUD operations on challengeId for a specified sourceId. Essentially, this endpoint forwards requests to the chall-manager for processing.

UserInstance

Unlike the AdminInstance endpoint, this one does not accept sourceId as a parameter. Instead, it automatically identifies the source issuing the request and checks mana availability before forwarding the request to the chall-manager.

UserMana

This endpoint handles GET requests to display the remaining mana of the source issuing the request.

Detailed Overview

The following diagram provides a more detailed view of how your browser interacts with the API endpoints and how these endpoints are mapped to the corresponding chall-manager endpoints.

3.2 - Challenge

Learn how we design the dynamic_iac challenge.

Overview

The plugin introduces a new challenge type called dynamic_iac, allowing the deployment of instances per user or team (referred to as a source). It works seamlessly with both user modes defined by CTFd (individual users or teams).

What’s new about this challenge type?

To get started, navigate to the challenge creation panel in CTFd and select the dynamic_iac challenge type.

Here’s what the plugin adds:

Global Scope

The global scope is a boolean setting that allows a single instance to be shared among all players.

For instance, in the following setup, challenges 1, 2, and 3 have the global scope enabled, while challenge 4 does not:

In this scenario, player X (yellow) and player Y (blue) will each have their own instances for challenge 4, but will share the same instance for challenges 1, 2, and 3. We recommend enabling this feature for static, stateless applications (e.g., websites).

Mana Cost

The mana cost is an integer representing the price users must pay in mana to deploy their own instance. Mana is refunded when the instance is destroyed. This system helps control the impact users have on the platform. For more details, see how mana works.

Until

The until setting allows you to specify a date and time at which instances will be automatically destroyed by the Janitor.

Example:
As a CTF administrator running a week-long event, you want a challenge available only on the first day. Set the Janitoring Strategy to Until, and configure the end date to DD/MM/YYYY 23:59.

As a player, you can start your instance anytime before this date and destroy it whenever you like before the deadline.

Timeout

The timeout is an integer that specifies, in seconds, how long after starting an instance the Janitor will automatically destroy it.

Example:
As a CTF administrator or challenge creator, you estimate that your challenge takes about 30 minutes to solve. Set the Janitoring Strategy to Timeout and set the value to 1800 seconds (30 minutes).

As a player, once you start your instance, it will be destroyed after 30 minutes unless renewed. You can also manually destroy the instance at any time to reclaim your mana.

Scenario Archive

The scenario is a zip archive defining the challenge deployment as a Golang Pulumi project. You can use examples or create your own using the SDK and Pulumi.

3.3 - Flag

Learn how we use flag variation to prevent shareflag.

Concept

Cheating is a part of competitions, especially cash-prize is involved. The player experience is particularly frustrating, so we try to minimize some of the common cases of cheating.

We’ve concentrated on the two most common cases, namely ShareFlag and FlagHolding:

  • ShareFlag: get or share flags with an another team.
  • FlagHolding: store flags to submit them on the very last moment.

How this work

We redefine the submit method of the dynamic_iac challenge to verify the generated flag against the current source.

Here’s the algorithm:

flowchart LR
    Submission --> A{Instance On ?}
    A -->|True| B{Instance I flag ?}
    A -->|False| Expired
    B -->|True| C{submission == I.flag}
    B -->|False| D{CTFd C flag ?}
    C -->|True| Correct
    C -->|False| D
    D -->|True| E{submission == C.flag}
    D -->|False| Incorrect
    E -->|True| Correct
    E -->|False| Incorrect

    %% I/O
    Submission
    Expired
    Correct
    Incorrect

On the submit method, we get all informations of the instance on chall-manager.

  1. We want the instance ON to prevent FlagHolding and make sure the submitted flag is the one in the instance.
  2. We check if the instance define a flag (if you don’t use the flag variation on the sdk).
  3. If the instance define a flag, we check if the submission is correct.
  4. If the instance does not define a flag or if the submission is incorrect, we check if a flag is defined on CTFd (also use as fallback).
  5. Now, it is the classic behavior of CTFd.

Conclusion

Addressing the shareflag issue is crucial to maintaining fairness in CTF competitions. By redefining the submission process with methods like the dynamic_iac challenge, we ensure that only legitimate efforts are rewarded, preserving the true spirit and integrity of the competition.

FAQ

Why don’t we use the CTFd Flag system for generated flag ?

That’s simple: if we generate the flag on CTFd, each team can submit any generated flag, which doesn’t address the shareflag issue.

Why must the instance be ON to submit?

There are two main reasons for this requirement:

  1. To prevent ShareFlag: the instance must be up and running to retrieve the generated flag from chall-manager, ensuring flags are unique and not shared between participants.
  2. To prevent FlagHolding: keeping the instance active forces players to consume their mana when submitting flags, encouraging continuous participation in the competition. Additionnaly, the generated flag is valid for a running instance and will be regenerated in case of recreation.

Why the CTFd Flag is consider as “fallback” ?

As we said before, the CTFd Flag system allows users to submit the same flag. We use this system to prevent connection error or latency with chall-manager or if the generated flag is invalid for synthax error (we choose the extended ASCII so it should not happen).

3.4 - Source

Learn how the Source abstraction is implemented in the plugin.

Concept

As explained here, the Source refers to the team or user that initiates a request. This abstraction allows compatibility with CTFd operating in either “users” or “teams” mode, making the plugin versatile and usable in both modes.

To enable sharing across all users, the sourceId is set to 0. The table below summarizes the different scenarios:

user_mode global scope sourceId
users FALSE current_user.id
teams FALSE current_user.team_id
users TRUE 0
teams TRUE 0

Example workflow

Instance creation

Here an example of sourceId usage in the instance creation process:

flowchart LR
    A[Launch] --> B{CTFd mode}
    B -->|users| C[sourceId = user.id]
    B -->|teams| D[sourceId = user.team_id]
    D --> E{challenge.global_scope}
    C --> E
    E -->|True| F[sourceId = 0]
    E -->|False| End1
    F --> End1

    End1(((1)))

The rest of the workflows is detailed in mana section.

3.5 - Mana

Discover how we designed mana to prevent infrastructure overload.

Concept

As a CTF administrator, it’s important to manage infrastructure resources such as VMs, containers, or Kubernetes Pods effectively. To avoid overloading the infrastructure by deploying excessive instances, we introduced a “mana” system that assigns a cost to each challenge.

For instance, consider these three challenges:

  • challenge1: a static web server in the web category;
  • challenge2: a Unix shell challenge in the pwn category;
  • challenge3: a Windows server VM in the forensic category.

Challenge3 demands more resources than Challenge2, and Challenge2 requires more than Challenge1.

Suppose each team has a mana limit of 5, with the following costs assigned:

Name Cost
challenge1 1
challenge2 2
challenge3 5

In this scenario, teams can deploy instances for Challenge1 and Challenge2 simultaneously but must remove both to deploy Challenge3.

Example:

  • Team 1: Focused on pwn challenges, will likely prioritize deploying Challenge2.
  • Team 2: Specializes in Windows forensics, making Challenge3 their main priority.

This system helps administrators control the use of resources on CTF infrastructures.

Combined with the flag variation engine feature of chall-manager, mana also helps minimize Flag Holding. To progress, players must submit flags and destroy instances to reclaim mana. If players choose to hoard flags until the last moment, mana limits their ability to continue, adding a strategic element to the competition.

How it works

Mana Total

To set the total mana available to users, go to the Settings section of the plugin and adjust the “Mana Total” field. By default, this feature is disabled with a value of 0. The total mana can be modified during the event without any impact on existing configurations.

Mana Cost

Each dynamic_iac challenge is assigned a mana cost, which can be set to 0 if no cost is desired. Changes to mana costs can be made during the event, but previously issued coupons will retain the cost at the time of their creation.

Coupons

When a user requests an instance, a coupon is generated as long as they have enough mana. Administrators can bypass restrictions and deploy instances directly through the admin panel, creating coupons without limits. Each coupon stores the sourceId, challengeId, and challengeManaCost at the time of creation.

Example workflow

Instance creation

Here an example of the usage of mana and the coupon creation while the instance create process:

flowchart LR
    End1(((1)))

    End1 --> G{Mana is enabled ?}
    G -->|True|H{Source can afford ?}
    G -->|False|I[Deploy instance on CM]
    H -->|True|J[Create coupon]
    H -->|False|End
    J --> I
    I --> K{Instance is deployed ?}
    K -->|True|M[End]
    K -->|False|N{Mana is enabled?}
    N -->|True|L[Destroy coupon]
    N -->|False|M
    L --> M

Detailed process:

  1. We check that Mana Total is greater than 0.
  2. If mana is enable, we check that the Source can afford the current Mana Cost.
  3. If Source cannot afford the instance, the process end.
  4. If Source can afford, we create a coupon.
  5. While the coupon is created, or the mana feature is disable, we create the instance on chall-manager.
  6. We check that the instance is running on Chall-Manager.
  7. If it’s running correctly, we end the process.
  8. If not, we destroy the coupon if exists, then end process with an error.

Synchronicity

Due to the vital role of mana, we have to ensure its consistency: elseway it could be possible to brute-force the use of Chall-Manager to either run all possible challenges thus bypassing restrictions.

To provide this strict consistency, we implemented a Writer-preference Reader-Writer problem based upon Courtois et al. (1971) work. We model the problem such as administration tasks are Writers and players Readers, hence administration has priority over players. For scalability, we needed a distributed lock system, and due to CTFd’s use of Redis, we choosed to reuse it.

FAQ

Why aren’t previous coupon prices updated?

We modeled the mana mechanism on a pricing strategy similar to retail: prices can fluctuate during promotions, and when an item is returned, the customer receives the price paid at purchase. This approach allows CTF administrators to adjust challenge costs dynamically while maintaining consistency.

Why can administrators bypass the mana check?

During CTF events, administrators might need to deploy instances for users who have exhausted their mana. While coupons will still be generated and mana consumed, administrators can set the cost to 0 at the challenge level to waive the charge if needed.

3.6 - Testing

Learn how we test the plugin.

Purpose

Developing in an Open Source ecosystem is good, being active in responding to problems is better, detecting problems before they affect the end-user is the ideal goal!

Following this philosophy, we put a lot of effort into CI testing, mainly using Cypress, and analyze code security via CodeQL.

GitHub Actions

From a technology point of view, we use GitHub Actions whenever possible to automate our tests.

Security

To ensure ongoing security, we enable advanced security analysis on the repository and conduct periodic scans with CodeQL on each pull request.

Testing

Our tests are systematically executed on each push and pull request, following a two-stage process.

The first stage involves Cypress for plugin integration, where we validate correct test cases to ensure comprehensive coverage. The second stage is executed to check for potential error with invalid cases. This stage runs after the Cypress tests to save time and due to technical constraints, such as the creation of challenges (preconditions).

To detect potential divergences with CTFd mode, we execute the tests in mode: users and mode: teams.