Container Security

Cards (48)

  • Containers
    • Just because you have access (i.e. a foothold) to a container, it does not mean you have access to the host operating system and associated files or other containers.
    • Due to the minimal nature of containers (i.e. they only have the tools specified by the developer), you are unlikely to find fundamental tools such as Netcat, Wget or even Bash! This makes interacting within a container quite difficult for an attacker.
  • Docker Vulnerabilities
    • While Docker containers are designed to isolate applications from one another, they can still be vulnerable. For example, hard-coded passwords for an application can still be present.
    • If an attacker is able to gain access through a vulnerable web application, for example, they will be able to find these credentials. You can see an example of a web application containing hard-coded credentials to a database server in the code snippet below:
  • Container Vulnerabilities
    Misconfigured Containers
    • Misconfigured containers will have privileges that are not necessary for the operation of the container. For example, a container running in "privileged" mode will have access to the host operating system - removing the layers of isolation.
    Vulnerable Images
    • There have been numerous incidents of popular Docker images being backdoored to perform malicious actions such as crypto mining.
  • Container Vulnerabilities
    Network Connectivity
    • A container that is not correctly networked can be exposed to the internet. For example, a database container for a web application should only be accessible to the web application container - not the internet.
    • Additionally, containers can serve to become a method of lateral movement. Once an attacker has access to a container, they may be able to interact with other containers on the host that are not exposed to the network.
  • Containers
    • At its fundamental, Linux capabilities are root permissions given to processes or executables within the Linux kernel. These privileges allow for the granular assignment of privileges - rather than just assigning them all.
    These capabilities determine what permissions a Docker container has to the operating system. Docker containers can run in two modes:
    • User (Normal) mode
    • Privileged mode
    Privileged containers, however, do not do this. Instead, they bypass the Docker Engine and directly communicate with the operating system.
  • Privileged Containers
    • Well, if a container is running with privileged access to the operating system, we can effectively execute commands as root on the host.
    • We can use a utility such as capsh which comes with the libcap2-bin package to list the capabilities our container has: capsh --print . Capabilities are used in Linux to assign specific permissions to a process. Listing the capabilities of the container is a good way to determine the syscalls that can be made and potential mechanisms for exploitation.
  • Privileged Containers
    1. We need to create a group to use the Linux kernel to write and execute our exploit. The kernel uses "cgroups" to manage processes on the operating system. Since we can manage "cgroups" as root on the host, we'll mount this to "/tmp/cgrp" on the container.
    2. For our exploit to execute, we'll need to tell the kernel to run our code. By adding "1" to "/tmp/cgrp/x/notify_on_release", we're telling the kernel to execute something once the "cgroup" finishes. 
    3. We find out where the container's files are stored on the host and store it as a variable.
  • Privileged Containers
    • 4. We then echo the location of the container's files into our "/exploit" and then ultimately to the "release_agent" which is what will be executed by the "cgroup" once it is released.
    • 5. Let's turn our exploit into a shell on the host
    • 6. Execute a command to echo the host flag into a file named "flag.txt" in the container once "/exploit" is executed.
    • 7. Make our exploit executable!
    • 8. We create a process and store that into "/tmp/cgrp/x/cgroup.procs". When the processs is released, the contents will be executed.
  • Unix Sockets 101
    • Sockets are used to move data between two places. Unix sockets use the filesystem to transfer data rather than networking interfaces. This is known as Inter-process Communication (IPC) and is essential in operating systems because being able to send data between processes is extremely important.
    • Unix sockets are substantially quicker at transferring data than TCP/IP sockets (Percona., 2020). This is why database technologies such as Redis boast such outstanding performance. Unix sockets also use file system permissions
  • How Does Docker Use Sockets
    • When interacting with the Docker Engine (i.e. running commands such as docker run), this will be done using a socket (usually, this is done using a Unix socket unless you execute the commands to a remote Docker host).
    • Recall that Unix sockets use filesystem permissions. This is why you must be a member of the Docker group (or root!) to run Docker commands, as you will need the permissions to access the socket owned by Docker.
  • Finding the Docker Socket in a Container
    • Remember, containers interact with the host operating system using the Docker Engine (and, therefore, have access to the Docker socket!)
    • This socket (named docker.sock) will be mounted in the container. The location of this varies by the operating system the container is running, so you would want to find it. However, in this example, the container runs Ubuntu 18.04, meaning the docker.sock is located in /var/run. 
  • Exploiting the Docker Socket in a Container
    • First, let's confirm we can execute docker commands. You will either need to be root on the container or have the "docker" group permissions as a lower-privileged user. 
    • We will use Docker to create a new container and mount the host's filesystem into this new container. Then we are going to access the new container and look at the host's filesystem.
  • Exploiting the Docker Socket in a Container
    • To avoid detection, it is best to use an image that is already present in the system, otherwise, you will have to upload this yourself.
    • docker run -v /:/mnt --rm -it alpine chroot /mnt sh
    • We will use chroot to change the root directory of the container to be /mnt (where we are mounting the files from the host operating system): chroot /mnt. Now, we will tell the container to run sh to gain a shell and execute commands in the container: sh
  • The Docker Engine - TCP Sockets Edition
    • Recall how Docker uses sockets to communicate between the host operating system and containers in the previous task. Docker can also use TCP sockets to achieve this. 
    • Docker can be remotely administrated. For example, using management tools such as Portainer or Jenkins to deploy containers to test their code
  • RCE via Exposed Docker Daemon: The Vulnerability
    • The Docker Engine will listen on a port when configured to be run remotely. The Docker Engine is easy to make remotely accessible but difficult to do securely. The vulnerability here is Docker is remotely accessible and allows anyone to execute commands. First, we will need to enumerate
  • Enumerating: Finding Out if a Device Has Docker Remotely Accessible
    • By default, the engine will run on port 2375
    • Looks like it's open; we're going to use the curl command to start interacting with the exposed Docker daemon. Confirming that we can access the Docker daemon: curl http://10.10.83.44:2375/version
  • RCE via Exposed Docker Daemon: Executing Docker Commands on Our Target
    • For this, we'll need to tell our version of Docker to send the command to our target (not our own machine). We can add the "-H" switch to our target. To test if we can run commands, we'll list the containers on the target: docker -H tcp://10.10.83.44:2375 ps
  • RCE via Exposed Docker Daemon: What Now
    • Now that we've confirmed that we can execute docker commands on our target, we can do all sorts of things. For example, start containers, stop containers, delete them, or export the contents of the containers for us to analyse further. 
    • Example: network ls, images, exec, run
  • What Are Namespaces
    Namespaces segregate system resources such as processes, files, and memory away from other namespaces. Every process running on Linux will be assigned two things:
    • A namespace
    • A Process Identifier (PID)
    Namespaces are how containerisation is achieved! Processes can only "see" the process in the same namespace. Take Docker, for example, every new container will run as a new namespace, although the container may run multiple applications (processes).
  • Determining if We're in a Container (Processes)
    • Let's list the processes running in our Docker container using ps aux. It's important to note that we only have six processes running in this example. The difference in the number of processes is usually a great indicator that we're in a container.
    • Additionally, the first process in the snippet below has a PID of 1. This is the first process that is running. PID 1 (usually init) is the ancestor (parent) for all future processes that are started. We can see that only 5 processes are running. A good indicator that we're in a container
  • How Can We Abuse Namespaces
    • cgroups (control groups) in a previous vulnerability. We are going to be using these in another method of exploitation. This attack abuses conditions where the container will share the same namespace as the host operating system (and therefore, the container can communicate with the processes on the host).
    • You might see this in cases where the container relies on a process running or needs to "plug in" to the host such as the use of debugging tools. In these situations, you can expect to see the host's processes in the container when listing them via ps aux.
  • Namespaces: The Exploit
    • For this vulnerability, we will be using nsenter (namespace enter). This command allows us to execute or start processes, and place them within the same namespace as another process. 
    • In this case, we will be abusing the fact that the container can see the "/sbin/init" process on the host, meaning that we can launch new commands such as a bash shell on the host. 
  • Docker Daemon
    • Docker daemon is responsible for processing requests such as managing containers and pulling or uploading images to a Docker registry.
    • Docker can be managed remotely and is often done in CI (Continuous Integration) and CD (Continuous Development) pipelines. For example, pushing and running new code in a container on another host to check for errors.
  • Docker Daemon
    • If an attacker can interact with the Docker daemon, they can interact with the containers and images. For example, they launch their own (malicious) containers or gain access to containers running applications with sensitive information (such as databases).
    • The Docker daemon is not exposed to the network by default and must be manually configured. However, exposing the Docker daemon is a common practice (especially in cloud environments such as CI/CD pipelines).
  • Docker Daemon
    • Implementing secure communication and authentication methods such as those listed below are extremely important in preventing unauthorised access to the Docker daemon.
  • SSH
    • Developers can interact with other devices running Docker using SSH authentication. To do so, Docker uses contexts which can be thought of as profiles. These profiles allow developers to save and swap between configurations for other devices.
    • For example, a developer may have one context for a device with Docker for development and another context for a device with Docker for production.
    • You must have SSH access to the remote device, and your user account on the remote device must have permission to execute Docker commands.
  • SSH: Docker Contexts
    This is not entirely secure. For example, a weak SSH password can lead to an attacker being able to authenticate. Strong password hygiene is strongly recommended. Some tips for a strong password have been included below:
    • A high amount of characters (i.e. 12-22+)
    • Special characters such as !, @, #, $
    • Capital letters and numbers placed sporadically throughout (i.e. sUp3rseCreT!PaSSw0rd!)
    Docker contexts allow you to interact with the Docker daemon directly over SSH, which is a secure and encrypted way of communication.
  • TLS Encryption
    • The Docker daemon can also be interacted with using HTTP/S. This is useful if, for example, a web service or application is going to interact with Docker on a remote device.
    • To do this securely, we can take advantage of the cryptographic protocol TLS to encrypt the data sent between the devices. When configured in TLS mode, Docker will only accept remote commands from devices that have been signed against the device you wish to execute Docker commands remotely.
    • nce you have created your certificates, you can tell Docker to run in TLS mode with the generated certificate.
  • Control groups
    • Control Groups (also known as cgroups) are a feature of the Linux kernel that facilitates restricting and prioritising the number of system resources a process can utilise.
    • For example, a process such as an application can be restricted to only use a certain amount of RAM or processing power or given priority over other processes. This often improves system stability and allows administrators to track system resource use better.
  • cgroups
    • In the context of Docker, implementing cgroups helps achieve isolation and stability. Because cgroups can be used to determine the number of (or prioritise) resources a container uses, this helps prevent faulty or malicious containers from exhausting a system.
    • Of course, the best mechanism is preventing this from happening, but preventing a container from bringing down a whole system is an excellent second line of defence.
  • namespaces
    • Docker uses namespaces to create isolated environments. For example, namespaces are a way of performing different actions without affecting other processes.
    • Think of these as rooms in an office; each room serves its own individual purpose. What happens in a room in this office will not affect what happens in another office. These namespaces provide security by isolating processes from one another.
  • Privileged Containers
    •  Privileged containers are containers that have unchecked access to the host.
    • The entire point of containerisation is to "isolate" a container from the host. By running Docker containers in "privileged" mode, the normal security mechanisms to isolate a container from the host are bypassed.
    • While privileged containers can have legitimate uses, for example, running Docker-In-Docker (a container within a container) or for debugging purposes, they are extremely dangerous.
  • Capabilities
    • Capabilities are a security feature of Linux that determines what processes can and cannot do on a granular level.
    • Traditionally, processes can either have full root privileges or no privileges at all, which can be dangerous as we may not want to allow a process to have full root privileges as it means it will have unrestricted access to the system.
    • Capabilities allow us to fine-tune what privileges a process has. 
  • Capabilities: Privileges
    CAP_NET_BIND_SERVICE
    • This capability allows services to bind to ports, specifically those under 1024, which usually requires root privileges.
    • Allowing a web server to bind on port 80 without root access.
    CAP_SYS_ADMIN
    • This capability provides a variety of administrative privileges, including being able to mount/unmount file systems, changing network settings, performing system reboots, shutdowns, and more.
    • You may find this capability in a process that automates administrative tasks. For example, modifying a user or starting/stopping a service.
  • Capabilities: Privileges
    CAP_SYS_RESOURCE
    • This capability allows a process to modify the maximum limit of resources available. For example, a process can use more memory or bandwidth.
    • This capability can control the number of resources a process can consume on a granular level. This can be either increasing the amount of resources or reducing the amount of resources.
  • Capabilities: Privileges
    • It's recommended assigning capabilities to containers individually rather than running containers with the --privileged flag (which will assign all capabilities).
    • For example, you can assign the NET_BIND_SERVICE capability to a container running a web server on port 80 by including the --cap-add=NET_BIND_SERVICE when running the container.
  • Capabilities: Privileges
    • It is important to frequently review what capabilities are assigned to a container. When a container is privileged, it shares the same namespace as the host, meaning resources on the host can be accessed by the container - breaking the "isolated" environment.
  • Seccomp
    • Seccomp is an important security feature of Linux that restricts the actions a program can and cannot do. To explain, picture a security guard at the entrance of an office.
    • The security guard is responsible for making sure that only authorised people are allowed into the building and that they do what they are supposed to do. In this scenario, Seccomp is the security guard.
  • Seccomp
    • Seccomp allows you to create and enforce a list of rules of what actions (system calls) the application can make.
    • For example, allowing the application to make a system call to read a file but not allowing it to make a system call to open a new network connection (such as a reverse shell).
    • These profiles are helpful because they reduce attackers' ability to execute malicious commands whilst maintaining the application's functionality.
  • Seccomp Profiles
    This Seccomp profile:
    • Allows files to be read and written to
    • Allows a network socket to be created
    • But does not allow execution (for example, execve)
    To create a Seccomp profile, you can simply create a profile using your favourite text editor