Alle Sicherheitshinweise

mercure

Nicht authentifizierte Remote Code Execution im DICOM-Receiver

Der mercure-DICOM-Receiver baut aus Werten, die in eingehenden DICOM-Daten enthalten sind, einen Betriebssystembefehl zusammen. Ein Host, der DICOM-Daten an den Receiver senden kann, kann dadurch beliebige Befehle auf dem empfangenden Server ausführen, ganz ohne Authentifizierung.

Verfasst vonVolker Schönefeld, Simon Weber2026-05-30
SchweregradKritischCVSS 9.8CVSS-3.1-VektorAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HCWECWE-78 (OS Command Injection)ProduktmercureBetroffene VersionenAlle Releases von 0.2.0-beta.1 bis 0.4.0-beta.9, über alle Deployment-Varianten hinweg (Docker, systemd, Nomad).Behoben in0.4.1. Der verwundbare system()-Aufruf wurde entfernt: getdcmtags postet nun über einen Socket mit URL-kodierten Werten an den Bookkeeper, statt ein Shell-Kommando zu bauen.CVEAusstehendGHSAAusstehend

Beschreibung

mercure ist eine quelloffene DICOM-Routing- und Verarbeitungsplattform für Workflows in der medizinischen Bildgebung. Wir schätzen die Arbeit der Maintainer daran sowie ihre schnelle, konstruktive Reaktion auf diesen Bericht.

Das getdcmtags-Binary ist Teil der DICOM-Receiver-Pipeline. Nachdem storescp eine eingehende DICOM-Datei geschrieben hat, extrahiert getdcmtags die Tag-Werte und registriert die Datei beim Bookkeeper-Dienst. Diesen Registrierungsaufruf baut es per String-Verkettung als Shell-Kommando zusammen und führt ihn mit system() aus.

Zwei Tag-Werte, SOPInstanceUID und SeriesInstanceUID, werden mit DCMTKs findAndGetOFStringArray() gelesen, das die Rohbytes ohne Prüfung des DICOM-UID-Formats (nur Ziffern und Punkte) zurückgibt. Sie werden direkt in das Kommando verkettet:

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()); // enthält SeriesInstanceUID
cmd.append("&file_uid=");
cmd.append(fileUID.c_str()); // SOPInstanceUID, vom Angreifer kontrolliert
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
}

View source →

Vor der Verkettung führt readTag() eine kleine Zeichenersetzung durch, die unzureichend und in einem Fall sogar schädlich ist: Ein Carriage Return wird zu ; umgeschrieben, was einen Shell-Kommandotrenner einführt. Dollarzeichen, Klammern, Backticks und Pipes bleiben unverändert. Da die Werte im Kommando in doppelten Anführungszeichen stehen, wird $(...)-Kommandosubstitution weiterhin von der Shell ausgewertet:

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 -> Semikolon
out[i] = ';';
break;
case 10: // LF -> Leerzeichen
out[i] = ' ';
break;
case 34: // doppeltes -> einfaches Anführungszeichen
out[i] = 39;
break;
default:
break;
}
}

View source →

Der Receiver betreibt storescp mit --promiscuous und akzeptiert daher Verbindungen von beliebigen Calling-AE-Titeln ohne Authentifizierung. Ein einziges DICOM-C-STORE an Port 11112 mit einer präparierten SOPInstanceUID genügt: getdcmtags liest den Wert, übergibt ihn an sendBookkeeperPost(), und system() wertet das eingebettete Kommando während der Argumentexpansion aus. Der Bookkeeper muss nicht erreichbar sein; das eingeschleuste Kommando läuft, bevor wget fehlschlägt.

Die Injection zielt gezielt auf SOPInstanceUID. SeriesInstanceUID ist ebenfalls vom Angreifer kontrollierbar, wird aber zuvor für Verzeichniserstellung und Dateiumbenennung verwendet; Shell-Metazeichen lassen diese Dateioperationen fehlschlagen, sodass getdcmtags beendet wird, bevor der verwundbare Aufruf erreicht ist. SOPInstanceUID wird ausschließlich von sendBookkeeperPost() verarbeitet.

Die Befehlsausführung als Benutzer mercure (uid 1000) wird durch die Ausgabe eines eingeschleusten id-Kommandos bestätigt:

Proof-of-Concept-Ausgabe

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

