Overview
Wizarr is a self-hosted invitation and onboarding system used by homelab and media-server communities to manage user access to Jellyfin, Plex, Emby, and other services, including custom "wizard step" onboarding flows written in Markdown. Versions prior to 2026.9.1 render that Markdown through a non-sandboxed Jinja2 environment with application globals exposed, allowing an authenticated user who can create wizard steps — or an administrator who imports a malicious shareable "bundle" — to inject Jinja2 template expressions that execute as arbitrary Python code on the host. The flaw carries a CVSS score of 9.1 (Critical) and was published October 9, 2026.
Technical Details
| Field | Value |
|---|---|
| CVE ID | CVE-2026-108264 |
| CVSS Score | 9.1 (Critical) — AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H |
| CWE | CWE-1336 (Server-Side Template Injection) / CWE-94 (Code Injection) |
| Attack Vector | Network (requires the privilege to create or import a wizard step/bundle) |
| Affected Component | app/blueprints/wizard/routes.py — wizard step Markdown rendering (also app/jinja_filters.py and app/services/wizard_widgets.py) |
| Root Cause | Jinja2 environment overlaid with autoescape=False and no sandbox, evaluating attacker-controlled Markdown as live template code |
| Fixed In | 2026.9.1 |
How It Works
Wizarr lets admins (and, depending on configuration, other users able to reach the wizard editor) build custom onboarding "wizard steps" as Markdown, and lets admins import pre-built "bundles" of wizard steps shared by the community via POST /settings/wizard/import. The rendering path for that Markdown overlays the application's Jinja2 environment with autoescape=False rather than using jinja2.sandbox.SandboxedEnvironment, so the engine still has full access to Python's object graph and application globals. Any Jinja2 expression syntax embedded in a wizard step's Markdown — whether typed directly into the editor or hidden inside an imported bundle — is evaluated server-side the next time that step is rendered via GET /wizard/{server}/{idx}, rather than being treated as literal text. Bundle import only validates field types, so it does not catch malicious template syntax smuggled into the Markdown body.
Jinja2's non-sandboxed mode is notorious for escalating straightforward template injection into full remote code execution, because an attacker can walk the Python object graph from any template-exposed object back to builtins. A generic, illustrative pattern (not a working Wizarr-specific payload) looks like:
{{ self.__init__.__globals__.__builtins__ }}
From there, a real exploit chain would continue traversing object attributes to reach something callable — such as os.system or subprocess.Popen — and invoke it with attacker-chosen arguments, which is how the advisory describes attackers executing arbitrary operating-system commands, disclosing the Flask SECRET_KEY, and reaching the database and connected media-server credentials.
Impact Assessment
| Impact Area | Description |
|---|---|
| Confidentiality | High — RCE exposes the Flask SECRET_KEY, the Wizarr database (users, credentials, invitations), and any connected Jellyfin/Plex/Emby credentials |
| Integrity | High — arbitrary Python/OS command execution on the Wizarr host, plus stored cross-site scripting via the same unsanitized rendering path |
| Availability | High — an attacker with code execution can disrupt or disable the Wizarr instance and anything it manages |
Who Is At Risk
- Self-hosted Wizarr instances (≤ 2026.9.0) where any user besides the fully trusted root admin can create or edit wizard steps
- Admins who import community-shared wizard-step "bundles" from third-party sources without reviewing their Markdown content first
- Homelabs where Wizarr's host or container has network access to the Flask secret, local database, or Jellyfin/Plex/Emby admin APIs — i.e., essentially all default deployments
Attack Chain
- An attacker with the ability to create a wizard step (or craft a shareable bundle for an admin to import via
POST /settings/wizard/import) embeds a Jinja2 expression in the step's Markdown body. - The step is saved without any template-syntax sanitization — only field types are validated on import.
- When the step is later rendered (
GET /wizard/{server}/{idx}), the application's non-sandboxed Jinja2 environment evaluates the embedded expression instead of displaying it as text. - The expression walks Python's object graph to reach a callable such as
os.system, achieving remote code execution as the application user, with follow-on access to the Flask secret key, the Wizarr database, and any connected Jellyfin/Plex/Emby credentials.
Mitigation
Immediate Actions
- Upgrade to Wizarr 2026.9.1 or later, which restricts wizard template rendering
- Audit any previously imported wizard-step bundles — especially from third-party or community sources — for
{{or{%template syntax that shouldn't be there - Rotate the Flask
SECRET_KEYand any credentials Wizarr holds for connected Jellyfin/Plex/Emby servers if compromise is suspected - Restrict who holds the role that can create, edit, or import wizard steps to fully trusted admins only, pending the upgrade
Detection Opportunities
- Review stored wizard-step content (editor-created and imported) for template syntax such as
{{,{%, or references to__globals__,__init__, or__builtins__ - Watch for unexpected outbound connections or child-process spawns from the container or host running Wizarr
- Check Wizarr and reverse-proxy logs for unusual activity around
POST /settings/wizard/importor repeated hits toGET /wizard/{server}/{idx}
Defence-in-Depth
- Run Wizarr in an isolated container with minimal host access and no unnecessary outbound network reachability
- Network-segment Wizarr from the Jellyfin/Plex/Emby admin APIs it connects to, so a compromised Wizarr instance can't pivot directly into the media server
- Treat community-shared wizard-step bundles as untrusted input, the same as any other imported, unaudited code or config
Background
Wizarr fills a common niche in self-hosted media-server communities: a friendly, templated onboarding experience that spares admins from manually walking each new user through Jellyfin, Plex, or Emby setup. That same flexibility — letting admins build and share reusable "wizard step" bundles — is what turned a convenience feature into a server-side template injection vector. Self-hosted admin tools that accept community-shared "content" (templates, themes, plugins, onboarding bundles) without sandboxing the engine that renders it are a recurring pattern behind SSTI and supply-chain-style vulnerabilities across the homelab ecosystem, and CVE-2026-108264 is a textbook example: a convenience feature (Markdown-based wizard steps) built on a templating engine (Jinja2) that was never isolated from the host application it was embedded in.