mercure
Unauthenticated Remote Code Execution in the DICOM Receiver
The mercure DICOM receiver builds an operating-system command from values carried inside incoming DICOM data. A host that can send DICOM data to the receiver can cause arbitrary commands to run on the receiving server, with no authentication of any kind.
system() call was removed: getdcmtags now posts to the bookkeeper over a socket with URL-encoded values instead of constructing a shell command.CVEPendingGHSAPendingDescription
mercure is an open-source DICOM routing and processing platform for medical imaging workflows. We appreciate the maintainers' work on it and their prompt, constructive response to this report.
The getdcmtags binary is part of the DICOM receiver pipeline. After storescp writes an incoming DICOM file, getdcmtags extracts tag values and registers the file with the bookkeeper service. It builds that registration call as a shell command through string concatenation and runs it with system().
Two tag values, SOPInstanceUID and SeriesInstanceUID, are read with DCMTK's findAndGetOFStringArray(), which returns the raw bytes without enforcing the DICOM UID format (digits and dots only). They are concatenated directly into the command string:
getdcmtags/main.cpp:64-87
void sendBookkeeperPost(OFString filename, OFString fileUID, OFString seriesUID){ if (bookkeeperAddress.empty()) { return; }
std::string cmd = "wget -q -T 1 -t 3 --post-data=\"filename="; cmd.append(filename.c_str()); // contains SeriesInstanceUID cmd.append("&file_uid="); cmd.append(fileUID.c_str()); // SOPInstanceUID, attacker-controlled cmd.append("&series_uid="); cmd.append(seriesUID.c_str()); // SeriesInstanceUID cmd.append("\""); cmd.append(" --header=\"Authorization: Token "); cmd.append(bookkeeperToken); cmd.append("\" http://"); cmd.append(bookkeeperAddress); cmd.append("/register-dicom -O /dev/null");
system(cmd.data()); // command injection}Before concatenation, readTag() applies a small character substitution that is insufficient and, in one case, harmful: a carriage return is rewritten to ;, which introduces a shell command separator. Dollar signs, parentheses, backticks, and pipes pass through untouched. Because the values sit inside double quotes in the command, $(...) command substitution is still evaluated by the shell:
getdcmtags/main.cpp:172-204
OFCondition result = dataset->findAndGetOFStringArray(tag, out);// ...for (size_t i = 0; i < out.length(); i++){ switch (out[i]) { case 13: // CR -> semicolon out[i] = ';'; break; case 10: // LF -> space out[i] = ' '; break; case 34: // double quote -> single quote out[i] = 39; break; default: break; }}The receiver runs storescp with --promiscuous, so it accepts associations from any calling AE title without authentication. A single DICOM C-STORE to port 11112 carrying a crafted SOPInstanceUID is enough: getdcmtags reads the value, passes it to sendBookkeeperPost(), and system() evaluates the embedded command during argument expansion. The bookkeeper does not need to be reachable; the injected command runs before wget fails.
The injection targets SOPInstanceUID specifically. SeriesInstanceUID is also attacker-controlled, but it is used earlier for directory creation and file renaming; shell metacharacters there make those file operations fail and getdcmtags exits before reaching the vulnerable call. SOPInstanceUID is consumed only by sendBookkeeperPost().
Command execution as the mercure user (uid 1000) is confirmed by the output of an injected id command:
Proof-of-concept output
uid=1000(mercure) gid=1000(mercure) groups=1000(mercure)Impact
- A remote, unauthenticated attacker who can reach the DICOM receiver can execute operating-system commands on the receiving server. The receiver accepts associations from any calling AE title, so no credentials are involved.
- The injected commands run as the
mercureuser inside the receiver container, with access to the imaging data the receiver processes and to its mounted configuration and data volumes. Depending on deployment, this provides a foothold for moving further into the clinical network. - For a hospital or clinic, this means the server receiving patient imaging can be taken over by anyone able to reach it on the network, with no login required.
Mitigation
Upgrade to mercure 0.4.1, which removes the system() call: getdcmtags now posts to the bookkeeper over a socket with URL-encoded values rather than building a shell command. Two general defenses apply to any code in this shape: invoke external programs without a shell (an argument vector via execvp() or posix_spawn(), not system()), and validate DICOM UIDs against their specified format (digits and dots only) before use. Until upgrading, restrict network access to the receiver port so that only trusted imaging sources can reach it.
Defender's Checklist
Upgrade to 0.4.1 or later
Deploy the patched release and confirm the receiver image was rebuilt, since the fix is in the
getdcmtagsbinary.Restrict the receiver port
Limit network access to the DICOM port (default 11112) to known imaging modalities and DICOM nodes; treat associations from unexpected hosts as suspicious.
Review receiver logs
Check receiver and
getdcmtagslogs for unexpected commands orwgetfailures around associations from unknown sources.
Severity Reasoning
storescp runs with --promiscuous and accepts any calling AE title, so no credentials are required.UI:NProcessing is automatic on receipt; no user interaction is involved.S:UCode executes within the receiver's own security scope.C/I/A:HArbitrary command execution as the receiver process gives full read, modification, and denial over the data and host resources it controls.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.
