Randori Attack Team Confirms Exploitability Of CVE-2020-2021

“Side-Loading” Software in a Running Docker Container

Eric McIntyre

At Randori, one way our automated attack platform operates is by bridging docker containers into remote network environments. The actual operation of this system is outside the scope of this article, but in short, a series of network tunnels within network tunnels provides us with a container whose network traffic fully (and only) emerges into a remote network.

This system works well for automated attacks, where the complete attack tooling is pre-installed within the container. However, in some cases our Hacker Operations Center (HOC) operators want to manually assist the automated platform to further an attack. When this occurs it is often the case that the operators need to install additional tools in the container.

The simplest method (for Debian-based containers) of running ‘apt’ commands does work, but given the package installation traffic egresses onto the remote network this method increases the risks of IDS/IPS detecting the container. Some corporate networks have monitoring and alerting configured for, as an example, package downloads from Kali repos.

Luckily, there’s a relatively easy way to sideload packages into a running docker container without changing the networking state* of the container.

Docker containers rely on a feature in Linux called namespaces. Namespaces are methods of separating processes into different environments, such that certain system functions are compartmentalized. Two important namespaces relevant to our side-loading technique are the mount namespace and the network namespace. The mount, or filesystem, namespace determines which files and folders are visible to the process. For example, two processes in different mount namespaces might have different versions of the /bin directory even though both would access the directory using the same path. The network namespace determines the interfaces and routes among a few other network-related settings. Two processes in different network namespaces could see different interfaces: eth0 in the first process could be a different eth0 than that in the second process. When the docker daemon creates a new container it usually gets a unique network and mount namespace.

Docker isn’t the only program in Linux to help users utilize namespaces. Another widely available program is nsenter. With nsenter we can set the namespaces in which a program is to run. One feature of nsenter is the ability to specify a subset of namespaces (-m for mount) and to use an existing namespace from another process (-t for target pid). By setting only the mount namespace to be that of a target container, and leaving the network and other namespaces as default, when we run apt-get install the repositories configured in the container are used, and the packages are installed in the container, but all the network traffic emerges on the host, instead of the network configured in the container.

* Note: if you have resolv.conf configured in the container to resolve to the target network or use custom dns only available on the target network, you’ll most likely need to replace this temporarily, add hosts entries to prevent use of dns resolvers, or setup your repository sources to use IPs.

Here’s what it looks like in a “real-ish” example:

  1. Let’s start a container named target with no network access, and provide a basic resolv.conf:
    $ docker run -d --rm --network none --name target ubuntu sleep infinity
    $ docker exec -it target bash -c 'echo nameserver > /etc/resolv.conf'
  2. We can verify that we are unable to install from within the container:
    $ docker exec -it target apt-get update
    ... Temporary failure resolving 'security.ubuntu.com' ...
  3. However, if we run the command in the host namespaces, except using mount namespace of the container, we can install into the container:
    $ CONTAINER_NAME=target
    $ CONTAINER_PID=`docker inspect -f '{{.State.Pid}}' $CONTAINER_NAME`
    $ sudo nsenter -m -t $CONTAINER_PID apt-get update
    $ sudo nsenter -m -t $CONTAINER_PID apt-get install sl
  4. Finally we can verify that the package was installed and remove the container:
    $ docker exec -it target /usr/games/sl
    $ docker kill target

Simple, but effective!

Let us know what other container tips and tricks you use or would want to see more of on Twitter @RandoriAttack. We look forward to sharing more adversarial tips, techniques and POCs here as we continue to enhance the capabilities of our automated attack platform.

Eric McIntyre is the Director of R&D at Randori and a member of the @RandoriAttack Team. Previously, he served as a security researcher at Kyrus Tech, Inc., where he worked for 5 years leading teams of software engineers and other researchers to develop novel solutions to unique software challenges in the national and commercial security spaces. Before joining Kyrus, Eric operated a software development firm, EM Technology, and was an adjunct faculty member at the University of Colorado-Boulder. At CU-Boulder, he was a lecturer for undergraduate electrical engineers and, as a postgraduate researcher, conducted NASA-sponsored research primarily in the field of sub-orbital passive microwave radiometry. 

You can follow him on twitter at @pwnpnw