mercure
Command Injection via Unquoted SFTP Password
When mercure tests an SFTP dispatch target, it builds an sshpass shell command and interpolates the target's configured password into it without quoting. An administrator can set the password to a value containing shell command-substitution syntax; testing the target then runs arbitrary commands as the mercure user. This is a single-field instance of the broader unquoted-target-field issue, kept separate because it reaches a different command (sshpass).
Description
mercure is an open-source DICOM orchestration platform; we appreciate the maintainers' prompt, constructive response to this report. mercure can dispatch processed studies to an SFTP target, and the web interface offers a "Test" button to check the connection.
The SFTP test builds an sshpass command and interpolates the configured password into it without quoting:
app/dispatch/target_types/builtin.py:215-228
async def test_connection(self, target: SftpTarget, target_name: str): ping_result, *_ = await async_run(f"ping -w 1 -c 1 {target.host}") # ... command = "sftp -o StrictHostKeyChecking=no " + f''' "{target.user}@{target.host}:{target.folder}" <<< "" ''' if target.password: command = f"sshpass -p {target.password} " + command # unquoted injection logger.debug(command) # password logged at DEBUG result, stdout, stderr = await async_run(command, shell=True, executable="/bin/bash")async_run() runs the string through a shell, so $(...) command substitution in the password is evaluated before sshpass even runs:
app/webinterface/common.py:67-74
async def async_run(cmd, **params) -> Tuple[Optional[int], bytes, bytes]: proc = await asyncio.create_subprocess_shell( cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, **params ) stdout, stderr = await proc.communicate() return proc.returncode, stdout, stderrAn administrator who sets the SFTP password to a value containing command-substitution syntax and clicks Test runs that command as the mercure user; sshpass then receives an empty password and the connection fails, but the injected command has already run. The test endpoint requires only an authenticated session, so any logged-in user can trigger a stored malicious target. We are not publishing a working payload.
Only the test path is exploitable. The actual file-dispatch path builds the command as a list (via shlex.split), which neutralizes the injection. The remaining target fields are covered in the unquoted target-field advisory.
The rsync handler shows the safe pattern: it passes the password as a list element and executes with async_run_exec() (no shell).
app/dispatch/target_types/rsync.py:31
sshpass_cmd=["sshpass", "-p", target.password], # list, no shell interpretationCommand 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 setting an SFTP target password to a command-substitution payload and testing the target. - The test endpoint requires only an authenticated session, so once a malicious SFTP 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 this 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, pass secrets and other values as argument-vector elements with create_subprocess_exec, never interpolate them into a shell string; the codebase's rsync handler already does this.
Defender's Checklist
Upgrade to 0.4.1 or later
Deploy the patched release, which removes shell execution from the SFTP dispatch handler.
Review SFTP targets
Audit SFTP target passwords in
mercure.jsonfor shell metacharacters or command-substitution syntax.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.
