I’ve created a dockerfile that starts a
kalilinux/kali-rolling image with KDE,
noVNC preinstalled. Which can further be seasoned to your individual taste. For a fast and quick start see the
pullkali.sh script and modify the
mods variable to change the image. For the customizable dockerfile and a simplified docker-build script see the github repository or if you want to get started immediately without changes see the setup steps at the dockerhub repository.
Kali is a great way to separate your daily driver from your pentesting tools. With a full desktop being a necessity for many applications, virtual machines or dual booting have been the leading choices for many testers in the field. However, these approaches always felt clunky to me.
Dual booting locks you from the tools of your daily driver. Having to restart your PC in order to start writing your report is a time consuming process that should not be necessary anymore in 2020.
Virtual machines insert a thick layer of virtualization between your host and Kali. While getting a lot closer and more flexible than dual booting, this still offers cumbersome issues that are annoying to deal with. From NAT port forwarding, gigantic VM storage files and cumbersome shared folders in order to share files between host and vm, the virtualization solution has left me with a desire for a better alternative.
Thus, I have set out my sights for a solution that allows for both separation and seamless integration at once. This is where Docker came in.
Sure, Docker was never meant to run persistent operating systems. But the more I thought about it, Kali isn’t supposed to be that. To me, Kali is just a chain of tools that are great for pentesting. Ready to be thrown away and rebuilt as needed. All you need to store is your projects and files that you are working on.
Plus, I could use it as an excuse to learn docker how Dockerfiles work!
Breaking it down
In this section we’ll break down how various sections work, we’ll also describe how you can modify the
pullkali.sh script and dockerfile to match your own flavor of security testing.
It is always good to know what you’re running on your machines and why! As such, we’ll take a quick look at the lines inside the Dockerfile at the current time of writing and explain the reasoning behind them. We’ll skip over the basic parts to keep this short.
RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ echo 'Installing desktop files, this may take a few minutes...' && \ apt-get install -y \ kali-defaults \ desktop-base \ kali-desktop-kde \ kde-plasma-desktop \ --show-progress
Here we install all the requirements to run a desktop environment in Kali. These have been separated into a new layer to ensure that later layers can be changed and rebuilt without having to rebuild this layer.
RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ echo 'Installing base files, this may take a few minutes...' && \ apt-get install -y \ curl \ sudo \ net-tools \ [...] --show-progress && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ mkdir ~/.vnc && \ x11vnc -storepasswd admin ~/.vnc/passwd
Here we install a number of standard packages common on linux systems and required to run the VNC and SSH servers. Finally, we also tell
x11vnc to store a password in
~/.vnc/passwd. Despite the fact that this password is not used, it still prevents annoying messages from
x11vnc that would otherwise clog up the logs.
RUN cd /root && git clone https://github.com/kanaka/noVNC.git && \ cd noVNC/utils && git clone https://github.com/kanaka/websockify websockify && \ ln -s /root/noVNC/vnc_auto.html /root/noVNC/index.html
Here we clone the repositories required to run noVNC.
RUN echo "Port 22000\nPermitRootLogin yes" >> /etc/ssh/sshd_config && \ echo "root:toor" | chpasswd && \ service ssh restart
Here we enable ssh on the server and set the following appropriate settings:
Port 22000: As the name states, we want to run ssh on port
22000to prevent any potential clashes with an ssh server running on the host.
PermitRootLogin yes: We want to connect as root, so this needs to be set to
We also set the password for root using the
RUN echo 'Installing additional packages...' && \ export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install \ kali-tools-top10 \ -y --show-progress && \ mkdir /usr/share/wordlists && \ ln -s /usr/share/wordlists /root/wordlists
As we’ve reached the last step of installations, here we can insert any personal modifications we want to apply.
RUN echo "#!/bin/bash \n\ [ -e /tmp/.X1-lock ] && rm -rf /tmp/.X1-lock \n\ export DISPLAY=:1 \n\ Xvfb :1 -screen 0 1920x1080x16 & \n\ sleep 5 \n\ plasmashell &>/dev/null & \n\ kwin --replace &>/dev/null & \n\ sleep 5 \n\ service ssh start \n\ chmod +x /root/post.sh \n\ /root/post.sh &>/dev/null & \n\ x11vnc -display :1 -nopw -listen localhost -xkb -forever & # -ncache 10 -ncache_cr can be added for performance. Might give issues with some vnc viewers \n\ cd /root/noVNC && ./utils/launch.sh --vnc localhost:5900 \n" >/root/startup.sh
Now we’ll drop the
startup.sh script into place. This script will run (and keep running) every time the docker container is started. In rough order, it does the following:
- Remove the
Xvfblockfile if it exists. (Workaround required to make the
Xvfbto start a virtual X display server
- Startup plasma on the newly created interface
- Execute the
RUN echo "#!/bin/bash \n\ \#java -jar \`which burpsuite\` & &>/root/log & \n" >/root/post.sh
Write out a template file for the
All of the steps required to build your local kalidesktop dockerfile are prepared in the
buildkali.sh script. Here we’re stepping through a few of the steps in there and explain the reason why we do them.
echo 'Creating volume...' sudo docker volume create kalivol
First off, we create a volume for the docker container. This is required as certain applications (cough cough Burpsuite professional cough) don’t like being installed headless and/or being activated repeatedly. As such, we create this volume for weird one off applications that cannot be reinstalled easily. Furthermore, this volume can also be used to store configuration files of IE nikto.
echo 'Building image, this might take a while...' sudo docker build -t kalidesktop kalidesktopdocker/
echo states, we’re building the image here. Depending on your internet speeds, this step may take a while but should only be required once per device. Assuming caching is enabled, you can make more personalization changes without having to rebuild the whole image from scratch (more on personalizations later).
echo 'Finished building! Running kali and waiting...' sudo docker run -d --network host --privileged -v $HOME:/home/$USER --mount source=kalivol,target=/opt/vol/ kalidesktop sleep 30
Here we’re running the container with the following flags:
-d: We’re starting detached. The container should run in the background.
--network host: We’re giving the container full access to the machines network interface. This makes interacting with the machine much easier.
--privileged: Here we’re giving the docker container additional rights on the host machine.
-v $HOME:/home/$USER: Here we mount the users' home folder. This way, any file in your host home folder becomes available in the
/home/$USERfolder in the container. Be sure to read the warning.
--mount source=kalivol,target=/opt/vol/: Here we mount the
kalivolvolume we’ve created earlier to
/opt/vol. As can be read above, this is for storing and installing special cases.
Finally we’re adding a 30 seconds sleep to allow the container to start itself and its services before we attempt to connect to it.
echo 'How do you want to connect?' select CHOICE in vnc novnc ssh none ; do case $CHOICE in vnc) echo 'Opening your default vnc viewer' xdg-open vnc://localhost:5900 || echo 'Opening vnc viewer failed. Please install a vnc viewer and connect to localhost:5900' & ;; novnc) echo 'Opening your default browser...' xdg-open http://127.0.0.1:6080/vnc.html & ;; ssh) echo 'Open a root shell with the following command, password "toor"' echo 'ssh root@localhost -p 22000 -X' ;; none) ;; *) esac break # break avoids endless loop done echo 'All done! Happy hacking!'
Finally, with a little bash-fu, we offer the user a few choices to automatically connect to the container.
On average, it takes roughly 20 minutes to build the dockerfile with wired internet connectivity.
real 19m58,532s user 0m0,752s sys 0m0,468s
A faster way to build is by using dockerhub for prebuilt images. The
pullkali.sh script leverages this while still offering the user the ability to make personal modifications to the downloaded images. Doing this effectively quarters the time neccesary to start a customized kali docker experience.
Let’s step through the file and describe what we’re doing with each step:
mods='export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install nmap'
This variable is set to allow the user to modify the downloaded image with. Any personal changes can be applied here.
echo 'Pulling the latest image from dockerhub, this might take a few minutes...' sudo docker pull newlinedotblog/kalidesktop
Here the latest docker image is being downloaded from dockerhub. Since this download (at the current time of writing) comes down to a total of
2.36 GB of data, this might take a few minutes. For the latest information of the size of the download and an indepth breakdown see the dockerhub tags page.
echo 'Creating volume' sudo docker volume create kalivol
As discussed in the buildkali section, this is required for certain oddball applications and configuration files.
echo 'Tagging the base image' sudo docker image tag newlinedotblog/kalidesktop kalidesktop-base
Here we give the downloaded image a unique tag. This is done to ensure that we can rename the final image back to simply
kalidesktop and ensure it doesn’t clash with the initially downloaded image from dockerhub.
echo 'Starting the base container...' contname=`sudo docker run -d --network host --privileged -v $HOME:/home/$USER --mount source=kalivol,target=/opt/vol/ kalidesktop-base` sleep 3 echo 'Running personal modifications' sudo docker exec -it $contname /bin/bash -c "$mods" echo 'Committing changes' sudo docker commit $contname kalidesktop echo 'Stopping base container' sudo docker stop $contname sleep 5 unset mods unset contname
These four steps together allow us to make changes to the prebuilt image from dockerhub and apply our own modifications to them and then store them as a new image. This way, we only have to make the changes once and we can keep using that image until we want to update the entire image agian.
As stated in the comment we have to start by running the base image as we would normally. The
$contname variable contains the hash of the running container. Then, while it’s running, we use the
docker exec command to run commands inside of the running container. These are the modifications from the
$mods variable declared in the first lines. We run this sequentially (instead of detached) to ensure that all the modifications are completed before we move on to the next step.
Then, we leverage the functionality of
docker commit. This allows us to save the current container, with applied changes, to a new image. We name the new and final image
kalidesktop for future use.
Finally, since we’re done with the base image, we tell it to stop. We also unset our variables here since we no longer need them.
The sleeps injected in between are for safety reasons. The sleep between stopping the base image and the next line below is to ensure the ports have been freed before reusing them when starting the modified personal container.
echo 'Starting personalized container' sudo docker run -d --network host --privileged -v $HOME:/home/$USER --mount source=kalivol,target=/opt/vol/ kalidesktop sleep 5
Finally, we’re running the personalized image as a new container in detached mode. For a full breakdown of the flags used here see the same part in the buildkali section. The next section simply offers the user ways to connect to the container, this is also already covered in the buildkali section.
Average build times for this set up come down to:
real 4m46,247s user 0m1,268s sys 0m0,902s
A bland docker image of Kali that keeps resetting each time you update is of no use to anyone. So built in to the two different ways of using this docker image are ways to make them yours and fit your personal style of using Kali.
These changes are changes that need to be run only once and shouldn’t be re-executed each time the container starts. Examples of these are making changes in configurations, installing additional packages and writing out files.
There is a section labeled
# Add any personal modifications to the docker here available in the Dockerfile. Any changes you make to this layer will ensure that rebuilding the image afterwards won’t take too much time as long as caching is enabled.
As described in the
pullkali section, using the
docker commit function, we can run create a new image from the base image that was just downloaded from dockerhub. Using the
$mods variable in the
pullkali.sh command, we can apply changes in an automated way.
‘Run at start’ modifications
Some commands you might want to start every time the container is started. For this reason,
/root/post.sh exists. This script is being called from the
startup.sh script every time the container is started. This script is a good place to put actions such as starting services and programs you know you’ll want to use each time you start up the container.
In the last lines of the Dockerfile, a placeholder
post.sh script is being inserted. Inside of it you can find an example (commented out) command running Burpsuite. You can change the contents of the file here. Be sure to end each non-last line with
\n\ to ensure the file is being written out correctly.
Using the same
$mods variable, commands can also be inserted into the
post.sh script. For example, the following mods command will run
nmap -V on each start
echo "nmap -V" >> /root/post.sh.
Use this container with care! As you can see above this machine (if you do not modify the running parameters) has full access to your home folder and your network devices. Any rogue executed
sudo rm -rf --no-preserve-root / will also affect your hosts home folder. If this risk is too great to tempt, I recommend changing the
docker run parameters to suit your liking.
In this project I’ve created a dockerfile project that allows for easy customization with a robust base to work from. With this new dockerfile, a user can get started with his own customized version of the
kalilinux/kali-rolling docker image within 5 minutes.
For the creation of this project I’ve let myself be inspired by a number of projects. Have a look at them and follow their stuff too if you like them: