Building a WordPress CVE Lab for Bug Bounty Practice

If you’re learning web app pentesting or working your way into bug bounty, WordPress is one of the richest hunting grounds you can practice on. Tens of thousands of plugins, a massive historical CVE database, and most vulnerabilities are still reachable against old plugin versions you can download and install yourself. The problem is getting a sensible lab environment that lets you drop in vulnerable plugin versions, reproduce known CVEs, and actually *understand* how each bug class works — rather than just running automated scanners against live targets (which you shouldn’t be doing anyway, whilst learning).

Automattic’s WPScan Vulnerability Test Bench (https://github.com/Automattic/wpscan-vulnerability-test-bench) is purpose-built for exactly this. It spins up a standardised WordPress multisite with pre-configured users at every privilege level so when a CVE write-up says “exploitable by any authenticated Subscriber,” you have a Subscriber account ready to log in with. When it says “requires Editor role,” you’ve got that too.

This post walks through the whole setup on Windows via WSL, then shows you how to use it as a practice lab for studying historical WordPress plugin CVEs.

Why this setup is good for learning

A few things matter when you’re building a skill in this space:

  • Reproducibility – When you work through a CVE write-up, you need the same WordPress version, same user roles, same multisite config the original researcher used. The test bench gives you that baseline.
  • Every privilege level available – Most plugin bugs are authentication- or authorisation-sensitive. You need to be able to test as a Subscriber, then as a Contributor, then as an Editor, to actually understand the auth boundary the bug crosses.
  • Disposable – Tearing down and rebuilding is a single command, so you can try dangerous payloads without worrying about polluting your environment.
  • Local only – Nothing you do here touches the public internet. You can exploit freely without legal or ethical concerns.

Prerequisites

A Windows 10 (build 19041+) or Windows 11 machine with virtualisation enabled in BIOS. That’s it.

Step 1: Install WSL and Ubuntu

Open **PowerShell as Administrator** — right-click the Start button and choose “Terminal (Admin)”.

Type:

wsl --install -d Ubuntu

This one command enables the WSL feature, enables the Virtual Machine Platform, installs WSL2, and installs the latest Ubuntu LTS. **Reboot when prompted.**

After reboot, Ubuntu launches automatically and asks you to create a UNIX username and password. This is separate from your Windows login pick something memorable because you’ll be typing it for `sudo` commands.

Confirm you’re on WSL2:

wsl -l -v

Ubuntu should show `VERSION 2`. If it shows 1, run `wsl –set-version Ubuntu 2`.

Step 2: Update Ubuntu

From here on, everything happens **inside the Ubuntu terminal**. Launch it from the Start menu.

sudo apt update && sudo apt upgrade -y

Step 3: Install Docker

This is where a lot of guides will send you down a rabbit hole. Ubuntu’s default `docker.io` package is convenient but ships without the buildx and compose plugins that DDEV requires. You’ll get a cryptic “compose build requires buildx 0.17.0 or later” error later and end up manually downloading binaries.

Skip that entirely by using Docker’s official repository, which bundles the plugins:

sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Let your user run Docker without `sudo`:

sudo usermod -aG docker $USER
sudo service docker start

 Step 4: Enable systemd

WSL doesn’t run systemd by default, which means Docker won’t come back after a reboot unless you start it manually each time. Fix that with a one-line config file:

sudo tee /etc/wsl.conf > /dev/null <<EOF
[boot]
systemd=true
EOF

Step 5: Restart WSL

This is the step people miss. The docker group change from Step 3 only takes effect in new shells, and systemd only activates after WSL fully restarts. Closing the terminal window isn’t enough you need to shut down the WSL VM itself.

From **PowerShell** (not Ubuntu):

wsl --shutdown

Wait about ten seconds, then reopen Ubuntu from the Start menu.

Verify Docker works without `sudo`:

sudo systemctl enable --now docker

Step 6: Install DDEV

DDEV’s install script does everything in one command — installs the `ddev` binary, installs `mkcert`, and registers a local certificate authority so HTTPS works on your test sites.

curl -fsSL https://ddev.com/install.sh | bash

Enter your sudo password when prompted.

Verify Install:

ddev --version

Step 7: Clone and launch the test bench

Clone into your Linux home directory, **not** `/mnt/c/…`. File I/O between WSL and the Windows filesystem is dramatically slower and will make `ddev start` crawl.

cd ~
git clone https://github.com/Automattic/wpscan-vulnerability-test-bench.git
cd wpscan-vulnerability-test-bench
ddev start

First start pulls Docker images and runs WordPress’s install routine. Takes a few minutes. When it finishes, DDEV prints the site URL — typically `https://wpscan-vulnerability-test-bench.ddev.site`.

Open the admin:

ddev launch wp-admin/

Step 8: The users you’ll be logging in as

The test bench creates six users, password `password` for all:

  • superadmin — network superadmin
  • simpleadmin — site admin
  • editor — Editor role
  • author — Author role
  • contributor — Contributor role
  • subscriber — Subscriber role

Keep this list handy. A huge proportion of the CVEs you’ll study hinge on privilege boundaries, and you’ll be switching between these accounts constantly.

Using the lab to study historical CVEs

1. Pick a CVE

Good places to find WordPress plugin vulnerabilities with enough detail to reproduce:

For your first few, pick bugs in popular, freely-downloadable plugins where the affected version is still available. Aim for variety across bug classes:

  • Broken access control / IDOR — common and great for learning auth boundaries
  • Stored / reflected XSS — learn how WordPress’s sanitisation helpers should be used (and how plugins skip them)
  • SQL injection — increasingly rare but still appears, especially around $wpdb->prepare() misuse
  • CSRF — plugins forgetting nonce checks on admin actions
  • Authenticated RCE — arbitrary file uploads, PHP deserialisation, arbitrary option updates leading to code execution
  • SSRF — plugins that fetch URLs without validation

2. Download the exact vulnerable version

WPScan reports tell you the affected version range, e.g. “vulnerable in versions < 2.4.1.” Download that specific version from the WordPress plugin SVN. For a plugin slugged example-plugin:

ddev wp plugin install https://downloads.wordpress.org/plugin/PLUGINNAME.VERSION.ZIP --activate

3. Install it in the test bench

You’ve got two options. Drop the plugin directory into wp-content/plugins/ and activate it in the admin UI, or if it’s still on wp.org (some vulnerable versions still are), let wp-cli install it:

ddev wp plugin install pathtoplugin.zip --activate

4. Configure the vulnerable state

Read the CVE carefully. Most plugin vulns require specific configuration settings enabled, shortcodes placed on a page, a post created with specific metadata. Set up whatever the write-up describes before you start testing. If there is no writeup then its up to you to set up the plugin in and go find it yourself, this helps build your hunting skills!

5. Reproduce the bug

Log in as the role the CVE specifies (or as unauthenticated if it’s pre-auth). Intercept the request in Burp Suite or ZAP, study what the plugin’s doing, trigger the bug, confirm the impact.

This is where the real learning happens not in running the PoC, but in reading the vulnerable plugin’s source code and seeing exactly what the author got wrong. Open the plugin files in VS Code (which integrates beautifully with WSL — code . from the project directory opens it) and trace from the HTTP endpoint to the vulnerable sink.

6. Study the patch

Find the fixed version of the plugin. Diff it against the vulnerable version:

diff -r plugin-2.4.0 plugin-2.4.1

The patch is where most of the learning lives. Seeing that the fix was “add current_user_can('edit_posts') check before the action” teaches you to look for missing capability checks in every plugin you audit afterwards.

7. Reset for the next one

ddev delete--omit-snapshots
ddev start

Fresh environment, no state bleed, ready for the next CVE.

Burp Suite on WSL

If you’re serious about this, you’ll want Burp running alongside. Install Burp Community (or Pro) in Windows itself, not in WSL — the Java GUI is miserable through WSLg. Point your browser at Burp’s proxy on 127.0.0.1:8080 and it’ll intercept requests to the DDEV site just as it would any other local target. Install Burp’s CA certificate in your browser to see HTTPS traffic.

Day-to-day DDEV commands

ddev stop                      # stop containers
ddev start                     # start again
ddev logs                      # aggregated server logs (useful for debugging PHP errors during exploitation)
ddev wp <command>              # run wp-cli inside the container
ddev ssh                       # shell into the web container
ddev describe                  # URLs, database info, all the details
ddev delete --omit-snapshots   # full teardown, files stay

ddev ssh is particularly useful when you’re exploiting a bug that writes a file — you can hop into the container and verify the file actually landed where you expected.

Troubleshooting

permission denied while trying to connect to the docker API at unix:///var/run/docker.sock

Your shell hasn’t picked up the docker group. Run wsl --shutdown from PowerShell, wait ten seconds, reopen Ubuntu.

compose build requires buildx 0.17.0 or later 

You installed docker.io instead of using Step 3’s official Docker repo. Easiest fix is to sudo apt remove docker.io and follow Step 3 properly.

Port conflicts on 80 or 443 Something on Windows is listening — often IIS or another local web server. Either stop that service or reassign DDEV’s ports with 

ddev config --router-http-port=8080 --router-https-port=8443.

Sluggish performance You cloned into /mnt/c/.... Move the project to your Linux home directory.

Browser shows certificate warning The mkcert -install step didn’t register the CA in Windows. Run mkcert -install again from Ubuntu, then restart your browser.

This is worth saying plainly. Everything in this post assumes you’re attacking your own local lab and publicly-known CVEs in old plugin versions you’ve deliberately installed on that lab. That’s legal, educational, and the right way to build skill.

What this setup is not for:

  • Scanning live WordPress sites without written permission, even if you think they’re vulnerable
  • “Testing” a friend’s site, or your employer’s site, without an authorised scope
  • Running exploits against plugins on sites you don’t own

If you want to find bugs on live targets, get onto a legitimate bug bounty platform — HackerOne, Bugcrowd, Intigriti — and work within their scopes. Patchstack and wordfence also runs a dedicated WordPress plugin bounty programme where you can submit novel findings in real plugins for real payouts, which is the natural progression once you’ve spent a few weeks in a local lab reproducing historical CVEs.

Where to go from here

Once you’re comfortable reproducing existing CVEs, start auditing plugins that haven’t been reported on. Pick a popular one, install it in the test bench, and read the source with a mental checklist: where does user input enter, does every admin action have a nonce check, does every sensitive function have a capability check, does every database query use $wpdb->prepare() with proper placeholders, are uploaded files validated. You’ll find bugs. Submit them responsibly the plugin author first, then through Patchstack or WPScan if they don’t respond.

The path from “reproducing known CVEs in a local lab” to “finding novel bugs that pay” is shorter than people think. The lab you’ve just built is where that journey starts.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *