The Problem: Modern organisations are often compromised not through "zero-day" magic, but through unpatched legacy software, weak passwords, and misconfigured permissions that allow attackers to move from a basic user to the system administrator.
The Project: Build a lab simulation of a potential "worst-case scenario" server. It contains a decade-old software bug (Shellshock), a guessable password, and a "backdoor" in the administrative settings.
Part 1 - Building a Vulnerable Docker Container System to Demonstrate Common Mistakes Made During IT Administration.
Part 2 - Full Penetration Test of the Vulnerable System from Initial Access to Privilege Escalation.
Part 3 - Using Wazuh SIEM to Monitor for and Alert on Ongoing Cyber Attacks.
Patching is Non-negotiable: Even a single unpatched service (like the old Bash version in this lab) can grant an attacker total control.
Visibility is Victory: Tools like Wazuh SIEM grant system administrators and security teams visibility into real-time attacks, such as brute force credential guessing.
Principle of Least Privilege: If a user does not need "sudo" or administrative rights, do not provide them. This project demonstrates how a simple "viewer" tool (or a temporary file) can be weaponised if misconfigured.
Weak (or Default) Credentials: Users will tend to use easy-to-remember passwords or will not change the default passwords for the systems or devices they purchase, install, and use. Weak credentials can be brute-forced fairly easily and quickly.
To simulate a 'production' environment, two separate virtual machines and two Docker engine instances will be running as follows:
Host OS: Docker will be used to run a single-node instance of Wazuh (a container for each Server, Indexer, & Dashboard).
Ubuntu VM: Will host the Wazuh Agent that will monitor the Ubuntu VM and a separate Docker instance running the 'vulnerable server' (OpenSSH & Apache container). The Wazuh agent will communicate and share telemetry with the Wazuh Server running on the main host OS.
KALI VM: Will simulate the cyber attacks directed at the 'vulnerable server/container'.
To practice and understand both sides of security, we need a target that is authentically "broken". This lab uses custom-built Docker Images and Containers to create a multi-vector vulnerable container. I chose Docker for this lab for the following reasons:
Simple and easy to build, ship, and run a custom 'isolated' environment with minimal resources, with the ability to mimic an actual web server running Apache and SSH.
Ability to quickly set up, test, destroy, correct and rebuild lightweight isolated containers as compared to Virtual Machines and Snapshots.
Easy to share the lab with anyone who would like to run the same simulation on their respective systems.
During the build and testing phase of this lab, I opted to add a 'Capture-The-Flag' (CTF) component to the Docker container. Please note this is not usually the cause in real-world applications or systems, but can help illustrate how cyber threat actors can access one set of information with the initial access, but then can access all information after privilege escalation.
The container is built on Ubuntu 24.04, while the OS is modern, I deliberately downgraded critical components:
Legacy Bash: During image build, Bash 4.0 is manually compiled for this lab. Any containers created from this image will be vulnerable to Shellshock (CVE-2014–6271). This specific vulnerability enables attackers to execute code remotely (Remote Code Execution - RCE) on Apache web servers, especially those running CGI Scripts.
Custom OpenSSH: Instead of the standard package, the build includes OpenSSH 9.4p1 from source to control the environment; not necessary, but included.
It is common for IT system administrators to set up a system or server with the latest software, but either due to time constraints or other reasons, they fail to keep the operating system and install applications up to date. This can be a huge risk, especially when new vulnerabilities are discovered and their respective exploits are published on the internet. "A chain is as strong as the weakest link" - the entire IT infrastructure's overall strength is limited by its least capable or most vulnerable system or configuration.
For more information on Shellshock, check out the resources & references section at the end of this page.
I intentionally left 'bread-crumbs' for an attacker to gain initial access to the web server and escalate their privileges:
Weak Credentials: The image includes a single user with weak credentials that can be quickly brute-forced using common hacking tools like nmap, Metasploit, Hydra, and even custom scripts. It is common for systems (and hardware) to include default and, worse yet, hardcoded weak credentials; in this case, the Dockerfile.
SSH Insecurity: The Dockerfile configuration explicitly permits root login and password authentication. While not exploited in this project/lab, feel free to add a weak credential to the root account within the Dockerfile to add another vulnerability and thus attack vector.
Sudoers Abuse: (Abuse Elevation Control Mechanism: Sudo and Sudo Caching)
I added a configuration to /etc/sudoers.d/99-test that allows the test user to run /usr/bin/cat without a password. This is a classic "Living off the Land" technique where a binary with a shell-escape (like cat) is used to gain root access.
I also added /etc/sudoers.d/98-www-data that allows the Apache www-data user to execute the /tmp/test.sh script via sudo to elevate privileges to the root level.
CGI Entry Point: Apache is configured to run scripts via cgid. We provide a script at /var/www/html/cgi-bin/date.sh, which uses our vulnerable Bash version to process requests.
I included three unique CTF flags generated using openssl rand and hidden in /root, /home/test, and /home/ubuntu. Each time you build and run the Docker container, a new, unique CTF value is generated (unlike the weak credentials).
Building and running the vulnerable Docker Container
You can follow the steps and commands outlined in my earlier project, Docker - Uptime Monitoring with Uptime Kuma, to install and configure the required permissions to use the Docker utilities.
NOTE: For this lab, DO NOT install the Docker Desktop client, to avoid any issues with Wazuh SIEM that will be deployed later.
Create 2 files, compose.yml and Dockerfile, within the same directory, and copy-paste each of the respective commands/instructions into the respective files from the sections below.
Navigate to that directory from your respective CLI/Terminal environment and execute: docker compose up --build -d; this will simultaneously build and run the Image and container.
You can confirm the container is running by executing the command: docker ps -a; take note of the container ID.
You can confirm the Apache web server is up and running by accessing the container IP address and port 8080.
Using the container ID, log into the vulnerable container and confirm that the Shellshock vulnerability is active. This can be done by executing the following at the terminal:
env x='() { :;}; echo Vulnerable to Shellshock' bash -c 'Not vulnerable to Shellshock'
Confirming the Apache web server is up and running.
Shellshock vulnerability confirmed.
While the Dockerfile defines the "what" (the vulnerable system), the compose.yml file defines the "how", specifically, how the container interacts with the network and how it persists on your host machine.
Build Context: build: '.' tells Docker to look for the Dockerfile in the current directory to create the image.
Container Naming: container_name: ssh-apache-bash-lab gives the container a specific name, making it easier to manage via the CLI (e.g., docker logs ssh-apache-bash-lab).
Port Mapping: It is the bridge between the physical host machine and the isolated virtual network inside Docker. It follows the format Host_Port:Container_Port
SSH Access (2223:22): SSH daemon inside the container listens on the standard Port 22, which is then mapped to Port 2223 on the host machine (avoiding any conflicts should the host machine already be running SSH on port 22).
Web Access (8080:8080): Inside the container, Apache is reconfigured to listen on Port 8080, and since my host is not running a web server (or other service) on port 8080, this is 1:1 mapping.
Reliability (restart: unless-stopped): This policy ensures the lab environment is resilient. If the container crashes due to a heavy brute-force attack or a "messy" exploit, Docker will automatically attempt to restart it. It will only stay down if you explicitly run docker compose stop.
The Dockerfile is the blueprint for the vulnerable container/web server. It starts with a modern foundation and systematically introduces legacy software and configuration flaws. Below is a brief explanation of each section and the final Dockerfile can be viewed further below.
Base Image: The container uses ubuntu:24.04 as the starting point. The Shellshock vulnerability only affects Linux/Unix operating systems; feel free to modify the base image and use another Debian-based OS.
Non-interactive Frontend: ENV DEBIAN_FRONTEND=noninteractive ensures the build process doesn't pause for user input during package installations.
For easier modification, I used ARG (arguments) to specify key variables used throughout the Dockerfile. These values can be changed to meet your requirements.
Legacy Bash: We target BASH_VERSION=4.0, which is famously susceptible to the Shellshock vulnerability.
OpenSSH: We specify version 9.4p1.
Weak Credentials: A default password, password12345, is set as a variable for the test user.
This section installs the base tools, commands, and utilities needed for the web server and for configuration.
Standard Utilities: The script installs essentials like sudo, curl, wget, and text editors - nano and vim (remove the editor you do not want to use).
Compilers & Libraries: Packages like build-essential, libssl-dev, and zlib1g-dev are included so we can manually build Bash and OpenSSH.
Web Server: Apache2 is installed for the web server and to serve as the gateway for the Shellshock attack
This is a critical section that converts an otherwise secure system shell into a vulnerable one. No system administrator intentionally sets up a vulnerable system, but the assumption here is that the Dockerfile was created when the Shellshock vulnerability had not been discovered.
Source Download: The script pulls the source code for Bash 4.0 directly from the GNU FTP servers.
Manual Compilation: It configures, builds, and installs this specific version to /usr/local/bin.
The Swap: Crucially, it creates a symbolic link (ln -sf) so that /bin/bash points to our vulnerable 4.0 version instead of the secure version provided by Ubuntu 24.04.
Clean Up: Finally, the downloaded and extracted Bash source code directory is deleted.
Similar to Bash, I manually built the SSH server to ensure it is configured exactly for our lab needs.
Configuration: The build is configured with specific paths for privilege separation (/var/lib/sshd) and default system paths.
Installation: After compiling, the binaries are moved to /usr/bin and /usr/sbin.
This section creates the "ladder" that an attacker uses to climb from a low-level user to the root administrator.
Weak User: It creates a user named test and assigns the weak password defined earlier in the second section of the Dockerfile.
The 'cat' Backdoor: A sudoers file (99-test) is created, allowing the test user to run the cat command as root without a password. In this lab, cat allows users to view sensitive Linux files (/etc/passwd and /etc/shadow).
Writable Scripts: It creates /tmp/test.sh and grants www-data (the web user) and test sudo permissions to edit and run it, providing another vector for privilege escalation.
To ensure the brute-force attack works, I relaxed standard security configurations.
Authentication: The configuration is modified to explicitly allow PasswordAuthentication.
Root Access: PermitRootLogin is set to yes, allowing an attacker to attempt to crack the root account directly if a weak password were to be set.
To make Shellshock exploitable via a web browser, Apache must be configured to execute scripts.
Port Change: Apache is moved to listen on port 8080.
CGI Activation: The cgid module is enabled, and a ScriptAlias is created for the /cgi-bin/ directory pointing it to /var/www/html/.
Vulnerable Script: A script named date.sh is created. Because this script begins with #!/bin/bash (which I linked to the vulnerable Bash version), any web request to it can trigger the Shellshock bug.
Finally, the container generates random "trophies" for the security practitioner to find.
Flag Placement: Randomly generated strings are placed in /root/root_flag.txt, /home/test/test_flag.txt, and /home/ubuntu/ubuntu_flag.txt
The container exposes ports 22 and 8080 and starts both the Apache web server and the SSH daemon simultaneously.
Docker Tutorial for Beginners [FULL COURSE in 3 Hours]: https://www.youtube.com/watch?v=3c-iBn73dDE
Shellshock — A deep dive into CVE-2014–6271: https://infosecwriteups.com/shellshock-a-deep-dive-into-cve-2014-6271-3eb5b33e5de6
Inside Shellshock: How hackers are using it to exploit systems: https://blog.cloudflare.com/inside-shellshock/
Photo by Ian Taylor on Unsplash