Lead Image © Maxim Maksutov, 123RF.com

Lead Image © Maxim Maksutov, 123RF.com

Application security testing with ZAP in a Docker container

Dynamic Duo

Article from ADMIN 49/2019
Pitting the OWASP Zed Attack Proxy against an insecure web app in a Docker container illustrates how you can tick a lot of security checkboxes.

The Internet has seemingly endless security concerns. The treasures that lie behind some organizations' websites are not only valuable commercially but can genuinely influence the economic development of whole countries if trade or military secrets are stolen. As a result, the number of both automated and expertly targeted online attacks has risen significantly over the last decade or so.

Security professionals have upped their efforts, striving to help create new tools, processes, and procedures to mitigate ever-evolving attacks. As for many items that hold value, seemingly as soon as an innovative defense is created, it is invariably defeated, and its treasures are plundered.

As each new defense formerly thought to be robust is exposed to be otherwise, new attack vectors appear, and offensive security testing professionals adapt their attacks and test their own systems and websites against these vulnerabilities.

The OWASP Top 10 project [1], an "… open community dedicated to enabling organizations to conceive, develop, acquire, operate, and maintain applications that can be trusted," has "injection" as the number one issue in their latest Top 10 report.

In this article, I walk through automating Structured Query Language (SQL) injection attacks in a test laboratory. As with all self-respecting DevOps environments, it's much better to containerize applications for portability and predictability, so I use Docker containers, which also means you can be up and running in a matter of minutes.

SQL injection attacks, or SQLi, are a very popular way to break into websites through badly written applications. OWASP describes such attacks as resulting "… in data loss, corruption, or disclosure to unauthorized parties, loss of accountability, or denial of access. Injection can sometimes lead to complete host takeover." In cybersecurity parlance, the type of offensive testing I'll undertake in this article is called application security (AppSec) testing. (Please see the "Caution!" box.)


Before you begin, it should go without saying that you should never, under any circumstances, run these types of attacks on systems that you do not own or have permission to attack. The attack tool used here is, by default, highly sophisticated. Such tools should only be run in a closed laboratory environment. You have been warned, and with good reason: Orange jumpsuits are so last year.

To get started, you need an application that's full of security holes, ready to be compromised.

Feeling Vulnerable

As mentioned, modern admins typically don't muck about with virtual machines or build boxes but instead use Docker. After a quick look on Docker Hub for vulnerable applications that run on a Linux/Apache/MySQL/PHP (LAMP) stack, I found a build from OWASP themselves called "Mutillidae," named after a family of wasps [2]. Although I'm a fan of Mutillidae, other applications like WebGoat [3], also from OWASP, offer a highly sophisticated and multilayered insecure web application, as well.

The most suitable Docker image, nowasp [4], appears to be courtesy of user citizenstig on Docker Hub. As a small precaution, I've had a look at obvious issues listed in the image's Dockerfile (Listing 1), but that's hardly something I'd bet the ranch on from a security perspective. Build your own image if you're concerned with its provenance and integrity. The owner of the image tells you how to change the MySQL password, if necessary, on the Docker Hub Overview page [5].

Listing 1

The nowasp Dockerfile

01 FROM tutum/lamp:latest
02 MAINTAINER Nikolay Golub <nikolay.v.golub@gmail.com>
04 ENV DEBIAN_FRONTEND noninteractive
06 # Preparation
07 RUN rm -fr /app/* && apt-get update && apt-get install -yqq wget unzip php5-curl dnsutils && apt-get upgrade -yqq ca-certificates && update-ca-certificates && rm -rf /var/lib/apt/lists/*
09 # Deploy Mutillidae
10 RUN wget -O /mutillidae.zip https://sourceforge.net/projects/mutillidae/files/latest/download && unzip /mutillidae.zip && rm -rf /app/* && cp -r /mutillidae/* /app  && rm -rf /mutillidae  && sed -i 's/DirectoryIndex index.html.*/DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm/g' /etc/apache2/mods-enabled/dir.conf&& sed -i 's/static public \$mMySQLDatabaseUsername =.*/static public \$mMySQLDatabaseUsername = "admin";/g' /app/classes/MySQLHandler.php && echo "sed -i 's/static public \$mMySQLDatabasePassword =.*/static public \$mMySQLDatabasePassword = \\\"'\$PASS'\\\";/g' /app/classes/MySQLHandler.php" >> /create_mysql_admin_user.sh && echo 'session.save_path = "/tmp"' >> /etc/php5/apache2/php.ini
12 EXPOSE 80 3306
13 CMD ["/run.sh"]

To fire up MySQL with a random password as part of the LAMP stack, use the Docker command:

$ docker run -d -p 80:80 citizenstig/nowasp

If you haven't run a docker pull command for that image before now, the run command will, of course, pull it down from Docker Hub first.

