This project is a Capture The Flag (CTF) platform built using Python and Flask. It includes various challenges such as Cryptography, Web Exploitation, Reverse Engineering, Forensics, and Steganography.
Note
Fully compiled archival version here
- User authentication and session management
- Multiple CTF challenges
- Leaderboard to track team scores
- Secure flag submission system
- Simple RESTful API
-
Clone the repository:
git clone https://github.com/DefinetlyNotAI/Scrapyard_Bounty.git cd ctf-platform
-
Create a virtual environment and activate it:
python -m venv venv source venv/bin/activate # On Windows use `venv\Scripts\activate`
-
Install the required packages:
pip install -r requirements.txt
-
Modify the scripts environment variables to your secrets
-
Run the Flask application:
python CTF.py
-
Open your web browser and navigate to
http://127.0.0.1:5000
.
- Cryptography: Decrypt a ROT13-encrypted message to uncover the flag.
- Web Exploitation: Bypass the login page using SQL Injection to discover the flag.
- Reverse Engineering: Analyze a binary file to find a hardcoded key.
- Forensics: Analyze a PCAP file in Wireshark to find a hidden key.
- Steganography: Extract hidden data from an image file using an automated script.
Contributions are welcome! Please fork the repository and submit a pull request.
This project is licensed under the MIT License.
This document provides a detailed overview of the available API endpoints for the project. Each endpoint includes information on the HTTP method, URL, required parameters, and whether admin access is required as well as rate limits.
Important
Keep the accept
header as "application/json" in the request headers to receive JSON error responses.
The API is designed to return HTML responses by priority default if errors occur.
- URL:
/api/executeQuery
- Method: POST
- Description: Executes a SQL query on the database.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β (Requires an HTTP request with a JSON payload)
- Request Body Preview:
{ "query": "SELECT * FROM users" }
- Response preview
#200
:{ "id": 1, "username": "example_user", "email": "user@example.com" }
#400
:{ "error": "No query provided" }
#500
:{ "error": "Query Execution Failed - API execute_query" }
- URL:
/api/status
- Method: GET
- Description: Checks if the API is running and verifies database connectivity.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β
- Response preview
#200
:{ "status": "API is running", "uptime_seconds": 12345.67, "database_connected": true }
- URL:
/api/download/<challenge_id>
- Method: GET
- Description: Downloads challenge zip files.
- Admin?: π
- Rate Limit: π₯ 5 requests/hour
- Plug and Play?: βοΈ (Browser-accessible if challenge exists, must be signed in)
- Response preview
#200
-> (Binary file download)#404
:{ "error": "Challenge files not found" }
#500
:{ "error": "Failed to fetch challenge files" }
- URL:
/backup/db
- Method: GET
- Description: Downloads CSV of the DB.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β
- Response preview
#200
-> (Binary file download)#500
:{ "error": "Error backing up database" }
- URL:
/api/get/size
- Method: GET
- Description: Fetches the current database size in a human-readable format.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β
- Response preview
#200
:{ "size": "24 MB" }
#500
:{ "error": "Failed to get database size" }
- URL:
/api/get/activeConnections
- Method: GET
- Description: Fetches the number of currently active database connections.
- Admin?: π
- Rate Limit: π₯ 60 requests/hour
- Plug and Play?: β
- Response preview
#200
:{ "activeConnections": 12 }
#500
:{ "error": "Failed to get active connections" }
- URL:
/api/get/allTeams
- Method: GET
- Description: Fetches all teams with their scores.
- Admin?: π
- Rate Limit: π₯ 60 requests/hour
- Plug and Play?: β
- Response preview
#200
:[ { "id": 1, "team_name": "Team Alpha", "score": 150 }, { "id": 2, "team_name": "Team Beta", "score": 100 } ]
#500
:{ "error": "Failed to get all teams" }
- URL:
/api/get/challengesProgress
- Method: GET
- Description: Fetches the progress of challenges for the logged-in team.
- Admin?: π
- Rate Limit: π₯ 50 requests/hour
- Plug and Play?: βοΈ (User must be logged in)
- Response preview
#200
:[ { "challenge_id": 1, "flag_submitted": true, "score": 50 }, { "challenge_id": 2, "flag_submitted": false, "score": 0 } ]
#401
:{ "error": "User not logged in" }
#500
:{ "error": "An error occurred - Function get_challenge_progress" }
- URL:
/api/get/leaderboard
- Method: GET
- Description: Retrieves the leaderboard with paginated results.
- Admin?: π
- Rate Limit: π₯ 30 requests/hour
- Plug and Play?: β (Pagination query parameters needed)
- Response preview
#200
:[ { "team_name": "Team Alpha", "score": 300 }, { "team_name": "Team Beta", "score": 250 } ]
#500
:{ "error": "Failed to get leaderboard" }
I'll continue with the documentation in the same format.
- URL:
/api/get/tables
- Method: GET
- Description: Retrieves a list of all tables in the database.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:["users", "teams", "challenges"]
#500
:{ "error": "Getting the tables failed" }
- URL:
/api/get/tables/<table_name>
- Method: GET
- Description: Retrieves up to 100 rows from a specific table.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:[ {"id": 1, "username": "admin", "email": "admin@example.com"}, {"id": 2, "username": "user1", "email": "user1@example.com"} ]
#500
:{ "error": "Getting the table rows failed" }
- URL:
/api/get/tables/<table_name>/schema
- Method: GET
- Description: Retrieves the schema of a given table.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:[ {"column_name": "id", "data_type": "integer"}, {"column_name": "username", "data_type": "text"}, {"column_name": "email", "data_type": "text"} ]
#500
:{ "error": "Getting the table schema failed" }
- URL:
/api/get/tables/headers/<string:table_name>
- Method: GET
- Description: Retrieves the headers of a given table.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:[ "Id", "Username", "Email" ]
#500
:{ "error": "Getting the table rows headers failed" }
- URL:
/api/delete/tables/<table_name>/<row_id>
- Method: DELETE
- Description: Deletes a specific row from a table based on its ID.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:{ "message": "Item deleted successfully." }
#500
:{ "error": "Deleting the table item failed" }
- URL:
/api/delete/tables/<table_name>
- Method: DELETE
- Description: Deletes an entire table from the database.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:{ "message": "Table deleted successfully." }
#500
:{ "error": "Deleting the table failed" }
- URL:
/api/delete/database
- Method: POST
- Description: Deletes the entire database (users and teams tables).
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- Response preview
#200
:{ "message": "Database deleted successfully." }
#500
:{ "error": "Deleting the database failed" }
- URL:
/api/get/user/profile
- Method: GET
- Description: Fetches the logged-in user's profile data.
- Admin?: π
- Rate Limit: π₯ 100 requests/hour
- Plug and Play?: βοΈ (User must be logged in)
- Response preview
#200
:{ "team_name": "Team Alpha", "score": 150, "flags_submitted": ["flag{example1}", "flag{example2}"] }
#401
:{ "error": "User not logged in" }
#404
:{ "error": "User not found" }
#500
:{ "error": "Failed to get user profile" }
- URL:
/api/get/user/rank
- Method: GET
- Description: Fetches the rank of the logged-in team.
- Admin?: π
- Rate Limit: π₯ 20 requests/hour
- Plug and Play?: βοΈ (User must be logged in)
- Response preview
#200
:{ "rank": 3, "next_team_score": 250 }
#401
:{ "error": "User not logged in" }
#404
:{ "error": "Team not found" }
#500
:{ "error": "Failed to get team rank" }
- URL:
/api/get/user/history
- Method: GET
- Description: Fetches the history of a logged-in teamβs progress.
- Admin?: π
- Rate Limit: π₯ 50 requests/hour
- Plug and Play?: βοΈ (User must be logged in)
- Response preview
#200
:[ { "timestamp": "2025-01-30T14:23:00Z", "flags_submitted": ["flag{example3}"], "score": 50 }, { "timestamp": "2025-01-29T10:15:00Z", "flags_submitted": ["flag{example1}", "flag{example2}"], "score": 100 } ]
#404
:{ "message": "No history found for the team" }
#500
:{ "error": "Failed to get team history" }
- URL:
/api/get/user/submissions
- Method: GET
- Description: Fetches the logged-in team's flag submission history.
- Admin?: π
- Rate Limit: π₯ 100 requests/hour
- Plug and Play?: βοΈ (User must be logged in)
- Response preview
#200
:[ { "flag": "flag{example1}", "timestamp": "2025-01-30T12:00:00Z" }, { "flag": "flag{example2}", "timestamp": "2025-01-29T08:45:00Z" } ]
#404
:{ "message": "No submissions found for the team" }
#500
:{ "error": "Failed to get submission history" }
- URL:
/api/shop/buy
- Method: POST
- Description: Allows a user to buy an item from the shop, generating a receipt image if the item is in stock.
- Admin?: π
- Rate Limit: π₯ 30 requests/hour
- Plug and Play?: β (Proper JSON body must be sent)
- Request Body Preview:
{ "item_id": "123", "email": "user@example.com" }
- Response preview
#200
-> Downloads Receipt:
- URL:
/api/shop/update_stock
- Method: POST
- Description: Allows an admin to update the stock levels for items in the shop.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β (Admin required and Proper JSON body must be sent)
- Request Body Preview:
{ "stock_123": "10", "stock_124": "5" }
- URL:
/api/shop/cancel_receipt
- Method: POST
- Description: Allows an admin to cancel a receipt by deleting it from the database.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β (Admin required and Proper JSON body must be sent)
- Request Body Preview:
{ "receipt_id": "abc123" }
- URL:
/api/shop/remove_mission/<int:mission_id>
- Method: GET
- Description: Allows an admin to remove a mission from the system.
- Admin?: π
- Rate Limit: π
- Plug and Play?: βοΈ (Admin required)
- URL:
/api/shop/add_mission
- Method: GET, POST
- Description: Allows an admin to add a new mission to the system.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β (Proper JSON body must be sent)
- Request Body Preview:
{ "name": "New Mission", "description": "A description of the new mission", "scraps": "100" }
- URL:
/abort/<http_code>
- Method: GET
- Description: Allows an admin to trigger an HTTP abort with a specified status code.
- Admin?: π
- Rate Limit: π
- Plug and Play?: β
- Response:
#200
-> The server aborts with the given HTTP code and a message:{ "message": "Requested to abort with code <http_code>" }
#400
:{ "message": "Error occurred in /abort" }
#500
:{ "message": "Error occurred in /abort" }
You may add this to the JSON that you are already will send:
{
"api-key": "API KEY"
}
With the proper API KEY you will be authenticated, This is rate limited to 15 incorrect requests per hour
Emoji | Sector | Meaning |
---|---|---|
β | Plug and Play | Impossible to use directly in the browser (requires proper HTTP requests) |
βοΈ | Plug and Play | Requires some setup (like being logged in or an admin) but still browser-accessible |
β | Plug and Play | Fully accessible via browser without any additional setup |
π | Admin? | No admin required to use the api |
π | Admin? | Admin is required to use the api |
π | Rate Limit | No rate limit |