Local-first CTF workspace manager and assistant CLI
Project description
CTFx
A local-first CTF workspace manager and assistant CLI for Jeopardy and AWD competitions.
Background
CTFx started from a bunch of PowerShell scripts I wrote for CTF competitions back in HKCERT 2024.
Later, in the HKCERT 2025 Final, I made a Python-based CTF Assistant for AWD matches. It worked pretty well, got some positive feedback, and helped our team achieve a solid result.
After that, I decided to turn those ideas and experiences into CTFx, hoping it can give CTF players a bit of extra help in the AI era.
Features
- Competition management — create, list, and switch between CTF workspaces
- Challenge scaffolding — auto-generate category-specific solve templates (
pwn,crypto,forensics,rev,misc) - Platform integration — fetch challenges and submit flags via CTFd API using either team token or platform cookies
- Terminal integration — open shells, WSL, editors, and file explorers at challenge paths
- AI context — generate
prompt.mdwith competition context plus CLI/MCP usage notes for LLM-assisted solving - WebUI — React dashboard with challenge add/delete, editable challenge detail, flags table, and writeup editor
- REST API + MCP — machine-readable interface for Claude Desktop / Cursor integration
- AWD mode — SSH/SCP helpers, exploit runner, patch tracker, host map
Installation
Requires Python 3.11+.
pip install ctfx
# or with uv (recommended)
uv tool install CTFx
Install Guide
Install from PyPI:
pip install ctfx
ctfx --help
Upgrade to the latest release:
pip install -U ctfx
Install from a local checkout during development:
git clone https://github.com/liyanqwq/CTFx
cd CTFx
pip install -e .
ctfx --help
Quick Start
# First run — initializes ~/.config/ctfx/config.json
ctfx setup
# Create a competition workspace
ctfx comp init --name HKCERT_CTF --year 2025 --mode jeopardy --platform ctfd
# Switch to it
ctfx use HKCERT_CTF_2025
# Add a challenge
ctfx add baby_pwn pwn
# Open in editor
ctfx code pwn/baby_pwn
# Open a terminal at the solve directory
ctfx cli pwn/baby_pwn/solve
# Start the WebUI + API + MCP server
ctfx serve
# Open WebUI in browser (auto-login)
ctfx webui
Typical Flag Workflow
CTFx now separates local flag recording from remote submission:
- Save a discovered flag locally in the WebUI or via API. This moves the challenge to
hoard. - Review hoarded flags in the
Flagspage. - Submit the flag to the remote platform when ready.
- A successful remote submission promotes the challenge to
solved.
Notes
- Paths passed to
ctfx cli,ctfx code,ctfx explorer, API routes, and MCP tools are restricted to the active competition workspace. Escaping with..is rejected. - Challenge categories, challenge names, and AWD service names are treated as path components and must not contain
/or\\. - If
terminal.python_cmdcontains spaces, quote the executable path normally. Example on Windows:"C:\Program Files\Python313\python.exe" -u ctfx webuigenerates a one-time login URL for the local WebUI. API and MCP still use the root token unless you are already authenticated in the browser.- For platform-backed competitions, set either
team_tokenorteam_cookiesin the competition profile before attempting remote submission. - Challenge status flow is
fetched -> seen -> working -> hoard -> solved.
CLI Reference
ctfx comp list [query] List competitions
ctfx comp use <name> Set active competition
ctfx comp init Create new competition workspace
ctfx comp info Show active competition metadata
ctfx chal list [query] [--cat] [--status] List challenges
ctfx chal add <name> [cat] Add and scaffold a challenge
ctfx chal rm <name> Remove a challenge
ctfx chal info <name> Show challenge detail
ctfx chal status <name> <status> Update challenge status
ctfx cli [path] Open terminal at path
ctfx wsl [path] [-- cmd] Open WSL shell or run command
ctfx e [path] Open file explorer
ctfx code [path] Open editor
ctfx py [file] Run Python in configured environment
ctfx fetch [--cat] [--chal] Fetch challenges from platform
ctfx submit <flag> [--chal] Submit flag to platform
ctfx import <url> LLM-assisted challenge import
ctfx ai [--print] Generate AI context prompt.md with CLI/MCP notes
ctfx mcp [--out PATH] Generate MCP client config
ctfx webui / ctfx web / ctfx ui Open WebUI in browser
ctfx token update Rotate root token
ctfx config show Display current configuration
ctfx config list Alias for config show
ctfx config set <key> <value> Set a config value (dot-notation)
ctfx serve [--port] [--host] Start WebUI + API + MCP server
ctfx setup Interactive configuration wizard
ctfx i Interactive REPL
ctfx toolkit list [--cat] [--tag] [--set] List tools across active sets
ctfx toolkit add [--set <id>] Add a tool interactively
ctfx toolkit rm <tool-id> [--set <id>] Remove a tool
ctfx toolkit info <tool-id> Show tool detail (prompt, ref, cmd)
ctfx toolkit import <url|file> [--as <id>] Import a set
ctfx toolkit export <set-id> [--out file] Export a set to JSON
ctfx toolkit update [<set-id>] Re-fetch set(s) from source URL
ctfx toolkit set list List all sets
ctfx toolkit set create <name> Create a new set
ctfx toolkit set enable/disable <set-id> Toggle set activation
ctfx toolkit set rm <set-id> Delete a set
Aliases
| Short | Full |
|---|---|
ctfx use <comp> |
ctfx comp use <comp> |
ctfx init |
ctfx comp init |
ctfx add <chal> [cat] |
ctfx chal add <chal> [cat] |
ctfx e [path] |
ctfx explorer [path] |
ctfx i |
ctfx interactive |
Configuration
Global config lives at ~/.config/ctfx/config.json. Run ctfx setup to configure interactively, or use ctfx config set for individual keys.
ctfx config show # view all settings
ctfx config set serve.port 9000
ctfx config set terminal.editor_cmd cursor
ctfx config set terminal.wsl_distro ubuntu
ctfx config set basedir ~/ctf
Settable keys: basedir, active_competition, serve.host, serve.port, terminal.cli_cmd, terminal.editor_cmd, terminal.wsl_distro, terminal.python_cmd, terminal.explorer_cmd, terminal.file_manager_cmd, auth.one_time_login_ttl_sec, auth.session_ttl_sec, auth.webui_cookie_name
Key fields:
basedir— root directory for all competition workspaces (default:~/ctf)root_token— auth secret for REST API, MCP, and WebUI; rotate withctfx token update(not settable viaconfig set)serve.port— default server port (default:8694)terminal.*— shell, editor, WSL distro, Python command
Example Windows Python configuration with spaces in the executable path:
ctfx config set terminal.python_cmd "\"C:\Program Files\Python313\python.exe\" -u"
Server & Authentication
ctfx serve # starts on 127.0.0.1:8694 by default
ctfx webui # opens browser with auto one-time login
API and MCP endpoints require Authorization: Bearer <root_token>.
The WebUI authenticates through a one-time login URL and then uses a secure session cookie for subsequent browser requests.
Changing the active competition through the CLI or API also updates the in-process MCP server state, so MCP tools follow the currently selected competition.
MCP client config for Claude Desktop / Cursor:
ctfx mcp --out mcp.json
The generated MCP URL uses /mcp/ with a trailing slash.
Workspace Structure
~/ctf/
└── HKCERT_CTF_2025/
├── ctf.json
├── prompt.md
└── pwn/
└── baby_pwn/
├── src/
├── solve/
│ └── exploit.py
├── wp.md
└── chal.md
AWD Mode
Set "mode": "awd" in ctf.json to enable AWD commands:
ctfx awd ssh <service> [--team NAME] SSH into service host
ctfx awd scp <service> <src> <dst> SCP file transfer
ctfx awd cmd <service> -- <command> Run remote command
Security note:
- AWD SSH now relies on known host keys. Add the target host to your SSH known hosts first, otherwise the connection will be rejected instead of being auto-trusted.
Personal Attacker Toolkit
CTFx includes a personal toolkit registry — a shareable catalogue of attack tools tagged by challenge category. The LLM queries it via MCP (get_toolkit) to select the right tool for each challenge.
Quick Start
# Add a tool to your personal set (interactive)
ctfx toolkit add
# List all active tools
ctfx toolkit list
# Filter by category or tag
ctfx toolkit list --cat pwn --tag rop
# Import a shared set from a URL or local file
ctfx toolkit import https://gist.github.com/example/xxxx
ctfx toolkit import ~/Downloads/crypto-recipes.json --as crypto-recipes
# Export your personal set to share it
ctfx toolkit export personal --out my-toolkit.json
# Keep an imported set up to date
ctfx toolkit update
Set Management
Multiple toolkit sets can be active simultaneously. Tools are merged in order — if two active sets define the same tool ID, the first set wins.
ctfx toolkit set list # show all sets with active status
ctfx toolkit set enable <set-id> # add to active sets
ctfx toolkit set disable <set-id> # remove from active sets (keeps file)
ctfx toolkit set rm <set-id> # delete set
Sharing Protocol
A toolkit set is a plain JSON file. Share it via any URL (GitHub Gist, raw file, etc.):
{
"id": "my-pwn-toolkit",
"name": "My PWN Toolkit",
"author": "you",
"version": "1.0.0",
"tools": [
{
"id": "pwntools-rop",
"name": "pwntools ROP chain",
"categories": ["pwn"],
"tags": ["rop", "x86_64"],
"cmd": "python {exploit}",
"prompt": "Use when binary has NX enabled and you need to leak libc. Check checksec first."
}
]
}
The prompt field tells the LLM when and how to use the tool — this is the most important field for MCP accuracy.
MCP Integration
When working on a challenge, call get_toolkit first to retrieve relevant tools:
get_toolkit(category="pwn") → filtered list with prompts
run_toolkit_tool("john-zip", {"file": "hash.txt"}) → stdout/stderr/returncode
Platform Support
| Platform | Fetch | Submit |
|---|---|---|
| CTFd | yes | yes |
| rCTF | planned | planned |
| Manual | via ctfx add |
no |
Development
git clone https://github.com/liyanqwq/CTFx
cd CTFx
pip install -e .
ctfx --help
Frontend (React + Vite):
cd frontend
npm install
npm run dev # dev server
npm run build # outputs to ctfx/server/static/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ctfx-0.2.4.tar.gz.
File metadata
- Download URL: ctfx-0.2.4.tar.gz
- Upload date:
- Size: 577.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f610f00918d0f78cdd19b1ac7bae60e3d721267a7e3a7614cf74f4894ccbb68
|
|
| MD5 |
3853e8f670421e5ffb42b2c2e52fe6ca
|
|
| BLAKE2b-256 |
dc3d8fcf4d2bf188d4265952fb99e3023465ad3bff7c639e42b28eb7d719b990
|
Provenance
The following attestation bundles were made for ctfx-0.2.4.tar.gz:
Publisher:
python-publish.yml on liyanqwq/CTFx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctfx-0.2.4.tar.gz -
Subject digest:
1f610f00918d0f78cdd19b1ac7bae60e3d721267a7e3a7614cf74f4894ccbb68 - Sigstore transparency entry: 1253068485
- Sigstore integration time:
-
Permalink:
liyanqwq/CTFx@dcc8663335058f6db37a21634ce0e7b2ef36ae06 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/liyanqwq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@dcc8663335058f6db37a21634ce0e7b2ef36ae06 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ctfx-0.2.4-py3-none-any.whl.
File metadata
- Download URL: ctfx-0.2.4-py3-none-any.whl
- Upload date:
- Size: 549.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f316e266006d062c147d18e4a655596b42805031f4a25d06938e17e896bcf0d
|
|
| MD5 |
9eb2989e9b6de0a879fc955ab4917fc0
|
|
| BLAKE2b-256 |
74791a12dcdefe8edb47fc8f72fb5f87743237523b3ad904503ce27efa4d93b7
|
Provenance
The following attestation bundles were made for ctfx-0.2.4-py3-none-any.whl:
Publisher:
python-publish.yml on liyanqwq/CTFx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctfx-0.2.4-py3-none-any.whl -
Subject digest:
2f316e266006d062c147d18e4a655596b42805031f4a25d06938e17e896bcf0d - Sigstore transparency entry: 1253068566
- Sigstore integration time:
-
Permalink:
liyanqwq/CTFx@dcc8663335058f6db37a21634ce0e7b2ef36ae06 -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/liyanqwq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@dcc8663335058f6db37a21634ce0e7b2ef36ae06 -
Trigger Event:
release
-
Statement type: