The best black-box WordPress security scanner
Project description
Plecost
Professional WordPress Security Scanner
Async-first, library-friendly, no external API required.
What is Plecost?
Plecost detects vulnerabilities in WordPress installations — core, plugins, and themes — and correlates findings against a daily-updated local CVE database. It runs as a CLI tool, a Python library, or inside task queues like Celery, with a consistent and automation-friendly output format.
Where Plecost differs from alternatives like WPScan:
- No Ruby, no API key, no subscription. Pure Python, ships with its own CVE database updated daily via GitHub Actions.
- Library-first design.
from plecost import Scannerworks the same as the CLI — no subprocess wrapping needed. - Fully async. Built on
httpxandasyncio; modules run in parallel across a dependency graph for maximum speed. - Stable finding IDs. Every finding has a permanent ID (
PC-CVE-CVE-2023-28121,PC-MCFG-009) safe to track in ticketing systems and dashboards. - SQLite or PostgreSQL. Local SQLite by default; swap to PostgreSQL for shared team deployments.
Quick Start
pip install plecost
# Download the CVE database (first time only — takes a few seconds)
plecost update-db
# Scan a target
plecost scan https://target.wordpress.com
That's it. No account, no API key, no daemon running in the background.
Installation
pip
pip install plecost
pip install plecost[fast] # adds uvloop for higher throughput
pip install plecost[postgres] # adds asyncpg for PostgreSQL support
Docker
docker run --rm ghcr.io/plecost/plecost scan https://target.com
# Save JSON report to local directory
docker run --rm -v $(pwd):/data ghcr.io/plecost/plecost scan https://target.com \
--output /data/report.json
From source
git clone https://github.com/Plecost/plecost.git
cd plecost
pip install -e ".[dev]"
CVE Database
Plecost ships with a local SQLite database covering WordPress core, plugins, and themes. It lives at ~/.plecost/db/plecost.db and is never sent to any external service during scans.
The database needs to be downloaded once before the first scan, and kept up to date thereafter.
First-time setup
plecost update-db
This downloads a pre-built snapshot from plecost-db releases (~10–50 MB). Subsequent runs only download the daily diff — typically under 100 KB.
Keeping it current
Run update-db regularly (weekly is fine for most use cases):
plecost update-db
Plecost checks a SHA256 checksum before downloading anything. If nothing changed since your last run, no data is transferred.
How the update mechanism works
The plecost-db repository runs a GitHub Actions workflow daily at 02:00 UTC. It queries the NVD API v2.0 for all WordPress-related CVEs modified in the last 24 hours, applies Jaro-Winkler fuzzy matching to correlate CVE product names against ~50,000 known plugin/theme slugs, and publishes a small JSON patch file as a release asset.
When you run plecost update-db, it:
- Downloads
index.jsonfrom theplecost-dbreleases (64 bytes) - Compares its SHA256 against the local copy
- Downloads only the missing patch files and applies them in order
- On first run, downloads
full.jsoninstead (complete history)
| Run | What's downloaded | Typical size |
|---|---|---|
| First time | full.json (all CVEs) |
10–50 MB |
| Daily update | today's patch | < 100 KB |
| Already up to date | nothing (checksum match) | 64 bytes |
Custom database location
# SQLite at a custom path
export PLECOST_DB_URL=sqlite:////data/plecost.db
plecost update-db
plecost scan https://target.com
# PostgreSQL (shared team setup)
pip install plecost[postgres]
export PLECOST_DB_URL=postgresql+asyncpg://user:pass@host/plecost
plecost update-db
plecost scan https://target.com
Scanning
Basic scan
$ plecost scan https://target.com
Plecost v4.1 — WordPress Security Scanner
Target: https://target.com
WordPress 6.4.2 detected | WAF: Cloudflare
Plugins (3)
woocommerce 8.2.1 VULNERABLE
contact-form-7 5.8 OK
elementor 3.17.0 OK
Findings (7)
PC-CVE-CVE-2023-28121 WooCommerce SQLi CRITICAL
PC-SSL-001 HTTP does not redirect to HTTPS HIGH
PC-HDR-001 Missing Strict-Transport-Security MEDIUM
PC-USR-001 User enumeration via REST API MEDIUM
PC-XMLRPC-001 XML-RPC interface accessible MEDIUM
PC-REST-001 REST API user data exposed LOW
PC-MCFG-009 readme.html discloses WP version LOW
Summary: 1 Critical 1 High 3 Medium 2 Low | Duration: 4.2s
Common options
# Authenticated scan
plecost scan https://target.com --user admin --password secret
# Route traffic through Burp Suite or OWASP ZAP
plecost scan https://target.com --proxy http://127.0.0.1:8080
# Run only specific detection modules
plecost scan https://target.com --modules fingerprint,plugins,cves
# Aggressive mode — 50 parallel requests (use on internal targets)
plecost scan https://target.com --aggressive
# Deep mode — full wordlist (4750+ plugins, 900+ themes); default scans top 150/50
plecost scan https://target.com --deep
# Stealth mode — random UA, passive detection only, slower
plecost scan https://target.com --stealth
# Save results as JSON
plecost scan https://target.com --output report.json
# Show only HIGH and CRITICAL findings
plecost scan https://target.com --quiet
All scan flags
| Flag | Description | Default |
|---|---|---|
--concurrency N |
Parallel requests | 10 |
--timeout N |
Request timeout (seconds) | 10 |
--proxy URL |
HTTP or SOCKS5 proxy | — |
--user / -u |
WordPress username | — |
--password / -p |
WordPress password | — |
--modules |
Modules to run (comma-separated) | all |
--skip-modules |
Modules to skip | — |
--stealth |
Passive mode, random UA, slower pacing | off |
--aggressive |
Max concurrency (50 requests) | off |
--output / -o |
JSON output file | — |
--no-verify-ssl |
Skip certificate verification | off |
--force |
Scan even if WordPress not detected | off |
--deep |
Full wordlist scan (4750+ plugins, 900+ themes); default is top 150/50 | off |
--verbose / -v |
Real-time module progress and findings during scan | off |
--quiet |
Show only HIGH and CRITICAL findings | off |
--module-option |
Module-specific option: MODULE:KEY=VALUE (repeatable) |
— |
Detection Modules
Plecost runs 16 async modules in parallel, wired through an explicit dependency graph. Modules without interdependencies run concurrently from the start; cves waits for plugins and themes to complete before correlating results against the local database.
| Module | What it checks | Finding IDs |
|---|---|---|
fingerprint |
WordPress version (meta, readme, RSS, wp-login) | PC-FP-001/002 |
waf |
WAF/CDN detection (Cloudflare, Sucuri, Wordfence, Imperva, AWS, Akamai, Fastly) | PC-WAF-001 |
plugins |
Plugin enumeration — passive HTML + brute-force against readme.txt |
PC-PLG-NNN |
themes |
Theme detection via passive scan + style.css brute-force |
PC-THM-001 |
users |
User enumeration via REST API and author archive pages | PC-USR-001/002 |
xmlrpc |
XML-RPC access, pingback.ping DoS vector, system.listMethods |
PC-XMLRPC-001/002/003 |
rest_api |
REST API link disclosure, oEmbed, CORS misconfiguration | PC-REST-001/002/003 |
misconfigs |
12 checks: wp-config.php, .env, .git, debug.log, directory traversal... |
PC-MCFG-001–012 |
directory_listing |
Open directory listing in wp-content/ subdirs |
PC-DIR-001–004 |
http_headers |
Missing HSTS, CSP, X-Frame-Options, X-Content-Type, Referrer-Policy... | PC-HDR-001–008 |
ssl_tls |
HTTP→HTTPS redirect, certificate validity, HSTS preload | PC-SSL-001/002/003 |
debug_exposure |
Active WP_DEBUG, PHP version disclosure via response headers |
PC-DBG-001/003 |
content_analysis |
Card skimming scripts, suspicious iframes, hardcoded API keys | PC-CNT-001/002/003 |
auth |
Authenticated checks: login verification, open user registration | PC-AUTH-001/002 |
cves |
CVE correlation for core + plugins + themes against local DB | PC-CVE-{CVE-ID} |
woocommerce |
WooCommerce-specific security checks (see below) | PC-WC-000–021 |
wp_ecommerce |
WP eCommerce-specific security checks (see below) | PC-WPEC-000–021 |
Use plecost explain <ID> for full technical detail and remediation steps on any finding ID.
WooCommerce Security
The woocommerce module performs dedicated security checks for WooCommerce installations and its official extensions (Payments, Blocks, Stripe Gateway). It runs automatically when WooCommerce is detected.
Passive checks (always on)
- Fingerprinting — detects WooCommerce version, active extensions (Payments, Blocks, Stripe Gateway), and exposed API namespaces
- REST API without authentication — checks whether
/wp-json/wc/v3/customers,/orders,/coupons, and/system-statusare accessible without credentials (CRITICAL/HIGH) - Sensitive file exposure — directory listing on
/wp-content/uploads/wc-logs/, access to/wp-content/uploads/woocommerce_uploads/
Semi-active checks (opt-in)
Semi-active checks send additional HTTP requests that could leave traces in server logs. Enable explicitly:
plecost scan https://target.com --module-option woocommerce:mode=semi-active
| Check | CVE | CVSS |
|---|---|---|
| WooCommerce Payments authentication bypass | CVE-2023-28121 | 9.8 Critical |
| WooCommerce Stripe Gateway IDOR (PII disclosure) | CVE-2023-34000 | 7.5 High |
Authenticated checks (optional)
Provide WooCommerce REST API credentials to unlock additional checks (system configuration disclosure, payment gateway enumeration):
plecost scan https://target.com \
--module-option woocommerce:wc_consumer_key=ck_xxx \
--module-option woocommerce:wc_consumer_secret=cs_xxx
WooCommerce JSON output
When WooCommerce is detected, the scan result includes a dedicated woocommerce section:
{
"woocommerce": {
"detected": true,
"version": "8.5.2",
"active_plugins": ["core", "payments", "blocks", "stripe-gateway"],
"api_namespaces": ["wc/store/v1", "wc/v3"]
}
}
WP eCommerce Security
The wp_ecommerce module performs dedicated security checks for the WP eCommerce (wp-e-commerce) plugin. It runs automatically when WP eCommerce is detected.
Important: WP eCommerce has been abandoned since 2020 (last version: 3.15.1). All current installations are vulnerable to unpatched CVEs. PC-WPEC-003 is always emitted when the plugin is detected.
Passive checks (always on)
- Fingerprinting — detects version via
readme.txt, active payment gateways (ChronoPay) - Directory exposure — plugin directory listing,
uploads/wpsc/,uploads/wpsc/digital/(digital downloads) - Admin scripts — direct access to
wpsc-admin/db-backup.phpandwpsc-admin/display-log.php - ChronoPay endpoint — callback endpoint accessibility check
Semi-active checks (opt-in)
Enable explicitly:
plecost scan https://target.com --module-option wpec:mode=semi-active
| Check | CVE | CVSS |
|---|---|---|
| ChronoPay SQL Injection | CVE-2024-1514 | 9.8 Critical |
| PHP Object Injection via AJAX | CVE-2026-1235 | 8.1 High |
Detection is boolean-only (SQL error strings, deserialization patterns) — no time-based probes.
WP eCommerce JSON output
When WP eCommerce is detected, the scan result includes a dedicated wp_ecommerce section:
{
"wp_ecommerce": {
"detected": true,
"version": "3.15.1",
"active_gateways": ["chronopay"],
"checks_run": ["readme", "directories", "sensitive_files", "chronopay_endpoint"]
}
}
Output Formats
Terminal (default)
Rich-formatted tables with color-coded severities. Use --quiet to suppress LOW/MEDIUM findings.
JSON
plecost scan https://target.com --output report.json
{
"url": "https://target.com",
"scanned_at": "2026-04-13T09:00:00Z",
"is_wordpress": true,
"wordpress_version": "6.4.2",
"waf_detected": "Cloudflare",
"plugins": [{ "slug": "woocommerce", "version": "8.2.1" }],
"themes": [{ "slug": "twentytwentyfour", "version": "1.2" }],
"users": ["admin", "editor"],
"findings": [
{
"id": "PC-CVE-CVE-2023-28121",
"remediation_id": "REM-CVE-CVE-2023-28121",
"title": "WooCommerce SQLi (CVE-2023-28121)",
"severity": "CRITICAL",
"cvss_score": 9.8,
"description": "...",
"remediation": "Update WooCommerce to version 7.8.0 or later.",
"references": ["https://nvd.nist.gov/vuln/detail/CVE-2023-28121"],
"evidence": { "plugin": "woocommerce", "version": "8.2.1" },
"module": "cves"
}
],
"summary": { "critical": 1, "high": 1, "medium": 3, "low": 2 },
"duration_seconds": 4.2,
"blocked": false,
"woocommerce": {
"detected": true,
"version": "8.2.1",
"active_plugins": ["core", "payments", "blocks"],
"api_namespaces": ["wc/store/v1", "wc/v3"]
},
"wp_ecommerce": null
}
Library Usage
Plecost is a first-class Python library. The same logic that powers the CLI is available as an importable API — no subprocess, no parsing CLI output.
Standalone script
import asyncio
from plecost import Scanner, ScanOptions
async def main():
options = ScanOptions(
url="https://target.com",
concurrency=10,
timeout=10,
modules=["fingerprint", "plugins", "cves"], # None = all modules
)
result = await Scanner(options).run()
print(f"WordPress {result.wordpress_version} | WAF: {result.waf_detected}")
for finding in result.findings:
print(f"[{finding.severity.value}] {finding.id}: {finding.title}")
asyncio.run(main())
Celery workers
from celery import Celery
from plecost import Scanner, ScanOptions
import asyncio
app = Celery("tasks")
@app.task
def scan_wordpress(url: str) -> dict:
opts = ScanOptions(url=url, modules=["fingerprint", "plugins", "cves"])
result = asyncio.run(Scanner(opts).run())
return {
"url": result.url,
"critical": result.summary.critical,
"findings": [f.id for f in result.findings],
}
Environment Variables
| Variable | Description | Used by |
|---|---|---|
PLECOST_DB_URL |
Database URL (SQLite or PostgreSQL) | update-db, scan |
PLECOST_TIMEOUT |
Request timeout in seconds | scan |
PLECOST_OUTPUT |
JSON output file path | scan |
GITHUB_TOKEN |
GitHub token to avoid download rate limiting | update-db |
Architecture
Modules without interdependencies run concurrently from the start. cves waits for plugins and themes to complete so it has a full list of installed software to match against the CVE database.
Troubleshooting
"CVE database not found"
The local database hasn't been downloaded yet:
plecost update-db
Target returns 429 (rate limiting)
# Reduce concurrency
plecost scan https://target.com --concurrency 3
# Or use stealth mode (includes automatic pacing)
plecost scan https://target.com --stealth
SSL certificate errors
plecost scan https://target.com --no-verify-ssl
Only use
--no-verify-sslin controlled environments.
Target returns 403 (scanner blocked)
Plecost detects this automatically on the pre-flight probe and aborts cleanly with finding PC-PRE-001. Try a different IP, a proxy, or a different User-Agent:
plecost scan https://target.com --proxy http://127.0.0.1:8080
plecost scan https://target.com --random-user-agent
WordPress not detected
plecost scan https://target.com --force
Local Test Environment
A self-contained Docker Compose environment — Damn Vulnerable WordPress (DVWP) — is included for local testing and development. It spins up a fully configured WordPress instance with a curated set of outdated, intentionally vulnerable plugins.
Located at tests/dvwp/.
Start
cd tests/dvwp
docker compose up -d
docker compose logs wpcli -f # watch setup (~60s), wait for plugin table
Once wpcli exits, the environment is ready:
| URL | Credentials |
|---|---|
| http://localhost:8765 | — |
| http://localhost:8765/wp-admin | admin / admin |
Pre-installed vulnerable plugins
| Plugin | Version | CVE |
|---|---|---|
| wpDiscuz | 7.0.4 | CVE-2020-24186 — unauthenticated RCE via file upload (CVSS 9.8) |
| Contact Form 7 | 5.3.1 | CVE-2020-35489 — unrestricted file upload |
| WooCommerce | 5.0.0 | CVE-2021-32790 — multiple |
| WooCommerce Payments | 3.9.0 | CVE-2023-28121 — unauthenticated privilege escalation (CVSS 9.8) |
| WooCommerce Stripe Gateway | 4.3.0 | CVE-2019-15826 — order information disclosure |
| Easy Digital Downloads | 2.11.5 | CVE-2021-39351 — stored XSS |
| Give – Donation Plugin | 2.10.3 | CVE-2021-34634 — SQL injection |
| YITH WooCommerce Wishlist | 2.2.9 | CVE-2021-24987 — stored XSS |
| Ninja Forms | 3.4.34.2 | CVE-2021-34648 — unauthenticated email injection |
| Duplicator | 1.3.26 | CVE-2020-11738 — path traversal |
| Loginizer | 1.6.3 | CVE-2020-27615 — SQL injection |
| Elementor | 3.1.2 | CVE-2022-1329 — authenticated RCE |
| WP Super Cache | 1.7.1 | CVE-2021-33203 — authenticated XSS |
| Wordfence | 7.5.0 | CVE-2021-24875 — reflected XSS |
Run plecost against it
plecost scan http://localhost:8765 -v
plecost scan http://localhost:8765 --deep -v
Reset
docker compose down -v && docker compose up -d
See tests/dvwp/README.md for full details.
Warning: For local/isolated use only. Never expose to the internet.
Comparison
| Feature | Plecost v4 | WPScan | Wordfence |
|---|---|---|---|
| Python library API | Yes | No | No |
| Async (httpx) | Yes | No | No |
| No API key required | Yes | No (CVEs need API) | No |
| WAF detection (7 providers) | Yes | Yes | No |
| Plugin brute-force | Yes | Yes | No |
| CVE correlation (daily updates) | Yes | Yes | Yes |
| Content / skimmer analysis | Yes | No | Yes |
| WooCommerce dedicated checks | Yes | No | No |
| Stable finding IDs | Yes | No | No |
| Docker native | Yes | Yes | No |
| Celery / library compatible | Yes | No | No |
| PostgreSQL support | Yes | No | No |
License
Plecost is distributed under the PolyForm Noncommercial License 1.0.0.
Free for: personal security research, internal corporate audits, academic and educational use, open source projects, charitable and government organizations.
Requires a commercial license for: scanning-as-a-service, inclusion in a commercial product, or any use generating direct or indirect revenue.
For commercial licensing: cr0hn@cr0hn.com (Dani) · ffranz@mrlooquer.com (Fran)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plecost-4.2.1.tar.gz.
File metadata
- Download URL: plecost-4.2.1.tar.gz
- Upload date:
- Size: 133.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c175df020e48a48d8ff154ac579238b16595d99c5b980b807eb2a14718bf7567
|
|
| MD5 |
39ac43cfcd849c3e6beb37d9e9342c7e
|
|
| BLAKE2b-256 |
55dbb0a4fd38742cece52cd70187dbca6b6f2bd97c4f5260dc15849a37ca9b9b
|
File details
Details for the file plecost-4.2.1-py3-none-any.whl.
File metadata
- Download URL: plecost-4.2.1-py3-none-any.whl
- Upload date:
- Size: 104.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9b467f2bfc6461dbc72b0f0744b7cb730d8685108ccccd98c9228c8703dff0a
|
|
| MD5 |
bd6f3dead4c9dd644e61a97d17cfec86
|
|
| BLAKE2b-256 |
3fbd963ff955eb4bd7aeff5f174883b6c7faf209be700edcf26f3c0f643ff730
|