insights

Running vulnerable docker containers in minutes

May 13, 2020

Overview

In this guide we’ll be showing you how to quickly and easily launch vulnerable Docker containers for research and testing purposes. We will be using various pre-built environments provided by Vulnhub. In addition, we provide a Bash script to help users launch multiple containers at once.

Why?

While reviewing different types of vulnerability scanning tools and services, it became very clear that testing such tools would require at least some known vulnerable applications to be configured. However, doing so may take up a considerable amount of time and effort. We decided to use a collection of pre-configured Docker containers and a Bash script to speed up this process.

Prerequisites

First, download and install the following required software:

To verify Docker was successfully installed, run the following:

$ docker --version
Docker version 19.03.8, build afacb8b

To verify Docker Compose was successfully installed on your machine, run the following:

$ docker-compose -v
docker-compose version 1.25.4, build unknown

Bash script

To make our lives easier we’ve made a simple Bash script (available here on GitHub) that lets you start and stop multiple Docker containers at once. The script runs a few checks to ensure your system meets all the requirements, and provides useful information in case there are any missing packages. The script will consume less than 1GB of system memory with the selected containers.

By default, the script contains six vulnerable services, each running on a unique port to avoid conflicts. These are:

Running the script

Make the file executable:

chmod +x vulnerables.sh

To start all containers:

./vulnerables.sh start

To stop all containers:

./vulnerables.sh stop

Logging and analysis

Docker has a simple way of fetching logs from each container. For incident responders or security researchers, reviewing system logs is a crucial part of any investigation. To get more information about logs visit the official Docker page here.

As an example use case, one of our tasks was to review and compare different vulnerability scanning services, to determine how intrusive or invasive they really are. By invasive I mean: does it send thousands of requests to a web page, brute force directory names, or does it overwhelm the service with requests. These were all interesting questions which required some level of access to system logs to answer.

Fetching container logs

To retrieve and export logs from a Docker container you first get the list of running containers using docker ps or docker stats:

Now you can choose which logs you want from each container: docker logs [container-name]:

$ docker logs heartbleed_nginx_1
37.120.XXX.XXX - - [30/Apr/2020:16:59:11 +0000] "GET / HTTP/1.1" 200 120 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
37.120.XXX.XXX - - [30/Apr/2020:16:59:15 +0000] "GET / HTTP/1.1" 200 120 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
37.120.XXX.XXX - - [30/Apr/2020:16:59:15 +0000] "GET / HTTP/1.1" 200 120 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
37.120.XXX.XXX - - [30/Apr/2020:16:59:15 +0000] "GET / HTTP/1.1" 200 120 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"

You can also get logs from a specific timeframe using –since, for example logs from the last five minutes:

$ docker logs --since 5m heartbleed_nginx_1
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /mgmt/login?dest=%2Fmgmt%2Fgui%3Fp%3Dhome&reason=&username= HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /dms2/Login.jsp HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /login HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /home.htm HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /sws/data/sws_data.js HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /ptz.htm HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /admin/login.jsp HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /explorer.html HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:50 +0000] "GET /loginMsg.js HTTP/1.1" 302 161 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:53 +0000] "GET /login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"
3.124.XXX.XXX - - [09/Apr/2020:14:26:53 +0000] "GET /this_server/all_settings.shtml HTTP/1.1" 404 169 "-" "Mozilla/5.0 (X11; Intruder; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0" "-"

And you can use pipe to export the docker logs into a file so they can be analysed later:

$ docker logs --since 2020-04-09 heartbleed_nginx_1 > heartbleed_nginx_1.log

Running containers manually

To run an individual container using the Docker Compose utility, first ensure the Docker server software is running on the host machine. A simple way to check is by using docker status from the command line interface.

Once that’s confirmed, choose the container you want to use. For example, if you want to launch a container with the infamous OpenSSL “Heartbleed” vulnerability, you can start a web service by typing the following:

docker-compose -f "vulhub/openssl/heartbleed/docker-compose.yml" up -d

This will first download all the images required to build the container, start to build the actual image and run the container. The -f option is the filename to the Docker container, the up option starts the container, and the -d option tells Docker to run it in the background.

Avoiding port clashes

Inside every container folder exists a file called docker-compose.yml provided by Vulnhub. This is the main configuration file which allows users to change how a container behaves, such as modifying what ports are used and much more.

Example of a docker-compose.yml file:

version: '2'
services:
 nginx:
   build: .
   volumes:
    - ./www:/var/www/html
    - ./conf:/etc/nginx
   ports:
    - "80:80"
    - "443:443"

The above will expose ports 80 and 443 on the host and container side, which can be accessed from the host machine’s IP address. If you are planning to run multiple containers on one machine, be sure to check containers are not using the same port numbers on the host side (“HOST:CONTAINER”) or you may have issues. Depending on the container you can change the host port (left number) to whatever you want.

For our latest research, and for links and comments on other research, follow our Lab on Twitter.

Alternatively, get in touch if you’d like to chat to us

Naz Markuta
Cyber Security Researcher