All Advisories

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).

Authored byVolker Schönefeld, Simon Weber2026-05-30
SeverityHighCVSS 7.2CVSS 3.1 VectorAV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:HCWECWE-78 (OS Command Injection)ProductmercureAffected VersionsAll releases that include SFTP target support (since September 2021) through 0.4.0-beta.9.Fixed In0.4.1. The shell-injection class was removed across the dispatch code by routing commands through list-form execution (no shell) instead of string interpolation.CVEPendingGHSAPending

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")

View source →

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, stderr

View source →

An 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 interpretation

View source →

Command 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 mercure user 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.json for shell metacharacters or command-substitution syntax.

  • Restrict administrator access

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

Severity Reasoning

AV:NThe target is configured and tested over the network through the web interface.AC:LSetting the password and triggering the test is sufficient.PR:HCreating or editing a target requires an administrator session.UI:NNo user interaction beyond the attacker's own request.S:UExecution stays within the UI service account's scope.C/I/A:HCommand execution as the mercure user exposes and can modify the data and configuration it reaches and can disrupt the service.

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.