Skip to main content

Wie ich Docker-Container durch das Ausnutzen von ImageMagick-Schwachstellen hackte

Artikel von
Blog Header Hacking Docker

11. März 2021

0 Min. Lesezeit

Was wäre, wenn ich Ihnen sagte, dass die Verwendung verwundbarer Docker-Images Sie einem erheblichen und unmittelbaren Risiko einer Command-Injection-Sicherheitslücke aussetzt, durch die Docker-Container mit diesem verwundbaren Image gehackt werden können?

In diesem Artikel führe ich Sie Schritt für Schritt durch das Container-Hacking. Dabei nutzen wir eine Node.js-basierte Webanwendung aus, die ein verwundbares, aber offizielles Docker-Basisimage für Node.js verwendet.

Hacking eines Containers mit einem verwundbaren Node.js-Image

Um diese Schwachstelle zu veranschaulichen, verwende ich eine ältere Node.js-Runtime-Version mit einer Fastify-Anwendung, die Bilder auf eine bestimmte Größe skaliert. Die Bildskalierung lagert die Verarbeitung an die ImageMagick-Bibliothek aus, die ein praktisches Kommandozeilenprogramm namens convert bereitstellt.

ImageMagick ist eine Sammlung von Programmiersprachen-Bindings und Kommandozeilenprogrammen, die häufig in Webanwendungen zur Bildverarbeitung eingesetzt werden, etwa zum Umwandeln in ein anderes Bildformat, Skalieren, Zuschneiden und mehr.

Die bedauerliche Realität ist jedoch, dass bei ImageMagick im Laufe der Jahre zahlreiche Sicherheitslücken aufgetreten sind. Eine davon ist die bekannte ImageTragick-Schwachstelle (CVE-2016-3714). Sie wird als fehlerhafte Eingabevalidierung eingestuft. Proof-of-Concept-Exploits sind jedoch seit 2016 frei verfügbar und können zu einer Remote-Command-Injection führen.

Diese Geschichte handelt vom Hacken von Containern – nicht wegen fehlender Best Practices für Sicherheit oder verwundbarer Abhängigkeiten von Node.js-Anwendungen, sondern aufgrund von Open-Source-Komponenten von Drittanbietern, die in einer Docker-basierten Node.js-Anwendung enthalten sein können.

Die Node.js-Anwendung

Beginnen wir mit dem Docker-Image, das die Node.js-Anwendung enthält. Der Einfachheit halber verwende ich ein sehr schlankes Dockerfile-Setup:

FROM node:6.1.0-wheezy 

RUN mkdir /usr/src/goof
COPY . /usr/src/goof
WORKDIR /usr/src/goof

RUN npm update
RUN npm install
CMD ["npm", "start"]

Beachten Sie, dass dieses Dockerfile keine sicheren Richtlinien für die Erstellung von Docker-Images befolgt und hier nur der Kürze halber verwendet wird. In unserem Cheatsheet zu 10 Best Practices für die Containerisierung von Node.js-Webanwendungen mit Docker finden Sie Empfehlungen für mehr Sicherheit.

