Skip to content

WPScan Reference

WPScan is the dedicated WordPress vulnerability scanner. It combines plugin/theme/user enumeration, vulnerability database lookups, and brute-force in one tool. Three operator-relevant invocation patterns:

# 1. Fingerprint + vulnerability lookup (most common opening scan)
wpscan --url http://target --enumerate ap,at,u --api-token YOUR_TOKEN
# 2. Brute-force a known user list
wpscan --url http://target --password-attack xmlrpc \
-U users.txt -P /usr/share/wordlists/rockyou.txt -t 20
# 3. Stealthy reconnaissance (single-thread, random UA, slow)
wpscan --url http://target --random-user-agent --throttle 1000 \
--max-scan-duration 600 --enumerate vp

Success indicator: a CLI report with detected core version, plugin/theme list, user list, and (with API token) annotated CVE references for each component.

WPScan ships as a Ruby gem. Install:

Terminal window
gem install wpscan

On Kali / Parrot OS it’s preinstalled. Update the local vulnerability DB cache:

Terminal window
wpscan --update

Free tier: 25 API requests per day. Register at wpscan.com, grab the token from your account page. Either pass per-command via --api-token or store it permanently:

Terminal window
mkdir -p ~/.wpscan
echo 'cli_options:
api_token: YOUR_TOKEN_HERE' > ~/.wpscan/scan.yml

After this, every wpscan invocation uses the token automatically. Without a token, WPScan still enumerates plugins/themes/users but won’t cross-reference them against the vulnerability database - you’ll see component versions but no CVE annotations.

The --enumerate (or -e) flag controls what WPScan attempts to identify. Multiple options can be combined comma-separated.

CodeTargetWhat it doesDetection mode
vpVulnerable pluginsPassive detection from page source; only flags plugins with known CVEsPassive only
apAll pluginsBrute-force the full plugin wordlist (~28k entries)Aggressive
pPopular pluginsSubset of ap - common plugins onlyMixed
vtVulnerable themesPassive theme detection; flags themes with known CVEsPassive only
atAll themesBrute-force theme wordlistAggressive
tPopular themesSubset of atMixed
uUsers?author= brute-force + wp-json/wp/v2/users + login-error differentialMixed
u[1-10]Users (range)Same, restricted to ID range - e.g. u1-50Mixed
mMediaEnumerate uploaded media by ID via ?attachment_id=Aggressive
cbConfig backupsProbe for wp-config.php.bak, wp-config.php~, etc.Aggressive
dbeDB exportsProbe for SQL dumps in common pathsAggressive
GoalCommand
Quick fingerprint, only known-vulnerable components--enumerate vp,vt,u
Full sweep including deactivated plugins--enumerate ap,at,u,cb,dbe
Just users (for brute-force prep)--enumerate u
User range from 1 to 100--enumerate u1-100
Hunt for forgotten config backups--enumerate cb,dbe

The default (no --enumerate specified) is roughly equivalent to vp,vt,u,cb,dbe - vulnerable components, users, and config/DB backups. Fast but misses deactivated-but-on-disk plugins. The first deep scan on a target should usually use ap,at to catch those.

WPScan distinguishes between “Passive” detection (only reads what’s on the homepage and other crawled pages) and “Aggressive” detection (sends extra requests to specific paths). Some --enumerate codes force one or the other:

  • vp, vt - passive only (won’t detect deactivated plugins/themes)
  • ap, at, m, cb, dbe - always aggressive
  • u - mixed (passive from page source first, then aggressive ?author= brute-force)

The --detection-mode flag overrides this:

Terminal window
wpscan --url http://target --enumerate u --detection-mode passive # passive only
wpscan --url http://target --enumerate u --detection-mode aggressive # force aggressive
wpscan --url http://target --enumerate u --detection-mode mixed # default

For stealth assessments: --detection-mode passive keeps WPScan from sending the noisy brute-force probes.

The --password-attack flag enables brute-force. Two modes:

ModePathNotes
wp-loginwp-login.php form POSTSlower; often rate-limited
xmlrpcxmlrpc.php XML-RPC APIFaster; uses system.multicall for amplification when threads are high
xmlrpc-multicallxmlrpc.php with explicit multicallHighest throughput; older WP only
Terminal window
wpscan --url http://target --password-attack xmlrpc \
-U user1,user2,user3 \
-P /usr/share/wordlists/rockyou.txt \
-t 20

Flags:

  • -U - usernames. Comma-separated (-U admin,david) or file (-U @users.txt)
  • -P - password wordlist file (one password per line)
  • -t - threads (default 5; up to 30 reasonable; >30 risks server-side rate limit)
  • --password-attack - explicit method choice (otherwise WPScan auto-picks)

Stop conditions:

Terminal window
--stop-on-find # stop after the first valid credential (default)
--no-stop-on-find # try every user, even after a hit
[+] Performing password attack on Xmlrpc against 3 user/s
[SUCCESS] - admin / sunshine1
Trying david / Spring2016 Time: 00:00:01 <==========> (474 / 474) 100.00%
[i] Valid Combinations Found:
| Username: admin, Password: sunshine1

For passwords spanning multiple words / spaces / special chars, the wordlist’s encoding matters - UTF-8 with one password per line works.

Instead of running a wordlist against many users (per-user lockout risk), run one password across many users:

Terminal window
echo 'Spring2024!' > one_password.txt
wpscan --url http://target --password-attack xmlrpc \
-U users.txt -P one_password.txt

See Login brute-force for spray-pattern selection.

Terminal window
--api-token TOKEN # WPScan API token (or use ~/.wpscan/scan.yml)

With token configured, WPScan automatically queries wpscan.com/api for each detected component. Annotated output:

[+] mail-masta
| Location: http://target/wp-content/plugins/mail-masta/
| Latest Version: 1.0 (up to date)
| Found By: Urls In Homepage (Passive Detection)
|
| [!] 2 vulnerabilities identified:
|
| [!] Title: Mail Masta 1.0 - Unauthenticated Local File Inclusion (LFI)
| Fixed in: <unfixed>
| References:
| - https://www.exploit-db.com/exploits/40290/
| CVE: CVE-2016-1000142
|
| [!] Title: Mail Masta 1.0 - Multiple SQL Injection
| Fixed in: <unfixed>
| References:
| - https://wpscan.com/vulnerabilities/8740

The Fixed in: <unfixed> line is operator-relevant. When no fix exists, the target is exposed regardless of how recent its WP install is.

Terminal window
-f, --format FORMAT # cli, cli-no-colour, cli-no-color, json
-o, --output FILE # write to file instead of stdout

For automation:

Terminal window
wpscan --url http://target --enumerate ap,at,u \
--api-token TOKEN \
--format json --output scan.json
Terminal window
# Then parse with jq
jq '.plugins | to_entries[] | select(.value.vulnerabilities | length > 0) |
{name: .key, vulns: .value.vulnerabilities[].title}' scan.json

Useful JSON paths:

jq pathWhat it gives
.versionWordPress core version
.pluginsObject keyed by plugin name
.plugins.PLUGIN.version.numberDetected version
.plugins.PLUGIN.vulnerabilitiesArray of CVE entries
.themesSame shape as plugins
.usersObject keyed by username
.config_backupsFound config backup URLs
Terminal window
--random-user-agent # rotate UA from a built-in list per-request
--user-agent "STRING" # set a fixed UA
--throttle MILLISECONDS # delay between requests (default 0)
--request-timeout SECONDS # per-request timeout (default 60)
--connect-timeout SECONDS # connection timeout (default 30)
--disable-tls-checks # ignore TLS errors (self-signed certs)
--max-threads N # cap thread count (overrides -t)
--max-scan-duration SECONDS # abort the entire scan after N seconds
Terminal window
wpscan --url http://target \
--random-user-agent \
--throttle 2000 \
--max-threads 1 \
--detection-mode passive \
--enumerate vp,vt

What this does: one thread, one request every 2 seconds, randomized UA per request, only passive detection (no path brute-forcing), only vulnerable-component identification. From the target’s perspective, looks like a slow scrape rather than a scan.

