All Advisories

mercure

Server-Side Template Injection in the Notification System

mercure can send a notification (webhook or email) when a DICOM series matches a routing rule. The notification body is an administrator-configured template rendered with an unsandboxed Jinja2 engine, with no validation. Template syntax placed in that field escapes to arbitrary code execution, and because notifications fire on every matching series, the code runs persistently as imaging data flows through.

Authored byVolker Schönefeld, Simon Weber2026-05-30
SeverityCriticalCVSS 9.1CVSS 3.1 VectorAV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:HCWECWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine)ProductmercureAffected VersionsAll releases from 0.2.0-beta.1 through 0.4.0-beta.9.Fixed In0.4.1. Notification and template rendering now use jinja2's SandboxedEnvironment instead of the bare Template().CVEPendingGHSAPending

Description

mercure is an open-source DICOM orchestration platform; we appreciate the maintainers' prompt, constructive response to this report. When a DICOM series matches a routing rule, mercure can fire a notification whose body is an administrator-configured template.

That body is rendered with Jinja2's bare Template(), with no SandboxedEnvironment, no autoescape, and no validation of the template content:

app/common/notification.py:60-85

from jinja2 import Template
def parse_payload(payload, event, rule_name, task_id, details="", context={}, *,
task=None, tags_list=None):
payload_parsed = payload
payload_parsed = payload_parsed.replace("@rule@", rule_name)
payload_parsed = payload_parsed.replace("@task_id@", task_id)
payload_parsed = payload_parsed.replace("@event@", event.name)
# ... context setup ...
return Template(payload_parsed).render(context) # unsandboxed Jinja2

View source →

Jinja2's default environment permits attribute access, so the same object-traversal that defeats the routing-rule eval() sandbox applies here: from a string literal one reaches the base object class, enumerates its subclasses, and recovers __import__ for arbitrary command execution. The vendor's 0.4.1 release notes confirm the templates were "rendered without sandboxing, allowing arbitrary code execution on every matching DICOM series." We are not publishing a working payload.

The notification template fields receive no sanitization. strip_untrusted() is applied to the tags field but not to any notification body:

app/webinterface/rules.py:217-218

notification_payload_body=form.get("notification_payload_body", ""), # no sanitization
notification_email_body=form.get("notification_email_body", ""), # no sanitization

View source →

Storing a notification body on a rule, pointing the webhook at any URL, and leaving the reception trigger enabled (the default for new rules) is enough: the router renders the template on every matching series, before the webhook is sent, so the injected code runs in the router process. That process reaches the shared filesystem, Redis, the DICOM data in transit, and other containers, which is the scope change. The payload persists in mercure.json and is valid Jinja2, so it resembles a normal template.

Command execution as the mercure user in the router is confirmed by the output of an injected id:

Proof-of-concept output

uid=1000(mercure) gid=1000(mercure) groups=1000(mercure)

Impact

  • An attacker with an administrator session can store a notification template that executes arbitrary code in the router process. It runs on every DICOM series matching the rule, so execution is persistent and fires automatically as imaging data flows through.
  • The router process reaches the shared filesystem, Redis, the DICOM data in transit, and other containers on the Docker network. Command output can also be returned through the webhook body itself.
  • The payload persists in mercure.json and is syntactically valid Jinja2, so it is hard to distinguish from a legitimate template.

Mitigation

Upgrade to mercure 0.4.1, which renders notification and other user-configured templates with jinja2's SandboxedEnvironment instead of the bare Template(); the sandbox blocks the attribute access the escape relies on. As defense in depth, validate template content on save and reject attribute-access patterns such as __class__, __subclasses__, and __import__.

Defender's Checklist

  • Upgrade to 0.4.1 or later

    Deploy the patched release, which renders templates with SandboxedEnvironment.

  • Review notification templates

    Audit notification body fields in mercure.json for attribute-access patterns (__class__, __subclasses__, __import__); these should not appear in a normal template.

  • Restrict administrator access

    Limit who holds administrator credentials and confirm the default admin password has been changed.

Severity Reasoning

AV:NThe template is configured over the network through the web interface and fires on network-delivered DICOM series.AC:LStoring the template once is sufficient; it then fires automatically.PR:HEditing routing rules and notification templates requires an administrator session.UI:NRendering happens automatically when a matching series is received; no user interaction.S:CRendering in the router process reaches the shared filesystem, Redis, and other containers beyond the component.C/I/A:HArbitrary code execution yields full read, modification, and denial over reachable data and services.

References

How We Can Help

Who We Are

The security researchers behind this advisory.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec Researcher

I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.

  • PhD on Hospital Cybersecurity
  • Critical vulnerabilities found in hospital systems
  • Alumni of THB MedSec Research Group
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.

  • 20+ years as CTO, 50M+ app downloads
  • Architected and secured large-scale IoT fleets
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Looking for a Penetration Test?

Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.