Die Fastify-Webanwendung stellt eine Route unter **/**upload bereit, die Datei-Uploads annimmt und mit dem hochgeladenen Bild den Prozess /usr/bin/convert ausführt, um es auf eine vorgegebene Größe zu bringen. Anschließend leitet sie den Benutzer auf eine Erfolgsseite weiter.

fastify.post("/upload", (req, res) => {
  req.multipart(uploadFileHandler, err => {
    if (err) {
      fastify.log.error(err);
    }

    fastify.log.info("upload completed");
    fastify.log.info("commencing image resizing");

    child_process.execFile(
      "/usr/bin/convert",
      [FILE_OUTPUT, "-resize", "280x150", FILE_RESULT],
      () => {
        res.redirect("/result.html");
      }
    );
  });
});

Der Code verwendet tatsächlich eine sichere Node.js-API zur Prozessausführung, die keine Verkettung von Zeichenfolgen zulässt.

Ist es eine gute Praxis, System-Shells zu starten und Prozesse auszuführen? Nicht wirklich – zumindest nicht ohne guten Grund. Handelt es sich um einen Node.js-Worker-Prozess, der eine Warteschlange abarbeitet oder von einem Job-Scheduler Aufträge erhält, passt das wahrscheinlich zu diesem Anwendungsfall.

Aber wer sind wir, über einen ähnlichen Anwendungsfall zu urteilen, der live bei der Google I/O 2017 vorgestellt wurde?

Videoplayer mit Firebase- und Google-Cloud-Code, der Bilder herunterlädt, skaliert und hochlädt.

Einen Docker-Container hacken

Zunächst habe ich das Projekt in Snyk importiert. Dabei werden unterstützte Manifestdateien automatisch erkannt – in meinem Fall die package.json der Anwendung und das Dockerfile.

Wie Sie sehen, enthält selbst die neueste Version des Node.js-6-Images (node:6-stretch) standardmäßig 866 Sicherheitslücken im Container-Image. Zum Glück verwenden wir Snyk: Das Tool empfiehlt verschiedene alternative Basisimages, mit denen sich die Sicherheit der Anwendung auf mindestens zwei Arten verbessern lässt:

  1. eine geringere Anzahl von Sicherheitslücken, wodurch sich die Angriffsfläche verkleinert.

  2. Basisimages, in denen die Node.js-Runtime selbst nicht verwundbar ist. Ein äußerst wichtiger und häufig übersehener Vorteil der empfohlenen Basisimages.

Snyk-Projektübersicht mit Dockerfile-Details und einer Tabelle mit Empfehlungen zu Upgrades von Basis-Images, Schwachstellen und Schweregradbewertungen.

Machen wir aus der ImageTragick-Sicherheitslücke in der im Container-Image enthaltenen ImageMagick-Version einen Remote-Command-Injection-Angriff.

Der Exploit verwendet speziell präparierte Bilddateien, die die Parsing-Funktion eines Delegates-Features in der ImageMagick-Bibliothek umgehen. Diese Funktion von ImageMagick führt Systembefehle aus, die mit Anweisungen in der Bilddatei verknüpft sind. Verlässt man den erwarteten Eingabekontext, kann ein Angreifer Systembefehle einschleusen.

Dieses Git-Repository enthält einen Proof-of-Concept-Exploit für Remote-Codeausführung. Dabei wird Netcat heruntergeladen, um in Distributionen, die Netcat nicht standardmäßig mitliefern (wie Debian Wheezy), eine Reverse Shell einzurichten.

So sieht die Proof-of-Concept-Nutzlast in der Bilddatei rce1.jpg aus:

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.0/oops.jpg"|touch "rce1)'
pop graphic-context

Die Bilddatei steht hier unter der Kontrolle des Benutzers. Ein Angreifer könnte daher eine solche Nutzlast erstellen, die einen von UNIX-ähnlichen Betriebssystemen unterstützten Befehl ausführt, um eine leere Datei zu erstellen – in diesem Fall touch rce1.

Sobald das Container-Image erstellt ist, können wir es ausführen:

docker run --rm --name rce rce

Unsere einfache Webanwendung ermöglicht es uns, eine Datei hochzuladen:

Einfache Webseite mit der Anzeige „Hallo!“, einer Bild-Upload-Funktion und einer Schaltfläche „Größe ändern“, um Fotos auf 280 × 150 Pixel zu konvertieren.

Wenn wir auf die Schaltfläche Resize klicken, um die Datei rce1.jpg zu verarbeiten, wird die Command-Injection ausgelöst.

Stellen wir eine Verbindung zur laufenden Docker-Container-Anwendung her, um den Angriff zu überprüfen. Wie Sie sehen, wurde im Stammverzeichnis der Node.js-Anwendung eine neue Datei namens rce1.jpg erstellt:

Terminal mit Docker-Container-Befehlen und einer Verzeichnisübersicht eines Node.js-Projekts, darunter package.json, Dockerfile, Exploits und eine hervorgehobene Datei.

Zusammenfassung: Container-Hacking

Die große Zahl an Sicherheitslücken in verschiedenen Docker-Images dürfte Sie kaum überraschen. Uns geht es genauso, denn wir haben festgestellt, dass die zehn meistgenutzten Docker-Images auf Docker Hub Sicherheitslücken aufwiesen – wie in unserem Bericht State of Open Source Security dargestellt.

Balkendiagramm mit dem Titel „Schwachstellen in offiziellen Container-Images“, das die Anzahl für Node, PostgreSQL, Nginx und weitere Images in den Jahren 2018 und 2019 vergleicht.

Der hier demonstrierte Angriff umgeht sämtliche Konventionen für sicheres Programmieren vollständig und geht über die Sicherheit der Node.js-Runtime oder der gebündelten Open-Source-Node-Module hinaus. Das unterstreicht, wie wichtig es ist, Ihre Docker-Images abzusichern. Genau dafür wurde Snyk entwickelt!

Snyk hebt priorisierte Sicherheitslücken hervor und gibt Ihnen Empfehlungen zur Behebung – beispielsweise durch den Wechsel zu anderen Basisimages:

Tabelle mit Empfehlungen für Upgrades von Node.js-Basis-Images, einschließlich der Anzahl an Schwachstellen und ihrer Einstufung als kritisch, mittel oder niedrig.

Wenn Sie den Angriff Schritt für Schritt nachstellen möchten, können Sie den README-Anweisungen im Open-Source-Repository folgen. Darin wird auch erklärt, wie Sie mit dieser ImageTragick-Schwachstelle einen Remote-Reverse-Shell-Angriff durchführen.

Wie geht es weiter?

Ob die Repositorys Ihres Dockerized-Anwendungsprojekts öffentlich oder privat sind: Mit dem kostenlosen Snyk-Tarif können Sie bekannte Sicherheitslücken in Ihren Docker-Images testen und beheben. Scannen und beheben Sie dabei auch gleich Sicherheitslücken in Ihren Node.js-Anwendungen!

Bitte beachten Sie, dass das hier und im begleitenden Open-Source-Repository gezeigte Dockerfile aufgrund fehlender Sicherheitsmaßnahmen nicht empfohlen wird. In den folgenden Blogbeiträgen finden Sie hilfreiche Best Practices für Docker-Sicherheit:

Container-Sicherheit mit Fokus auf Entwickler

Snyk findet und behebt automatisch Schwachstellen in Container-Images und Kubernetes-Workloads.