For predictability, you can set the MySQL password as an environment variable within the container:

$ docker run -d -p 80:80 -p 3306:3306 -e MYSQL_PASS="Chang3ME!" citizenstig/nowasp

As you will note, you're opening up at least one low-level, privileged network port. Don't be surprised if you have to sudo or become root to run the command. Figure 1 shows that the container is up and running and which ports it's using. When you navigate to http://localhost , however (on TCP port 80 as per Figure 1), you are presented with the unwelcoming error web page shown in Figure 2.

Figure 1: A vulnerable web app called Mutillidae is running on TCP port 80 (HTTP) and TCP port 3306 (MySQL).
Figure 2: Mmmm, not great; the database is broken.

I have to admit when I saw this error, I immediately wanted to dive into the container to fix the issue. First, I grabbed the container hash from the docker ps command and inserted it into the command:

$ docker exec -it 8290c2f0147c /bin/bash

However, instead of looking for the location of the database config file and entering the password for DB_PASSWORD, I just clicked the setup/reset the DB link on the error web page, and two seconds later, after a pop-up dialog box that needed an OK button clicked (Figure 3), the vulnerable application was visible. Figure 4 shows the good news. If you do like rummaging inside containers, then Listing 2 shows you how to reset your MySQL password directly from the command line.

Listing 2

Resetting the Password

$ mysql -u root
$ use mysql;
$ update user set authentication_string=PASSWORD('') where user='root';
$ update user set plugin='mysql_native_password' where user='root';
$ flush privileges;
$ quit;
Figure 3: Don't rummage inside the container to fix the database error in Figure 2, just use the setup/reset link and click OK to reset the database.
Figure 4: Mutillidae is running on localhost from within the container's LAMP stack.

A Dog with Two Tails

Now that you're up and running, as Mutillidae suggests: "Keep Calm and Pwn On"! To start, click the Login/Register link in the menubar. The very clever Mutillidae is designed around training users about AppSec and offers hints and videos throughout. Note that you will not test against the startup page; instead, from the left sidebar, go to OWASP 2017 | A1 Injection (SQL) | SQLi Extract Data | User Info (SQL) . Later, I'll simply provide a URL to reach that page.

Nothing is unusual about the login page shown in Figure 5. These two HTML input boxes are visible on millions of websites all over the web.

Figure 5: A very familiar-looking login page asking for a login and passworrd.

Next, you'll need a tool to attack the vulnerable application. As you might have guessed, OWASP has a super-clever tool in its arsenal for just that task, which I wrote about in an earlier ADMIN article [6]. The venerable tool in question is called OWASP Zed Attack Proxy (ZAP) [7], and it acts, as the name clearly suggests, as a go-between proxy that allows you to speak to a remote or other machine and try different attacks.

Should I remind you again about the damage such attack tools can do? I'd even go so far as to say that because you're running a vulnerable machine locally or in a virtual test laboratory, you should disconnect from the Internet, so it's not attacked by someone else (which is easy because it's so vulnerable) and infects your other machines without your knowledge.


Having pointed you at my previous ZAP article, I'm going to shamelessly reuse some of the commands introduced there. First, you need to fire up a ZAP Docker container and then use an old-school tool called xtightvncviewer to create a GUI through which to access ZAP.

To install the TightVNC viewer package on a Debian derivative, enter:

$ apt install xtightvncviewer

To create a non-persistent Docker container that provides ZAP functionality and a few other useful bits and bobs, as you'll see in a moment, enter:

$ docker run -u zap -p 5900:5900 -p 81:8080 -i owasp/zap2docker-stable x11vnc --forever --usepw --create

The container will stop running if you hit Ctrl+C or close your terminal window.

Incidentally, if your local CPU or I/O is struggling with any Docker images in this article, you might cause less load on your system by running the

$ docker pull <imagename>

command first to download the image, without running it straight away. (It seems to help my machine a little.)

Once your container has fired up, you will be prompted to enter a simple password, so you can access the GUI via TightVNC. Just ignore the odd-looking stty: 'standard input': Inappropriate ioctl for device error. Again, if it's the first time you've run ZAP, you'll be waiting a little while for the image to appear locally after being downloaded from Docker Hub.

The default port is TCP 5900 for TightVNC, so you won't need to adjust the port, just connect to ZAP's internal IP address with the command:

$ xtightvncviewer

You can easily get the IP address on which ZAP is listening with the command that follows. Use a second terminal window to run ZAP with the command above and then another window to get the IP address below, if needed:

$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container-hash>

Simply replace the ZAP container hash (found with the command: docker ps) to see your running containers.

Connect now using the TightVNC command to ZAP's GUI. After some output flying up your ZAP terminal window, once you've put your password in, you should see the result (Figure 6).

Figure 6: OWASP ZAP running from a container over TightVNC.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs

Support Our Work

ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.