The best black-box WordPress security scanner
Project description
██████╗ ██╗ ███████╗ ██████╗ ██████╗ ███████╗████████╗
██╔══██╗██║ ██╔════╝██╔════╝██╔═══██╗██╔════╝╚══██╔══╝
██████╔╝██║ █████╗ ██║ ██║ ██║███████╗ ██║
██╔═══╝ ██║ ██╔══╝ ██║ ██║ ██║╚════██║ ██║
██║ ███████╗███████╗╚██████╗╚██████╔╝███████║ ██║
╚═╝ ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝
v4.0.0
Plecost — Professional WordPress Security Scanner
Fully async, zero-interaction WordPress security scanner built for professionals.
Plecost v4.0 detects vulnerabilities in WordPress core, plugins, and themes — enumerates users, identifies misconfigurations, and correlates everything against a daily-updated CVE database. Built on Python 3.11+ with httpx and asyncio, it runs as a CLI tool, a Python library, or inside Celery workers with a consistent, automation-friendly output format.
Table of Contents
- Demo
- Quick Start
- Requirements
- Installation
- Usage
- Environment Variables
- CLI Commands Reference
- CVE Database Management
- Detection Modules
- Finding IDs
- Output Formats
- Library Usage
- Architecture
- Performance
- Troubleshooting
- Comparison
- License
Demo
$ plecost scan https://example.com
Plecost v4.0 — WordPress Security Scanner
Target: https://example.com
Started: 2026-04-12 09:00:00
[+] WordPress detected: 6.4.2
[+] WAF detected: Cloudflare
Plugins discovered (3)
┌─────────────────────┬─────────┬──────────────────┐
│ Plugin │ Version │ Status │
├─────────────────────┼─────────┼──────────────────┤
│ woocommerce │ 8.2.1 │ Vulnerable │
│ contact-form-7 │ 5.8 │ OK │
│ elementor │ 3.17.0 │ OK │
└─────────────────────┴─────────┴──────────────────┘
Themes discovered (1)
┌──────────────────┬─────────┬────────────┐
│ Theme │ Version │ Status │
├──────────────────┼─────────┼────────────┤
│ twentytwentyfour │ 1.2 │ OK │
└──────────────────┴─────────┴────────────┘
Findings (7)
┌────────────────┬──────────────────────────────────────────┬──────────┐
│ ID │ Title │ Severity │
├────────────────┼──────────────────────────────────────────┼──────────┤
│ PC-CVE-001 │ WooCommerce SQLi (CVE-2023-28121) │ CRITICAL │
│ PC-MCFG-009 │ readme.html discloses WordPress version │ LOW │
│ 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-SSL-001 │ HTTP does not redirect to HTTPS │ HIGH │
│ PC-REST-001 │ REST API user data exposed │ LOW │
└────────────────┴──────────────────────────────────────────┴──────────┘
Summary: 1 Critical 1 High 3 Medium 2 Low
Duration: 4.2s
Quick Start
# Install
pip install plecost
# Download the CVE database (first time)
plecost update-db
# Run a scan
plecost scan https://target.com
# Authenticated scan with JSON output
plecost scan https://target.com --user admin --password secret --output report.json
# Stealth mode (random UA, passive checks only)
plecost scan https://target.com --stealth
Requirements
- Python 3.11 or later
- pip (or Docker, see Installation)
- SQLite3 (included in Python's standard library)
- Internet access for CVE database updates (NVD API)
Installation
pip
pip install plecost
pip install plecost[fast] # includes uvloop for higher throughput
From source
git clone https://github.com/Plecost/plecost.git
cd plecost/src
pip install -e .
pip install -e ".[dev]" # include dev/test dependencies
Docker
docker run --rm ghcr.io/Plecost/plecost scan https://target.com
# With proxy and JSON output saved locally
docker run --rm -v $(pwd):/data ghcr.io/Plecost/plecost scan https://target.com \
--proxy http://host.docker.internal:8080 \
--output /data/report.json
Usage
Basic Examples
# Scan with authentication
plecost scan https://target.com --user admin --password secret
# Route through a proxy (Burp Suite, OWASP ZAP, SOCKS5)
plecost scan https://target.com --proxy http://127.0.0.1:8080
plecost scan https://target.com --proxy socks5://127.0.0.1:1080
# Run only specific modules
plecost scan https://target.com --modules fingerprint,plugins,cves
# Skip modules you don't need
plecost scan https://target.com --skip-modules content_analysis,waf
# Aggressive mode (max concurrency: 50 parallel requests)
plecost scan https://target.com --aggressive
# Stealth mode (random User-Agent, passive detection only, slower)
plecost scan https://target.com --stealth
# Save results as JSON
plecost scan https://target.com --output report.json
# Suppress low-severity findings (show HIGH and CRITICAL only)
plecost scan https://target.com --quiet
# Scan without verifying SSL certificate
plecost scan https://target.com --no-verify-ssl
# Force scan even if WordPress is not detected
plecost scan https://target.com --force
Multi-Target Workflow
# Scan a list of targets and save individual JSON reports
while read url; do
plecost scan "$url" --output "reports/$(echo $url | tr '/:' '_').json" --quiet
done < targets.txt
All Flags
| Flag | Description | Default | Env Var |
|---|---|---|---|
--concurrency N |
Number of parallel requests | 10 | — |
--timeout N |
Request timeout in seconds | 10 | PLECOST_TIMEOUT |
--proxy URL |
HTTP or SOCKS5 proxy URL | None | — |
--user / -u |
WordPress username for authenticated scan | None | — |
--password / -p |
WordPress password for authenticated scan | None | — |
--modules |
Comma-separated list of modules to run | all | — |
--skip-modules |
Comma-separated list of modules to skip | none | — |
--stealth |
Random UA, slower pacing, passive checks only | False | — |
--aggressive |
Max concurrency (50 parallel requests) | False | — |
--output / -o |
Save JSON report to file | None | PLECOST_OUTPUT |
--random-user-agent |
Rotate User-Agent on each request | False | — |
--no-verify-ssl |
Skip SSL certificate verification | False | — |
--force |
Continue scan even if WordPress is not detected | False | — |
--quiet |
Show only HIGH and CRITICAL findings | False | — |
Environment Variables
All key settings can be configured via environment variables, making Plecost easy to use in CI/CD pipelines, Docker Compose, and automation scripts.
| Variable | Purpose | Used by | Default |
|---|---|---|---|
PLECOST_TIMEOUT |
Request timeout in seconds | scan |
10 |
PLECOST_OUTPUT |
Output file path for JSON report | scan |
— |
PLECOST_DB_URL |
Database URL (SQLite or PostgreSQL) | update-db, build-db, sync-db |
sqlite:///$HOME/.plecost/db/plecost.db |
NVD_API_KEY |
NVD API key for higher rate limits (free at nvd.nist.gov) | build-db, sync-db |
— |
GITHUB_TOKEN |
GitHub token to avoid download rate limiting | update-db |
— |
Example: CI/CD pipeline
export NVD_API_KEY=your-nvd-api-key
export PLECOST_DB_URL=sqlite:////data/plecost.db
export PLECOST_OUTPUT=/reports/scan.json
export PLECOST_TIMEOUT=30
plecost update-db
plecost scan https://target.com --quiet
Example: Docker Compose
services:
scanner:
image: ghcr.io/Plecost/plecost
environment:
- PLECOST_TIMEOUT=30
- PLECOST_OUTPUT=/reports/scan.json
- NVD_API_KEY=${NVD_API_KEY}
volumes:
- ./reports:/reports
command: scan https://target.com
CLI Commands Reference
plecost scan
Scans a WordPress target and reports findings.
plecost scan https://target.com [OPTIONS]
See All Flags for the full option list.
plecost update-db
Downloads the latest pre-built CVE database from GitHub releases. This is the recommended command for end users — fast, no NVD API access required.
plecost update-db [--db-url sqlite:///path/to/plecost.db] [--token GITHUB_TOKEN]
| Option | Description | Env Var |
|---|---|---|
--db-url |
Database destination URL | PLECOST_DB_URL |
--token |
GitHub token (optional, avoids rate limiting) | GITHUB_TOKEN |
plecost build-db
Builds the CVE database from scratch by querying the NVD API. Downloads up to 5 years of vulnerability history. Intended for maintainers and self-hosted setups.
plecost build-db [--db-url sqlite:///path/to/plecost.db] [--years 5] [--nvd-key API_KEY]
| Option | Description | Env Var | Default |
|---|---|---|---|
--db-url |
Database destination URL | PLECOST_DB_URL |
~/.plecost/db/plecost.db |
--years |
Years of NVD history to download | — | 5 |
--nvd-key |
NVD API key for higher rate limits | NVD_API_KEY |
— |
Note: Without an NVD API key, requests are rate-limited to 1 per 6 seconds. A full 5-year build takes 30–60 minutes. Get a free key at nvd.nist.gov/developers/request-an-api-key.
plecost sync-db
Applies an incremental NVD update — fetches only CVEs published or modified since the last sync. Used by the daily GitHub Actions workflow to keep the distributed database current.
plecost sync-db [--db-url sqlite:///path/to/plecost.db] [--nvd-key API_KEY]
| Option | Description | Env Var |
|---|---|---|
--db-url |
Database to update | PLECOST_DB_URL |
--nvd-key |
NVD API key | NVD_API_KEY |
plecost modules list
Lists all available detection modules with their names and dependency graph.
plecost modules list
plecost explain
Prints the full technical description, impact, and remediation steps for any finding ID.
plecost explain PC-XMLRPC-002
plecost explain PC-CVE-CVE-2023-28121
plecost explain PC-MCFG-009
CVE Database Management
Plecost ships with a local SQLite CVE database covering WordPress core, plugins, and themes. The database is stored at:
~/.plecost/db/plecost.db (Linux / macOS)
%USERPROFILE%\.plecost\db\plecost.db (Windows)
Which command to use?
| Scenario | Command |
|---|---|
| First install / update as end user | plecost update-db |
| Self-hosted or custom DB location | plecost build-db |
| Daily CI/CD incremental update | plecost sync-db |
How it works
-
update-db— Downloads a pre-built, compressed database from the latest GitHub release. Fast (~1 MB download, seconds to complete). -
build-db— Queries the NVD API v2.0 for allkeywordSearch=wordpressCVEs from the past N years. Parses CPE 2.3 entries, applies Jaro-Winkler fuzzy matching to correlate CVE product names against ~50,000 known plugin/theme slugs, and stores results in SQLite. Slow (30–60 min without API key). -
sync-db— Reads thelast_nvd_synctimestamp from the database, fetches only CVEs modified since that date vialastModStartDate, and upserts new or updated records. Runs daily via GitHub Actions.
How the patch system works
Plecost uses an incremental JSON patch system instead of downloading a full SQLite database each day:
| Run | What happens | Typical size |
|---|---|---|
First update-db |
Downloads full.json with all historical CVEs |
~10–50 MB |
Subsequent update-db |
Downloads only today's patch (new/modified CVEs) | <100 KB |
| No changes | Compares checksum only, downloads nothing | 64 bytes |
Each daily patch is a self-contained JSON file verified with SHA256 before being applied.
For architecture details, see docs/cve-patch-system/.
Using PostgreSQL
Plecost supports PostgreSQL for production or team deployments:
pip install plecost[postgres]
plecost build-db --db-url postgresql+asyncpg://user:pass@host/plecost
plecost scan https://target.com --db-url postgresql+asyncpg://user:pass@host/plecost
# or via env var:
export PLECOST_DB_URL=postgresql+asyncpg://user:pass@host/plecost
Detection Modules
Plecost ships 15 independent detection modules that run in parallel, with an explicit dependency graph for maximum throughput.
| Module | Description | Finding IDs |
|---|---|---|
fingerprint |
WordPress version detection via meta tag, readme, RSS, feed, wp-login | PC-FP-001, PC-FP-002 |
waf |
WAF/CDN detection: Cloudflare, Sucuri, Wordfence, Imperva, AWS WAF, Akamai, Fastly | PC-WAF-001 |
plugins |
Plugin enumeration: passive HTML scan + brute-force against readme.txt |
PC-PLG-NNN |
themes |
Theme enumeration: passive + brute-force via style.css |
PC-THM-001 |
users |
User enumeration via REST API and author archive pages | PC-USR-001, PC-USR-002 |
xmlrpc |
XML-RPC checks: access, pingback.ping (DoS vector), system.listMethods |
PC-XMLRPC-001/002/003 |
rest_api |
REST API exposure: link disclosure, oEmbed, CORS misconfiguration | PC-REST-001/002/003 |
misconfigs |
12 misconfiguration checks: wp-config.php, .env, .git, debug.log, etc. |
PC-MCFG-001 to 012 |
directory_listing |
Open directory listing in wp-content/ subdirectories |
PC-DIR-001 to 004 |
http_headers |
Missing security headers: HSTS, CSP, X-Frame-Options, X-Content-Type, etc. | PC-HDR-001 to 008 |
ssl_tls |
SSL/TLS hygiene: HTTP→HTTPS redirect, certificate validity, HSTS preload | PC-SSL-001/002/003 |
debug_exposure |
WP_DEBUG active, PHP version disclosure via headers |
PC-DBG-001, PC-DBG-003 |
content_analysis |
Card skimming scripts, suspicious iframes, hardcoded API keys/secrets | 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 daily-updated local DB | PC-CVE-{CVE-ID} |
Finding IDs
All finding IDs follow a stable, permanent naming convention. IDs will never be renamed or reassigned — safe to reference in dashboards, ticketing systems, and automation rules.
| Prefix | Category | Examples |
|---|---|---|
PC-FP-NNN |
Fingerprint / version disclosure | PC-FP-001 (meta tag), PC-FP-002 (readme.html) |
PC-USR-NNN |
User enumeration | PC-USR-001 (REST API), PC-USR-002 (author archives) |
PC-AUTH-NNN |
Authentication issues | PC-AUTH-001 (login verified), PC-AUTH-002 (open registration) |
PC-XMLRPC-NNN |
XML-RPC exposure | PC-XMLRPC-001 (accessible), PC-XMLRPC-002 (pingback DoS) |
PC-REST-NNN |
REST API exposure | PC-REST-001 to 003 |
PC-CVE-{ID} |
CVE correlations | PC-CVE-CVE-2024-1234 |
PC-MCFG-NNN |
Misconfigurations | PC-MCFG-001 (wp-config.php exposed) to PC-MCFG-012 |
PC-DIR-NNN |
Directory listing | PC-DIR-001 to PC-DIR-004 |
PC-HDR-NNN |
HTTP security headers | PC-HDR-001 (HSTS missing) to PC-HDR-008 (X-Powered-By) |
PC-SSL-NNN |
SSL/TLS hygiene | PC-SSL-001 to PC-SSL-003 |
PC-DBG-NNN |
Debug exposure | PC-DBG-001 (WP_DEBUG active), PC-DBG-003 (PHP version leak) |
PC-CNT-NNN |
Malicious content | PC-CNT-001 (card skimmer), PC-CNT-002 (iframe), PC-CNT-003 (secrets) |
PC-WAF-NNN |
WAF detection | PC-WAF-001 |
Use plecost explain <ID> for full technical description and remediation steps for any finding.
Output Formats
Terminal output (default)
Rich-formatted tables with color-coded severities. Use --quiet to suppress LOW/MEDIUM findings.
JSON output
plecost scan https://target.com --output report.json
The JSON report follows this schema:
{
"url": "https://target.com",
"scanned_at": "2026-04-12T09: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
}
Library Usage
Plecost is designed as a first-class Python library for use in security automation pipelines, CI/CD gates, and vulnerability management platforms.
Standalone Script
import asyncio
from plecost import Scanner, ScanOptions
async def scan():
options = ScanOptions(
url="https://target.com",
concurrency=10,
timeout=10,
proxy="http://127.0.0.1:8080", # optional
modules=["fingerprint", "plugins", "cves"], # None = all modules
skip_modules=[],
credentials=("admin", "secret"), # optional
stealth=False,
aggressive=False,
)
scanner = Scanner(options)
result = await scanner.run()
print(f"WordPress: {result.is_wordpress}")
print(f"Version: {result.wordpress_version}")
print(f"WAF: {result.waf_detected}")
print(f"Plugins: {len(result.plugins)}")
print(f"Users: {len(result.users)}")
for finding in result.findings:
print(f"[{finding.severity.value}] {finding.id}: {finding.title}")
print(f" Remediation: {finding.remediation}")
result.to_json("report.json")
asyncio.run(scan())
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,
"is_wordpress": result.is_wordpress,
"critical": result.summary.critical,
"high": result.summary.high,
"findings": [f.id for f in result.findings],
}
Architecture
CLI / Python API
│
▼
ScanOptions → ScanContext
│
▼
Scheduler (async task graph)
│
┌─────────────┼──────────────────────────┐
▼ ▼ ▼
[fingerprint] [waf] (runs in parallel from start)
│
├──────────┬──────────┬──────────┬──────────┬────────────┐
▼ ▼ ▼ ▼ ▼ ▼
[plugins] [themes] [users] [xmlrpc] [misconfigs] [http_headers]
│ │ │ │ │ │
└────┬─────┘ └──────────┴──────────┴────────────┘
▼
[cves] (depends on plugins + themes results)
│
▼
[Terminal Reporter] / [JSON Reporter]
Each module is an independent async coroutine. The scheduler resolves dependencies and runs tasks at maximum parallelism — modules without interdependencies execute concurrently from the start.
Performance
Plecost is designed for speed. All network I/O is non-blocking, and the task graph runs modules at maximum concurrency.
| Scenario | Concurrency | Avg. Duration |
|---|---|---|
| Full scan, no WAF | 10 (default) | ~4–8s |
| Full scan, Cloudflare CDN | 10 (default) | ~8–15s |
| Aggressive mode | 50 | ~2–4s |
| Stealth mode | 3 | ~20–40s |
| Plugin brute-force (10k list) | 10 | ~90s |
Throughput scales linearly with --concurrency up to the target's rate limits. Use --aggressive for internal targets or lab environments. If you encounter 429 responses, reduce concurrency with --concurrency 3.
Troubleshooting
"CVE database not found"
The local CVE database has not been downloaded yet. Run:
plecost update-db
If you want to store the database in a custom location:
plecost update-db --db-url sqlite:////path/to/custom/plecost.db
# or:
export PLECOST_DB_URL=sqlite:////path/to/custom/plecost.db
plecost update-db
Target returns 429 (rate limiting)
Reduce concurrency:
plecost scan https://target.com --concurrency 3
Or use stealth mode, which includes automatic pacing:
plecost scan https://target.com --stealth
SSL certificate errors
For testing environments with self-signed certificates:
plecost scan https://target.com --no-verify-ssl
Warning: Only use
--no-verify-sslin controlled environments. Disabling certificate verification exposes you to man-in-the-middle attacks.
WordPress not detected
If you know the target is WordPress but it's behind a WAF or custom setup:
plecost scan https://target.com --force
NVD API rate limiting during build-db
Without an API key, NVD allows 1 request per 6 seconds. Get a free key at nvd.nist.gov/developers/request-an-api-key to increase the limit to ~50 req/30s:
plecost build-db --nvd-key YOUR_API_KEY
# or:
export NVD_API_KEY=YOUR_API_KEY
plecost build-db
Comparison
| Feature | Plecost v4 | WPScan | Wordfence | ScanTower |
|---|---|---|---|---|
| Python library API | Yes | No | No | No |
| Async (httpx) | Yes | No | No | No |
| WAF detection (7 providers) | Yes | Yes | No | Yes |
| Plugin brute-force | Yes | Yes | No | Yes |
| CVE correlation (daily updates) | Yes | Yes (API key) | Yes | Yes |
| Content / skimmer analysis | Yes | No | Yes | No |
| Stable finding IDs | Yes | No | No | No |
| Docker native | Yes | Yes | No | No |
| Celery / library compatible | Yes | No | No | No |
| No external API dependency | Yes | No | No | No |
| PostgreSQL support | Yes | No | No | No |
License
Plecost is distributed under the PolyForm Noncommercial License 1.0.0 — a standard, lawyer-drafted noncommercial license.
Free for:
- Personal security research and penetration testing
- Internal corporate security audits (not resold)
- Academic, educational, and public research use
- Charitable and government organizations
- Open source projects
Requires a commercial license for:
- Offering scanning as a service (SaaS, API)
- Including Plecost in a commercial product or paid offering
- Any use generating direct or indirect revenue
For commercial licensing inquiries: cr0hn@cr0hn.com
See LICENSE for full terms.
Author: Dani (cr0hn) — cr0hn@cr0hn.com
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.0.0.tar.gz.
File metadata
- Download URL: plecost-4.0.0.tar.gz
- Upload date:
- Size: 63.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae45798114ef4cac34e1602564f3267fc50f3702b16984f573ee66df969bbfb3
|
|
| MD5 |
e428713d2d25644dccf025a49d218cd7
|
|
| BLAKE2b-256 |
0a5917e7f69231847eb2e443d131888f9c5b8dd2e2e9851144cfe9e307acfafe
|
File details
Details for the file plecost-4.0.0-py3-none-any.whl.
File metadata
- Download URL: plecost-4.0.0-py3-none-any.whl
- Upload date:
- Size: 54.5 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 |
b80027eb236b93541d4af57895f4c5a87c79b040ed057d79591b5e2102327f33
|
|
| MD5 |
e7be3dc00b6cf609c7ed8ab7e479abd6
|
|
| BLAKE2b-256 |
957e211d5fecf654487152ace3b3dd2d66751e5be73977165d1dc432c2d0c59c
|