Skip to main content

The best black-box WordPress security scanner

Project description

Plecost

Plecost

Professional WordPress Security Scanner

Async-first, library-friendly, no external API required.

CI PyPI Python 3.11+ Docker License: PolyForm NC

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 Scanner works the same as the CLI — no subprocess wrapping needed.
  • Fully async. Built on httpx and asyncio; 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:

  1. Downloads index.json from the plecost-db releases (64 bytes)
  2. Compares its SHA256 against the local copy
  3. Downloads only the missing patch files and applies them in order
  4. On first run, downloads full.json instead (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-status are 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.php and wpsc-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

Plecost architecture diagram

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-ssl in 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


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.2.1.tar.gz (133.1 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.2.1-py3-none-any.whl (104.9 kB view details)

Uploaded Python 3

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

Hashes for plecost-4.2.1.tar.gz
Algorithm Hash digest
SHA256 c175df020e48a48d8ff154ac579238b16595d99c5b980b807eb2a14718bf7567
MD5 39ac43cfcd849c3e6beb37d9e9342c7e
BLAKE2b-256 55dbb0a4fd38742cece52cd70187dbca6b6f2bd97c4f5260dc15849a37ca9b9b

See more details on using hashes here.

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

Hashes for plecost-4.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a9b467f2bfc6461dbc72b0f0744b7cb730d8685108ccccd98c9228c8703dff0a
MD5 bd6f3dead4c9dd644e61a97d17cfec86
BLAKE2b-256 3fbd963ff955eb4bd7aeff5f174883b6c7faf209be700edcf26f3c0f643ff730

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