First Blood Awards using Webhooks
The previous tutorial shows you how to configure CTFd to send webhook requests to a target endpoint when a First Blood event is detected. However, it only implements half of the functionality we want to achieve.
After the endpoint receives the webhook request, the server still needs to interact with CTFd's API to award the user. The following steps will show you how to reconfigure your webhook server to extract the submission ID from the webhook event and send an API request to CTFd to generate an award.
Server Configuration
Requirements
- Flask: to run and serve the application
- ngrok or bore: to expose the local development server publicly
- Requests library: to make HTTP requests
Steps
Make sure the Flask application is not running. Terminate it with
Control + C
.Install the Requests library.
pip install requests
Generate an API token and assign it to the
API_TOKEN
environment variable.export API_TOKEN="<token>"
Assign your CTFd instance's URL to the
BASE_URL
environment variable.export BASE_URL="https://<subdomain>.ctfd.io"
Open the
app.py
file in your code editor and add the highlighted code shown below:app.py# fmt: off# pip install Flask==2.2.2# Set the WEBHOOK_SECRET envvar to your CTFd instance's webhook secretimport hashlibimport hmacimport osimport requestsfrom flask import Flask, jsonify, requestapp = 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": token = os.environ["API_TOKEN"] url = os.environ["BASE_URL"] url = url.strip("/") payload = request.json s = requests.Session() s.headers.update({"Authorization": f"Token {token}", "Content-type":"application/json"}) submission = s.get(f"{url}/api/v1/submissions/{payload['id']}", json=True).json() r = s.post( f"{url}/api/v1/awards", json={ "name": "First Blood", "value": "9000", "category": "First Blood", "description": f"Reward for being the first to solve {submission['data']['challenge']['name']}", "icon":"shield", "user_id": f"{submission['data']['user']['id']}" }, timeout=10, ) print(request.url) print(request.headers) print(request.json) return "", 200if __name__ == "__main__": app.run(debug=True, threaded=True, host="127.0.0.1", port=12345)
Click here for more information about interacting with CTFd's REST API.
Run the Flask application.
python app.py
Test the endpoint by creating a new challenge and submitting a correct answer as outlined in this section.
Check the user's profile to see if it received the award. To check a user's awards, follow the steps here.
In the next tutorial, we'll discuss how you can deploy the debugging applications locally, and remotely, to test CTFd's webhooks.