Auswirkung

  • Ein entfernter, anonymer Angreifer, der den DICOM-Receiver erreichen kann, kann Betriebssystembefehle auf dem empfangenden Server ausführen. Der Receiver akzeptiert Verbindungen von beliebigen Calling-AE-Titeln, sodass keine Anmeldedaten erforderlich sind.
  • Die eingeschleusten Befehle laufen als Benutzer mercure innerhalb des Receiver-Containers, mit Zugriff auf die vom Receiver verarbeiteten Bilddaten sowie auf die eingebundenen Konfigurations- und Datenvolumes. Je nach Deployment verschafft dies einen Ausgangspunkt für weitere Bewegungen im klinischen Netzwerk.
  • Für ein Krankenhaus oder eine Klinik bedeutet das: Der Server, der Patientenbilder empfängt, kann von jedem übernommen werden, der ihn im Netzwerk erreicht, ganz ohne Anmeldung.

Abhilfe

Aktualisieren Sie auf mercure 0.4.1; dort wurde der system()-Aufruf entfernt: getdcmtags postet nun über einen Socket mit URL-kodierten Werten an den Bookkeeper, statt ein Shell-Kommando zu bauen. Zwei allgemeine Schutzmaßnahmen gelten für jeden Code dieser Art: externe Programme ohne Shell aufrufen (ein Argumentvektor über execvp() oder posix_spawn(), nicht system()) und DICOM-UIDs vor der Verwendung gegen ihr spezifiziertes Format (nur Ziffern und Punkte) prüfen. Bis zum Update sollte der Netzwerkzugriff auf den Receiver-Port auf vertrauenswürdige Bildquellen beschränkt werden.

Checkliste für Betreiber

  • Auf 0.4.1 oder neuer aktualisieren

    Das gepatchte Release ausrollen und sicherstellen, dass das Receiver-Image neu gebaut wurde, da der Fix im getdcmtags-Binary liegt.

  • Receiver-Port einschränken

    Den Netzwerkzugriff auf den DICOM-Port (Standard 11112) auf bekannte Bildgebungsmodalitäten und DICOM-Knoten beschränken; Verbindungen von unerwarteten Hosts als verdächtig behandeln.

  • Receiver-Logs prüfen

    Receiver- und getdcmtags-Logs auf unerwartete Befehle oder wget-Fehler im Umfeld von Verbindungen unbekannter Quellen prüfen.

Bewertung im Detail

AV:NDer Receiver lauscht auf einem DICOM-Netzwerkport (Standard 11112); der Angriff wird als reguläre C-STORE-Verbindung übertragen.AC:LEine einzige präparierte Verbindung genügt. Es ist keine Race-Condition oder spezielle Zeitabstimmung nötig.PR:Nstorescp läuft mit --promiscuous und akzeptiert beliebige Calling-AE-Titel, sodass keine Anmeldedaten erforderlich sind.UI:NDie Verarbeitung erfolgt automatisch beim Empfang; keine Benutzerinteraktion erforderlich.S:UDer Code wird im Sicherheitskontext des Receivers selbst ausgeführt.C/I/A:HBeliebige Befehlsausführung als Receiver-Prozess gewährt vollständigen Lese-, Änderungs- und Verweigerungszugriff auf die Daten und Host-Ressourcen, die er kontrolliert.

Referenzen

So können wir helfen

Wer wir sind

Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec-Forscher

Ich evaluiere Ihr SaMD mit derselben branchenprägenden Sicherheitsexpertise, die ich dem BAK MV für die Überarbeitung des B3S-Standards beigetragen habe.

  • Promotion über Krankenhaus-Cybersicherheit
  • Kritische Schwachstellen in Krankenhaussystemen gefunden
  • Alumni der THB MedSec-Forschungsgruppe
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

Als ehemaliger CTO und Entwickler, der zum Pentester wurde, arbeite ich mit Ihrem Team zusammen, um Schwachstellen aufzudecken und Lösungen zu finden, die zu Ihrer Architektur passen.

  • 20+ Jahre als CTO, 50+ Mio. App-Downloads
  • Architektur und Absicherung großer IoT-Flotten
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Penetrationstest gesucht?

Machine Spirits ist spezialisiert auf Sicherheitsbewertungen für Medizinprodukte und Gesundheits-IT. Von MDR-Penetrationstests bis C5-Cloud-Compliance helfen wir MedTech-Unternehmen, regulatorische Anforderungen zu erfüllen.