Vulhub: The Docker-Compose Library That Makes CVE Reproduction Deterministic
Hook
Security researchers waste an estimated 40% of their time just trying to reproduce vulnerabilities. Vulhub eliminates this problem with a single command.
Context
Anyone who's tried to reproduce a CVE from a security advisory knows the pain: hunting down specific software versions, resolving undocumented dependencies, dealing with OS incompatibilities, and spending hours reconstructing an environment that may still not match the original vulnerability conditions. This reproducibility crisis plagues security research, penetration testing training, and detection rule development. Before containerization, the standard approach was pre-built virtual machines like Metasploitable—multi-gigabyte downloads containing dozens of vulnerabilities in a single monolithic system. While functional, VMs suffered from resource overhead, difficult updates, and the inability to isolate individual vulnerabilities for focused testing.
Vulhub emerged in 2017 as a response to this problem, applying infrastructure-as-code principles to vulnerability research. Rather than packaging everything into monolithic VMs, it treats each CVE as an independent Docker Compose configuration. The project's core insight: vulnerabilities are software states that can be declared as code. Instead of distributing pre-built images, Vulhub provides Dockerfiles and compose configurations that deterministically recreate vulnerable conditions. With over 20,000 GitHub stars and 200+ environments covering everything from Apache Struts to Redis to Node.js frameworks, it's become the de facto standard for reproducible security research environments.
Technical Insight
Vulhub's architecture is deliberately anti-framework. There's no CLI tool, no orchestration layer, no configuration management—just a flat directory structure where each vulnerability is a self-contained project. This design choice prioritizes simplicity and transparency over abstraction. When you clone the repository, you get folders named by software and CVE identifier, like struts2/s2-057/ or redis/CVE-2022-0543/. Each contains a docker-compose.yml, Dockerfiles, and a README explaining the vulnerability.
Here's what a typical environment looks like, using the Struts2 S2-057 remote code execution vulnerability:
version: '2'
services:
struts2:
image: vulhub/struts2:2.3.34
ports:
- "8080:8080"
That's it. Running docker compose up -d in this directory spawns an Apache Struts 2.3.34 instance vulnerable to S2-057. The corresponding Dockerfile in the image builds from a specific base, installs the exact vulnerable version, and configures it precisely as needed for exploitation:
FROM tomcat:8.5-jre8
MAINTAINER phithon <root@leavesongs.com>
RUN set -ex \
&& rm -rf /usr/local/tomcat/webapps/* \
&& chmod a+x /usr/local/tomcat/bin/*.sh
COPY struts2-showcase.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
The modularity extends to complex multi-container environments. For vulnerabilities requiring databases or supporting services, the compose files declare complete application stacks. The Redis Lua sandbox escape (CVE-2022-0543), for instance, needs a specific Debian package version of Redis compiled with Lua support—conditions that would take significant manual effort to recreate but are captured in 15 lines of Dockerfile.
What makes this architecture powerful is the separation of concerns. The vulnerable application layer is isolated from exploitation tooling. Vulhub doesn't include exploit code—it provides the target. This means researchers can use their own tools (Burp Suite, custom scripts, Metasploit modules) against standardized targets. The README for each vulnerability walks through manual exploitation steps, teaching the vulnerability's mechanics rather than just providing a button to push.
The trade-off for this simplicity is discoverability. With no central index beyond the directory tree, finding relevant environments requires knowing what you're looking for or browsing the GitHub repository structure. The project organizes vulnerabilities by software name, so nginx/, tomcat/, weblogic/ directories contain all CVEs for those platforms. Within each software directory, vulnerabilities are further subdivided by CVE identifier or version number.
A subtle architectural decision is the use of pre-built images on Docker Hub under the vulhub/ namespace. While each directory contains Dockerfiles (transparency), the compose files reference pre-built images (convenience). This hybrid approach means you can inspect exactly what's being built while getting the speed of pulling cached layers. For security-conscious users who don't trust external images, rebuilding locally is straightforward: modify the compose file to use build: . instead of image: vulhub/....
The lack of state management is both a feature and limitation. Each docker compose up creates a fresh environment; each docker compose down destroys it completely. There's no persistence, no configuration drift, perfect reproducibility. But it also means you can't easily save progress across sessions or compare behavior between vulnerability variants without external tooling.
Gotcha
The biggest limitation is platform compatibility—specifically Apple Silicon. Many Dockerfiles assume x86_64 architecture and don't build cleanly on ARM. While Docker Desktop for Mac includes Rosetta emulation (--platform linux/amd64), performance suffers and some images fail entirely. The project maintainers have started adding ARM support, but coverage is incomplete. If you're on an M1/M2/M3 Mac, expect to troubleshoot platform issues on roughly 20-30% of environments.
Network accessibility creates friction in certain regions. The project relies heavily on Docker Hub for pre-built images, and users in mainland China report consistent pull failures without VPN or mirror configuration. More subtly, some vulnerability exploits themselves require outbound internet access (for reverse shells or callback servers), which requires understanding Docker's bridge networking and potentially configuring custom networks.
There's also a documentation asymmetry. Popular CVEs like Spring4Shell or Log4Shell have detailed READMEs with step-by-step exploitation guides. Older or more niche vulnerabilities sometimes have sparse documentation, assuming you already understand the vulnerability from the original advisory. This isn't a flaw per se—Vulhub is a lab environment, not a tutorial—but beginners may struggle with less-documented entries. The project explicitly states it's for security research and education, meaning there's no support guarantee and environments may break as dependencies age or Docker behavior changes.
Verdict
Use Vulhub if you're building detection rules and need ground-truth vulnerable targets to test against, conducting security training where students need identical environments, researching CVE exploitability across multiple platforms, or learning offensive security techniques with real-world vulnerabilities rather than synthetic challenges. It's perfect for penetration testers maintaining a local lab, security engineers validating patches, or developers who want to understand how vulnerabilities in their tech stack actually work. The value proposition is reproducibility and breadth—you get 200+ real CVEs as code. Skip it if you need production vulnerability scanning (this is research infrastructure, not a scanner), require Windows-native environments (it's Linux containers only), work exclusively on ARM without tolerance for emulation overhead, or want guided, gamified security training with scoring and hints (try OWASP WebGoat instead). Also skip if you're looking for zero-day research environments—Vulhub documents known CVEs with public advisories, not novel vulnerability discovery.