WPScan’s default user-agent (WPScan v3.X (https://wpscan.org)) is matched by most WAF rules. Always set a real-browser UA:

Terminal window
wpscan --url http://target \
--user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
Terminal window
--plugins-detection MODE # passive, aggressive, mixed (default)
--plugins-version-detection MODE # same options, for version-specifically
--plugins-list FILE # custom plugin wordlist

For targeted brute-force when you suspect a specific set of plugins:

Terminal window
echo "mail-masta
site-editor
contact-form-7" > custom_plugins.txt
wpscan --url http://target --enumerate ap --plugins-list custom_plugins.txt

Run WPScan through Burp for inspection or rewriting:

Terminal window
wpscan --url http://target --proxy http://127.0.0.1:8080

Burp’s HTTP history will show every request WPScan issues - useful for understanding what specific finding got flagged, or for replaying a specific request with a tweaked payload.

For SOCKS:

Terminal window
wpscan --url http://target --proxy socks5://127.0.0.1:9050 # via Tor

Authentication for the proxy:

Terminal window
wpscan --url http://target --proxy http://127.0.0.1:8080 \
--proxy-auth user:pass
Terminal window
--cookie-string "name=value; name2=value2" # send cookies
--cookie-jar FILE # persist cookies across requests
--headers "Header: value" # extra headers (repeatable)
--http-auth user:pass # HTTP basic auth

If part of the WordPress site is behind HTTP basic auth:

Terminal window
wpscan --url http://target --http-auth admin:basicpass --enumerate ap

If the target requires a session cookie:

Terminal window
wpscan --url http://target \
--cookie-string "wordpress_logged_in_HASH=admin|...|sig" \
--enumerate ap

(Authenticated scanning lets WPScan see admin-area routes; rarely needed for offensive recon but sometimes valuable.)

SymptomCauseFix
Scan Aborted: The target seems to be downTarget uses Cloudflare with browser challengeUse --user-agent real-browser; sometimes need to disable Cloudflare-blocked features
[ERROR] The url supplied 'http://target' seems to be downTarget redirects to HTTPSUse --url https://target directly
Plugin enumeration takes foreverap mode walks ~28k pluginsLimit with --plugins-list to a focused subset
No CVE annotationsMissing API tokenAdd --api-token or configure in ~/.wpscan/scan.yml
Scan Aborted: 429 Too Many RequestsServer rate-limitingLower -t, add --throttle 2000, or rotate IPs
Brute-force is locked out after a few attemptsPer-user lockoutSwitch to spray pattern (one pass, many users)
Random user-agent still gets WAF-blockedUA list known to defenderUse a fixed real-browser --user-agent
Output file is hugeap,at,u,m,cb,dbe produces verbose CLI reportUse --format json for machine-readable, smaller-on-disk results

The end-to-end workflow combining everything in this cluster:

Terminal window
# 1. Initial fingerprint + vuln lookup (passive-leaning)
wpscan --url http://target \
--random-user-agent \
--enumerate vp,vt,u \
--api-token $WPSCAN_TOKEN \
--format json --output stage1.json
# 2. Read the report; identify the vulnerable plugins worth chasing
jq '.plugins | to_entries[] | select(.value.vulnerabilities | length > 0)' stage1.json
# 3. If no easy CVE hits, do a deep plugin sweep
wpscan --url http://target \
--random-user-agent \
--enumerate ap,at,cb,dbe \
--api-token $WPSCAN_TOKEN \
--format json --output stage2.json
# 4. With user list from step 1, brute-force
wpscan --url http://target \
--password-attack xmlrpc \
-U admin,david,roger \
-P /usr/share/wordlists/rockyou.txt \
-t 20
# 5. Once admin creds are found, drop to manual:
# log into wp-admin, use theme editor → 404.php webshell
# OR: msfconsole > use exploit/unix/webapp/wp_admin_shell_upload

Each stage’s output feeds the next. WPScan is the recon backbone; the vulnerable plugins and admin-to-RCE pages cover what to do with each kind of finding.

FlagWhat it does
--url URLTarget WordPress URL (required)
--enumerate vp,ap,t,vt,at,u,m,cb,dbeWhat to enumerate (see matrix above)
-e CODESShort form of --enumerate
--api-token TOKENWPScan vulnerability-DB token
--detection-mode {passive|aggressive|mixed}Force detection style
--password-attack {wp-login|xmlrpc|xmlrpc-multicall}Brute-force method
-U USERSUsername list (comma-separated or @file)
-P PASSWORDSPassword wordlist file
-t NThreads (default 5)
--max-threads NHard cap on threads
--throttle MSDelay between requests in ms
--max-scan-duration SECAbort whole scan after N seconds
--user-agent STRINGSet User-Agent
--random-user-agentRotate UA from built-in list
--proxy URLHTTP/SOCKS proxy
--proxy-auth user:passAuth for the proxy
--cookie-string STRINGSend cookies
--http-auth user:passHTTP Basic Auth
--headers "K: V"Add header (repeatable)
--disable-tls-checksIgnore TLS errors
-f, --format {cli|cli-no-colour|json}Output format
-o, --output FILEWrite to file
--updateUpdate local vuln DB cache
--stop-on-findStop brute-force on first hit (default)
--plugins-list FILECustom plugin wordlist for ap
--plugins-detection MODEDetection mode for plugins specifically
-v, --verboseVerbose output
--banner / --no-bannerToggle ASCII banner
-h, --help / --hhShort / full help
Defenses D3-IDA