Security: Webhooks allow full RCE via lack of sandboxing on Jinja templating
Been a while since I've had a look at Crafty! Lots of great work has gone on, well done everyone for the progress in general.
Quick Information
- Operating System: Any (tested on Windows 11, Ubuntu 22.04, and Docker on Ubuntu)
- Install Type: Any (tested on Git Cloned(Manual) / Linux Installer / Docker)
- Crafty Version: v4.6.1
What Happened?
Webhooks currently use jinja2's basic Environment for templating, without any additional security controls/checks. This allows access to dangerous Python builtins that can be abused to gain full remote code execution (RCE) on the Crafty host.
This means that any user with the permission to edit or create webhooks can fully control a Crafty host, potentially escalating their privileges within the application, or running malicious code.
Expected result
Ideally any attempts to access dangerous Python builtins would be blocked. Easiest way to achieve this would be by switching to jinja2.sandbox's SandboxedEnvironment, which I think would (currently) be sufficient to mitigate this issue entirely. The SandboxedEnvironment blocks a number of dangerous globally accessible objects from being accessed, limiting the scope to only the context passed in (e.g. event_data). ImmutableSandboxedEnvironment is probably preferred too - unless you require Jinja webhooks to be able to modify Lists/Dictionaries when executed.
Internal attributes on those objects would still be accessible however, so the "belts and braces" approach is to define your own class that extends the is_safe_attribute method with further checks that only allow users to access attributes relevant to Crafty. I'd suggest the following as a minimum/blanket approach that should work:
from jinja2.sandbox import ImmutableSandboxedEnvironment, is_safe_attribute
class CraftyRestrictedEnvironment(ImmutableSandboxedEnvironment):
def is_safe_attribute(self, obj, attr, value):
if attr.startswith('_'):
return False
return super().is_safe_attribute(obj, attr, value)
This blocks any attributes that begin with _, in addition to the parent class (ImmutableSandboxedEnvironment) blocking any __ "dunder" attributes. You can then use CraftyRestrictedEnvrionment in place of the default Environment from jinja2 in base_webhook.py.
Happy to write the patch myself if you'd like! The Jinja docs have some good guidance on sandboxing too.
Steps to reproduce
- Create a server within Crafty, or gain access to an existing one with permissions to edit/create webhooks
- Create or edit a webhook and add the following template string to the body of the webhook:
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
- Trigger the webhook, and in the output you will see the result of running the
idcommand, proving RCE (edit as required for your OS etc)
Screenshots
Windows 11 Test:
Ubuntu 22.04 Test:
Docker Test:
Priority/Severity
- High (anything that impacts the normal user flow or blocks app usage)
- Medium (anything that negatively affects the user experience)
- Low (anything else e.g., typos, missing icons/translations, layout/formatting issues, etc.)
Because the attacker can effectively execute arbitrary python code (and view the results), they can use this to edit/corrupt/modify Crafty files to escalate their user privileges within the application, DoS the server, and execute arbitrary processes/subcommands. Depending on their installation method and user setup they may be able to take control of the host machine entirely.


