How I was hacking docker containers by exploiting ImageMagick vulnerabilities
11. März 2021
0 Min. LesezeitWhat if I told you that using vulnerable Docker images can put you at significant and imminent risk of a command injection security vulnerability of hacking docker containers that use that vulnerable Docker image?
In this article, I’ll take you through a step-by-step process of container hacking, in which we will exploit a Node.js-based web application that uses a vulnerable, yet official, Docker base image for Node.js.
Container hacking of a vulnerable Node.js image
To demonstrate this vulnerability, I’m going to use an old Node.js runtime version with a Fastify application that resizes images to a specific size. The image resizing action works by offloading the work to the ImageMagick library, which provides a handy convert command-line tool.
ImageMagick is a set of programming language bindings and command line tools that are commonly used in web applications to process images, such as converting them from one image format to another, resizing, cropping, and more.
The unfortunate reality, however, is that ImageMagick has demonstrated many security vulnerabilities over the years, one of which is the famous ImageTragick vulnerability (CVE-2016-3714). This classifies itself as an Improper Input Validation, but proof of concept exploits have been available in the wild since 2016 which may lead to remote command injection.
This is a story of hacking containers not due to the lack of security best practices, or vulnerable dependencies of Node.js applications, but that of third-party open-source components which may exist in a Docker-based Node.js application.
The Node.js application
Let’s start with the Docker image that bundles the Node.js application. To keep things simple, I will use a very lightweight Dockerfile setup:
1FROM node:6.1.0-wheezy
2
3RUN mkdir /usr/src/goof
4COPY . /usr/src/goof
5WORKDIR /usr/src/goof
6
7RUN npm update
8RUN npm install
9CMD ["npm", "start"]
Note that this Dockerfile doesn’t follow secure guidelines for building Docker images and it is only used for brevity. Check out our cheatsheet on 10 best practices to containerize Node.js web applications with Docker for security best practices.
The Fastify web application provides an **/**upload
path route that accepts file uploads, and executes a process, /usr/bin/convert
, with the uploaded image, in order to convert it to a given size and redirects the user to a success page.
1fastify.post("/upload", (req, res) => {
2 req.multipart(uploadFileHandler, err => {
3 if (err) {
4 fastify.log.error(err);
5 }
6
7 fastify.log.info("upload completed");
8 fastify.log.info("commencing image resizing");
9
10 child_process.execFile(
11 "/usr/bin/convert",
12 [FILE_OUTPUT, "-resize", "280x150", FILE_RESULT],
13 () => {
14 res.redirect("/result.html");
15 }
16 );
17 });
18});
The code actually uses a secure Node.js API for process execution, not allowing string concatenation.
Is it a good practice to spawn system shells and execute processes? Not really—at least not with good justification. If this is a Node.js worker process consuming from a queue or being fed by a job scheduler then it probably fits this use case.
But who are we to judge if a similar use case was demoed live in Google I/O 2017 event?
Let’s hack a Docker container
I started with importing the project to Snyk, which automatically picks up the supported manifest files, in my case these are the application’s package.json and Dockerfile.
You can see that even with the latest version of the Node.js 6 image (node:6-stretch) there are 866 security vulnerabilities that exist by default in the container image. Luckily, we use Snyk, and it recommends us various alternate base image upgrades that can improve the security of the application in at least two different ways:
a lower number of vulnerabilities, which reduces the attack surface.
base images in which the Node.js runtime itself isn’t vulnerable. A highly important, and often unnoticed benefit, of using recommended base image advice.
Let’s turn that ImageTragick security vulnerability in the version of ImageMagick that exists in the container image, into a remote command injection attack.
The exploit makes use of specially crafted image files that bypass the parsing functionality of a delegates feature in the ImageMagick library. This capability of ImageMagick executes system commands that are associated with instructions inside the image file. Escaping from the expected input context allows an attacker to inject system commands.
This git repository includes proof-of-concept exploit for a remote command execution with a netcat download to achieve a reverse shell in distributions that do not bundle netcat by default (such as Debian wheezy).
Here is what the proof-of-concept payload in the image file rce1.jpg
looks like:
1push graphic-context
2viewbox 0 0 640 480
3fill 'url(https://127.0.0.0/oops.jpg"|touch "rce1)'
4pop graphic-context
The image file here is in the user’s control, and as such, a malicious attacker could create that payload, which executes a command supported by UNIX-like operating systems to create an empty file—in this case, touch rce1
.
Once the container image is built, we can run it:
1docker run --rm --name rce rce
Our plain and simple web application allows us to upload a file:
When we hit the Resize button to process the rce1.jpg
file, it will trigger the command injection.
Let’s connect to the running Docker container application to validate this attack. As we can see, a new file named rce1.jpg
was created in the root directory of the Node.js application:
Container hacking summary
I’m sure you aren’t overly surprised regarding the number of security vulnerabilities that are prominent across various Docker images. Neither are we, given the fact that we observed that the top 10 Docker images on Docker Hub contained security vulnerabilities, as presented in our State of Open Source Security report.
The attack we demonstrated here completely bypasses all secure coding conventions and goes beyond the security of the Node.js runtime or the open source node modules dependencies that the application bundles. This really emphasizes the need to secure your Docker images. Snyk was created to do just that!
By highlighting prioritized vulnerabilities, Snyk provides you with remediation advice in the form of other base images you can switch to:
If you’d like to re-create the attack step-by-step you are welcome to follow the README instructions in the open source repository which also details how to perform a remote reverse shell attack, based on this ImageTragick vulnerability.
What’s next?
Whether your Dockerized application project’s repositories are open source or private, you can use the Snyk free tier to test and fix known security vulnerabilities in your Docker images. Scan and fix your Node.js applications too while you’re at it!
Please note that the Dockerfile presented here, and in the accompanying open-source repository, is not recommended due to a lack of security practices. Instead, find some valuable Docker security practices to work with, in the following blog posts:
10 best practices to containerize Node.js web applications with Docker
Docker for Node.js developers: 5 things you need to know not to fail your security
Developer-First Security für Container
Mit Snyk identifizieren Sie Schwachstellen in Container-Images und Kubernetes-Workloads und adressieren sie automatisch.