Skip to main content

Deploying Debugging Apps for Custom Webhooks

CTFd allows you to add custom webhooks to your CTFd instance. In order to add one, the target webhook endpoint needs to be validated.

CTFd provides simple applications for debugging the webhook interactions. Each of these applications allow you to quickly spin up a development server that has an endpoint that respond appropriately to pass CTFd's endpoint validation process.

This tutorial shows how each of the endpoint validation requirement are implemented in Flask and in PHP. You can certainly build your code in any way you like, as long as the response of the endpoint satisfies the requirement.

It also shows how to select event triggers, and add the the server's endpoint to a CTFd instance.

Local Deployment

Requirements

  • Docker: to build and run images in container
  • Docker Compose: to build and run multiple containers
  • ngrok or bore: to expose the local development server publicly

Deploying the Flask Debugging Application

  1. Download the following application files, and place them in a folder.

As shown in the highlighted block of code below, the Flask server is set to respond to a GET request in JSON format. Which is an HMAC-SHA256 hash of a token (automatically generated and added by CTFd in the URL as a parameter) and a WEBHOOK_SECRET environment variable (manually added).

app.py
   # fmt: off   # pip install Flask==2.2.2   # Set the WEBHOOK_SECRET envvar to your CTFd instance's webhook secret   import hashlib   import hmac   import os   from flask import Flask, jsonify, request   app = Flask(__name__)   @app.route('/', methods=["GET", "POST"])   def verify():      if request.method == "GET":         key = os.environ["WEBHOOK_SECRET"]         token = request.args.get("token")         response = hmac.new(               key=key.encode(),               msg=token.encode(),               digestmod=hashlib.sha256         ).hexdigest()         return jsonify({               "response": response,         })      elif request.method == "POST":         print(request.url)         print(request.headers)         print(request.json)         return "", 200   if __name__ == "__main__":      host = os.getenv("HOST", "127.0.0.1")      port = int(os.getenv("PORT", "12345"))      app.run(debug=True, threaded=True, host=host, port=port)  # nosec
  1. Take note of your webhook's Shared Secret. You can find it by going to your CTFd Admin Panel > Plugins > Webhooks.

  2. In the Dockerfile, add your Shared Secret in the WEBHOOK_SECRET environment variable, assign a port number that's available to use, and expose the port. Then, save your changes.

    • ENV WEBHOOK_SECRET=75834fde46f5107db3d65b6873176c905142214a9fd7e07947086e62b65f9
    • ENV PORT=5000
    • EXPOSE 5000
    Dockerfile
    FROM python:3-slim-busterCOPY app.py /opt/app.pyRUN pip install install Flask==2.2.2 && chmod +x /opt/app.pyWORKDIR /optENV WEBHOOK_SECRET=75834fde46f5107db3d65b6873176c905142214a9fd7e07947086e62b65f9ENV HOST=0.0.0.0ENV PORT=5000EXPOSE 5000CMD [ "python3", "/opt/app.py" ]
  3. In your terminal, navigate to the folder that has the application files.

    cd <folder>
  4. Build the Docker image with the following command, with py-debugger as our image name.

    docker build -t py-debugger .
  5. Run the image we just built with the command specified below.

    docker run -p 5000:5000 py-debugger
  6. In a separate terminal, run ngrok with the port number of the application, 5000.

    Then, take note of the forwarded address, https://<subdomain>.ngrok.io. This will be our endpoint.

    ngrok http 5000

    Session Status online
    Account <account-name> (Plan: Free)
    Version 3.1.0
    Region <region> ()
    Latency -
    Web Interface http://127.0.0.1:4040
    Forwarding https://<subdomain>.ngrok.io -> http://localhost:5000

    Connections ttl opn rt1 rt5 p50 p90
    0 0 0.00 0.00 0.00 0.00
  7. To test the endpoint, use your browser** or cURL and go to the forwarded address, https://<subdomain>.ngrok.io/?token=123, with a sample token 123 (without a token, the server will respond with an error). You should expect a JSON response as shown below.

  8. To add the endpoint in CTFd and select events that would trigger an HTTP request to the endpoint, you can follow the steps in this tutorial.

Deploying the PHP Debugging Application

  1. Download the following application files, and place them in a folder.

    Once you have downloaded the index.php file, make sure to rename it as such, as it may show up in your downloads folder as index-<hash>.php. Or else, it may not work, since the docker-compose.yml file is configured to look for index.php, as highlighted in the code below.

    docker-compose.yml
    version: '2'services:nginx:   image: php:8-apache   ports:      - 8000:80   volumes:      - ./index.php:/var/www/html/index.php
  2. Take note of your webhook's Shared Secret. You can find it by going to your CTFd Admin Panel > Plugins > Webhooks.

  3. Open the index.php file in a text editor and add your Shared Secret in the $secret variable. Then, save your changes.

    $secret = '75834fde46f5107db3d65b6873176c905142214a9fd7e07947086e62b65f9'

    index.php
    <?$token = $_GET["token"];$secret = '75834fde46f5107db3d65b6873176c905142214a9fd7e07947086e62b65f9';header('Content-Type: application/json');$response = array(   'response' => hash_hmac('sha256', $token, $secret),);echo json_encode($response);?>
  4. In your terminal, navigate to the folder that has the application files.

    cd <folder>
  5. Build the image and run the instance with the following command:

    docker-compose up
  6. If you check the docker-compose.yml file, our the port number assigned is 8000. In a separate terminal, run ngrok with the port number of the application, 8000.

    Then, take note of the forwarded address, https://<subdomain>.ngrok.io. This will be our endpoint.

    ngrok http 8000

    Session Status online
    Account <account-name> (Plan: Free)
    Version 3.1.0
    Region <region> ()
    Latency -
    Web Interface http://127.0.0.1:4040
    Forwarding https://<subdomain>.ngrok.io -> http://localhost:5000

    Connections ttl opn rt1 rt5 p50 p90
    0 0 0.00 0.00 0.00 0.00
  7. To test the endpoint, use your browser or cURL and go to the forwarded address, https://<subdomain>.ngrok.io/?token=123, with a sample token 123 (without a token, the server will respond with an error). You should expect a JSON response as shown below.

  8. To add the endpoint in CTFd, and select specific events that would trigger an HTTP request to the endpoint, you can follow the steps in this tutorial.

Remote Deployment

You can also deploy the applications in a remote machine using Hosted CTFd's Challenge Deployment Service. The process is the same as above, with a few minor tweaks to meet its deployment requirements, which you can check here.