Skip to main content

The best black-box WordPress security scanner

Project description

██████╗ ██╗     ███████╗ ██████╗ ██████╗ ███████╗████████╗
██╔══██╗██║     ██╔════╝██╔════╝██╔═══██╗██╔════╝╚══██╔══╝
██████╔╝██║     █████╗  ██║     ██║   ██║███████╗   ██║
██╔═══╝ ██║     ██╔══╝  ██║     ██║   ██║╚════██║   ██║
██║     ███████╗███████╗╚██████╗╚██████╔╝███████║   ██║
╚═╝     ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝   ╚═╝
                                                  v4.0.0

Plecost — Professional WordPress Security Scanner

CI Docker PyPI Python 3.11+ License: PolyForm NC

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

$ 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

  1. update-db — Downloads a pre-built, compressed database from the latest GitHub release. Fast (~1 MB download, seconds to complete).

  2. build-db — Queries the NVD API v2.0 for all keywordSearch=wordpress CVEs 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).

  3. sync-db — Reads the last_nvd_sync timestamp from the database, fetches only CVEs modified since that date via lastModStartDate, 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-ssl in 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plecost-4.0.0.tar.gz (63.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plecost-4.0.0-py3-none-any.whl (54.5 kB view details)

Uploaded Python 3

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

Hashes for plecost-4.0.0.tar.gz
Algorithm Hash digest
SHA256 ae45798114ef4cac34e1602564f3267fc50f3702b16984f573ee66df969bbfb3
MD5 e428713d2d25644dccf025a49d218cd7
BLAKE2b-256 0a5917e7f69231847eb2e443d131888f9c5b8dd2e2e9851144cfe9e307acfafe

See more details on using hashes here.

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

Hashes for plecost-4.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b80027eb236b93541d4af57895f4c5a87c79b040ed057d79591b5e2102327f33
MD5 e7be3dc00b6cf609c7ed8ab7e479abd6
BLAKE2b-256 957e211d5fecf654487152ace3b3dd2d66751e5be73977165d1dc432c2d0c59c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page