WPProbe: A Pragmatic Approach to Detecting WordPress Plugins
- Introduction
- Why WPProbe?
- A Local Way to Build the Database
- Technical Background
- How WPProbe Works (and What It Can’t Do)
- Vulnerability Info (Wordfence Matching)
- References & Ideas
- Final Words
Introduction
WPProbe is a tool I built to solve a simple problem: detecting installed WordPress plugins in a reliable and quiet way. In this post, I explain how the tool came to life, its strengths, its limits, and why this REST-based idea is still not widely used, even though it works well.
Why WPProbe?
Honestly, I still don’t get why WordPress shows all REST endpoints by default when debug is on. Even if some bug bounty programs reward people for finding plugin vulnerabilities, there seems to be very little awareness of the attack surface exposed by this REST feature, which is on by default.
I’m not saying I’m a security expert, there are clearly people way more skilled than me, but I think this default setting is risky. Especially when you know that WordPress runs around 47% of all websites (based on recent numbers). That kind of popularity should come with stronger default protections.
And one quick reminder: if you get an idea, don’t throw it away too fast. WPProbe is proof of that. I had the idea almost one year before I built it, but I forgot about it. Then one day I remembered and just built it, fast and simple. Sometimes, old ideas are worth finishing.
Let’s be real: WPProbe isn’t magic. I was just bored, like often, and decided to work on something random. This time, it turned out to be useful. People liked it, maybe because it’s simple and solves a real problem.
I had no clue if it would even work. I try lots of things that don’t lead anywhere. But even failed projects teach me something. This time, it worked.
While working on WordPress modules for Metasploit, I saw something strange: a default WordPress install (like the official Docker image) shows all REST endpoints at ?rest_route=/
. That’s on by default and lets anyone see every route from WordPress core and plugins.
This is great for passive fingerprinting, but very few tools use it. At the time, I wanted to help LeakIX scan WordPress sites without overloading servers. Tools like wpfinger
work, but they’re too aggressive, sending too many requests. I wanted something calmer but still efficient.
Even WPScan, as far as I know, doesn’t use this method directly (I didn’t test their API). But it’s strange, because many recent critical CVEs can be used through REST endpoints.
A Local Way to Build the Database
To build WPProbe’s database, I downloaded the list of over 100,000 plugins from https://plugins.svn.wordpress.org/ and installed them one by one in a local WordPress Docker container using WP-CLI with the plugin name. This allowed me to avoid manually downloading each plugin from SVN, which would have been tedious. While some plugins may be disabled or unavailable, this method was much faster and more practical for fetching the necessary data.
However, it’s important to note that this isn’t a traditional database. Instead, I store the plugin data in a JSON Lines format (JSONL), where each line represents the information for a plugin, including its REST endpoints. This allows for efficient storage and easy parsing without the overhead of a relational database.
Here’s how I did it:
-
Downloading the plugin list:
I first retrieved the list of available plugins from the official WordPress SVN repository (https://plugins.svn.wordpress.org/). This list gave me access to the plugin names and allowed me to fetch the necessary plugins. -
Installing plugins using WP-CLI:
Instead of downloading plugins manually, I used WP-CLI to install each plugin by name directly into a local WordPress Docker container. This approach allowed me to quickly install each plugin without manually handling the files. -
Fetching the data:
After each plugin was installed, I ran a PHP script inside the Docker container to fetch the REST endpoints of the plugin. This script helped me retrieve the available routes without making extra HTTP requests to external servers. Using the local environment made the process faster and more efficient while avoiding unnecessary traffic. -
Deleting the plugin after fetching data:
To prevent unnecessary accumulation of data, I immediately deleted the plugin after retrieving the required information. This kept the environment clean and allowed me to proceed with testing the next plugin without any overhead.
PHP Script for Fetching REST Endpoints
The PHP script runs inside the local container and fetches the available REST endpoints for each plugin. Here’s the full code:
<?php
define('WP_USE_THEMES', false);
require_once 'wp-load.php'; // Include WordPress core
global $wp_rest_server;
$wp_rest_server = new WP_REST_Server();
do_action('rest_api_init'); // Initialize all registered REST routes
// Blacklist common routes that aren't useful for this scan
$blacklisted_routes = ["/", "/wp/v2", "/wp/v2/posts" /* etc */];
// Get all registered routes
$routes = $wp_rest_server->get_routes();
$filtered_routes = [];
// Filter out blacklisted routes
foreach ($routes as $route => $details) {
if (!in_array($route, $blacklisted_routes)) {
$filtered_routes[] = addslashes($route); // Add safe versions of routes
}
}
// Output the filtered routes as a JSON response
header("Content-Type: application/json");
echo json_encode(array_values($filtered_routes), JSON_PRETTY_PRINT);
exit;
?>
Technical Background
I’m not a Go developer. I have little experience with the language. So yes, the WPProbe code is probably not perfect, but it works pretty well. The main goal was to make something that just works.
At first, WPProbe was a quick Python PoC. But it was way too slow. I also wanted to learn Go and make something that could be used by LeakIX in production. I didn’t want something noisy like wpfinger
. No judgment here, I actually admire the work LeakIX has done, but in this specific case, it felt too risky for wide use.
I’m not a sysadmin either. I used one single Docker instance for everything. The full scan took almost two weeks (because of bad plugins, container crashes, slow DB startup, etc.). I didn’t publish the lab to avoid abuse, but the method is here if anyone wants to rebuild it.
How WPProbe Works (and What It Can’t Do)
WPProbe has 3 steps:
- It fetches all REST endpoints via
?rest_route=/
(orwp-json
alternative) - It matches those to known plugin signatures from a local database
- If possible, it tries to read
readme.txt
,README.txt
to find the version, then checks for known CVEs via Wordfence
When I started, WPProbe could detect about 900 plugins. After finishing the lab, it now supports over 3,000 plugins.
All the official plugins from WordPress.org are supported. But non-official ones are hard to handle automatically. If you know a trick to find and scan those, I’d love to hear it. Sure, 3,000 plugins out of over 100,000 may sound small, but I think it’s already a pretty solid achievement considering the method and limitations.
Plugins may have slightly different endpoints depending on the version. So WPProbe uses a confidence score, based on how many routes match, unless we get the version, then it’s accurate.
Some plugins use the same routes, which can give false positives. That’s normal. I didn’t want to remove all shared routes, since some are valid. So I left them as-is. That said, for bigger plugins like Jetpack or WooCommerce, I cleaned some of the noise using bash scripts.
Vulnerability Info (Wordfence Matching)
Thanks to the free Wordfence API, WPProbe can say if a plugin version has known vulnerabilities.
Right now, it checks readme.txt
to get the version. A better way would be to use hashes of these files, like wpfinger
does, but that’s way too heavy to setup and use for a simple tool like WPProbe.
WPProbe can send a few requests, but only after plugin detection, so it keeps things quiet. The more plugins it finds, the more requests it makes, but it stays under control.
References & Ideas
The idea came from building WordPress modules for Metasploit. It started while I was working on a module, actually several, where I used this handy technique to check which endpoints were available. It helped me reproduce PoCs locally before writing the module itself. But I also heard it mentioned in Critical Thinking Podcast - Episode 55 by a Wordpress Security Researcher. That pushed me to work on it.
I also took some ideas from wpfinger
, made by the LeakIX team. For the local Wordfence vulnerability database, I actually reused that idea, big shout-out to Gregory Boddin, the CTO of LeakIX, who developed it. It was a smart move that I adapted into WPProbe.
Final Words
WPProbe is a small but useful tool for scanning WordPress in a quiet and smart way.
I plan to keep improving it:
- Update the plugin signature list when needed
I’m not sure if I’ll keep working on it completely on my own. Help is always welcome, whether it’s sharing ideas or contributing code. The project is under the MIT license, but please don’t abuse that. Even a small mention or credit means a lot. Between open source fans, it’s important to give credit where it’s due.
Source code: https://github.com/Chocapikk/WPProbe