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
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).
# 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
Take note of your webhook's Shared Secret. You can find it by going to your CTFd Admin Panel > Plugins > Webhooks.
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
DockerfileFROM 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" ]
In your terminal, navigate to the folder that has the application files.
cd <folder>
Build the Docker image with the following command, with
py-debugger
as our image name.docker build -t py-debugger .
Run the image we just built with the command specified below.
docker run -p 5000:5000 py-debugger
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.00To test the endpoint, use your browser** or cURL and go to the forwarded address,
https://<subdomain>.ngrok.io/?token=123
, with a sample token123
(without a token, the server will respond with an error). You should expect a JSON response as shown below.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
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 asindex-<hash>.php
. Or else, it may not work, since thedocker-compose.yml
file is configured to look forindex.php
, as highlighted in the code below.docker-compose.ymlversion: '2'services:nginx: image: php:8-apache ports: - 8000:80 volumes: - ./index.php:/var/www/html/index.php
Take note of your webhook's Shared Secret. You can find it by going to your CTFd Admin Panel > Plugins > Webhooks.
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);?>
In your terminal, navigate to the folder that has the application files.
cd <folder>
Build the image and run the instance with the following command:
docker-compose up
If you check the
docker-compose.yml
file, our the port number assigned is8000
. 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.00To test the endpoint, use your browser or cURL and go to the forwarded address,
https://<subdomain>.ngrok.io/?token=123
, with a sample token123
(without a token, the server will respond with an error). You should expect a JSON response as shown below.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.