mercure
Command Injection via Unquoted Target Fields in Connection Tests
When mercure tests a dispatch target's connection, it builds shell commands (ping, echoscu, sftp) by interpolating the target's configured fields into a string and running it through a shell. None of the fields are quoted or validated, across DICOM, DICOM+TLS, and SFTP targets, so an administrator can place shell metacharacters in any of them and run arbitrary commands as the mercure user. The safe pattern already exists in the codebase: the rsync handler builds commands as argument lists.
Description
mercure is an open-source DICOM orchestration platform; we appreciate the maintainers' prompt, constructive response to this report. When an administrator configures a dispatch target (a destination for processed studies), the web interface offers a "Test" button that checks connectivity.
Each target type's test builds shell commands from the target's fields and runs them through a shell. The DICOM test interpolates four fields into ping and echoscu without quoting:
app/dispatch/target_types/builtin.py:105-131
async def test_connection(self, target: DicomTarget, target_name: str): target_ip = target.ip or "" target_port = target.port or "" target_aec = target.aet_target or "ANY-SCP" target_aet = target.aet_source or "ECHOSCU"
if target_ip and target_port and not loopback_mode: ping_result, *_ = await async_run(f"ping -w 1 -c 1 {target_ip}") # unquoted # ... cecho_result, *_ = await async_run( f"echoscu -to 2 -aec {target_aec} -aet {target_aet} {target_ip} {target_port}" )DICOM+TLS adds the key, certificate, and CA paths, for seven injectable fields; SFTP interpolates the host, user, and folder. async_run() always uses a shell, so metacharacters in any field are interpreted:
| Field | DICOM | DICOM+TLS | SFTP |
|---|---|---|---|
ip / host | yes | yes | yes |
port | yes | yes | — |
aet_target / aet_source | yes | yes | — |
tls_key / tls_cert / ca_cert | — | yes | — |
user / folder | — | — | yes |
An administrator who sets any field to a value containing shell command-substitution syntax and clicks Test runs that command as the mercure user; the test still returns success, so there is no visible indication. The test endpoint itself requires only an authenticated session, so once a malicious target exists, any logged-in user can trigger it. We are not publishing a working payload. The SFTP password is a separate instance of the same pattern, covered in the SFTP password injection advisory.
The safe pattern is already present: the rsync handler builds commands as argument lists and runs them with async_run_exec() (no shell).
app/dispatch/target_types/rsync.py:139
result, stdout, stderr = await async_run_exec(*c) # create_subprocess_exec, no shellCommand execution as the mercure user is confirmed by the output of an injected id:
Proof-of-concept output
uid=1000(mercure) gid=1000(mercure) groups=1000(mercure)Impact
- An administrator can run arbitrary OS commands as the
mercureuser in the UI container by placing shell metacharacters in any target field and testing the target. This affects every field across DICOM, DICOM+TLS, and SFTP targets. - The test endpoint requires only an authenticated session, so once a malicious target is stored, any logged-in user can trigger the injection.
- The UI container reaches the shared configuration volume, Redis, and the Docker network, so command execution there is a foothold into the rest of the deployment.
Mitigation
Upgrade to mercure 0.4.1, which routes dispatch commands through list-form execution instead of building shell strings. In general, build external commands as argument vectors with create_subprocess_exec (the codebase's async_run_exec), never interpolate untrusted values into a shell string, and validate target fields (AE titles, IPs, ports) against their expected formats.
Defender's Checklist
Upgrade to 0.4.1 or later
Deploy the patched release, which removes shell execution from the dispatch handlers.
Review configured targets
Audit target fields in
mercure.jsonfor shell metacharacters in IP, AE title, certificate path, host, user, or folder values.Restrict administrator access
Limit administrator credentials and confirm the default admin password has been changed.
Severity Reasoning
References
How We Can Help
Who We Are
The security researchers behind this advisory.

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

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.
