Crafty Controller Webhook Jinja2 Server-Side Template Injection RCE (CVE-2025-14700)
by secdongle · 2026-07-06
- Severity
- Critical
- CVE
- CVE-2025-14700
- Category
- web
- Affected product
- Crafty Controller (Minecraft server management panel)
- Affected versions
- <= 4.6.1
- Disclosed
- 2026-07-06
- Patch status
- unpatched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-06 |
| Last Updated | 2026-07-06 |
| Author / Researcher | secdongle |
| CVE / Advisory | CVE-2025-14700 |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.9 (per NVD) |
| Status | Weaponized |
| Tags | crafty-controller, minecraft, jinja2, ssti, server-side-template-injection, reverse-shell, tornado, xsrf, python, cwe-1336 |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Crafty Controller (Minecraft server management panel) |
| Versions Affected | <= 4.6.1 |
| Language / Platform | Python 3 exploit against a Tornado-based web application |
| Authentication Required | Yes (any valid low-privilege authenticated account) |
| Network Access Required | Yes |
Summary
Crafty Controller’s server Webhook configuration accepts a user-controlled “body” template that is rendered server-side with Jinja2 without sandboxing. An authenticated user can set the webhook body to a Jinja2 expression that escapes the sandbox via self._TemplateReference__context to reach the cycler object’s __init__.__globals__, exposing the os module and enabling arbitrary shell command execution. By binding the malicious webhook to the start_server trigger and then invoking the server-start action through Crafty’s API, the payload is rendered and executed with the same privileges as the Crafty Controller backend process, yielding a reverse shell.
PoC Script
The PoC automates the full authenticated attack chain: Tornado XSRF/session bootstrap, dummy Minecraft server creation, malicious webhook injection, and triggering of the start_server action to fire the SSTI payload.
Vulnerability Details
Root Cause
The webhook body field is rendered through Jinja2 with the full default context, and the PoC’s payload reaches os.system via the self template reference and the cycler extension’s globals:
| |
When Crafty later renders this webhook body (as part of the start_server action pipeline), Jinja2 evaluates the expression, walking from self (the template reference) through cycler’s __init__ function’s __globals__ dict to reach the imported os module, then calling os.system() with the attacker’s reverse-shell command.
Attack Vector
- Authenticate to the Crafty Controller API (
/api/v2/auth/login/), obtaining a JWT plus Tornado_xsrfcookie/header pair. - Create a dummy Minecraft server via
POST /api/v2/serversto obtain aserver_idto attach a webhook to. POST /api/v2/servers/{server_id}/webhookwith a Discord-type webhook whosebodyis the Jinja2 SSTI payload above and whosetriggerincludesstart_server.- Precisely replicate the browser’s header/cookie/XSRF synchronization (a custom
tokenheader set to the_xsrfvalue,Referer,Origin, etc.) and callPOST /api/v2/servers/{server_id}/action/start_server(and optionally theeulaaction) to fire the webhook rendering pipeline. - Jinja2 renders and executes the payload, opening a reverse shell to the attacker’s
lhost:lport.
Impact
Authenticated remote code execution on the host running Crafty Controller, at the privilege level of the Crafty backend process (often with elevated access, since it manages game server processes).
Environment / Lab Setup
Target: Crafty Controller 4.6.1 (docker-compose.yml provided:
registry.gitlab.com/crafty-controller/crafty-4:4.6.1, web dashboard on :8443,
Minecraft port :25565); complete the setup wizard to create an admin account
Attacker: Python 3, `pip install requests`; a netcat listener for the reverse shell
Proof of Concept
PoC Script
See
poc.py(plusdocker-compose.ymlfor a disposable vulnerable lab) in this folder.
| |
Detection & Indicators of Compromise
POST /api/v2/servers/{id}/webhook HTTP/1.1
...
{"webhook_type":"Discord","body":"{{ self._TemplateReference__context...os.system(...) }}","trigger":["start_server"]}
POST /api/v2/servers/{id}/action/start_server HTTP/1.1
Signs of compromise:
- Webhook configurations whose
bodyfield contains Jinja2 template syntax referencingself,__globals__,__init__, oros/subprocess - Unexpected outbound TCP connections from the Crafty Controller host immediately following a
start_server(oreula) action call - Newly created “dummy” Minecraft servers with generic/automation-style names (e.g. matching
CVE_2025_14700_Exploit_Automation) - Reverse shell child processes spawned by the Crafty backend process
Remediation
| Action | Detail |
|---|---|
| Primary fix | Update Crafty Controller beyond 4.6.1 to a version that sandboxes webhook template rendering (e.g. Jinja2 SandboxedEnvironment) and/or disallows access to self/__globals__/dunder attributes |
| Interim mitigation | Restrict webhook-configuration privileges to fully trusted administrators only; disable webhook features if unused; monitor outbound connections from the Crafty Controller host |
References
Notes
Mirrored from https://github.com/secdongle/POC_CVE-2025-14700 on 2026-07-06. Working exploit (poc.py) plus docker-compose lab; injects the Jinja2 SSTI payload and triggers a real reverse shell, matching the README’s documented output exactly.
| |