CakePHP 5.4.2 - Alto - Remoto - Unserialize - mecanismos de cache desserializam objetos sem lista de permissões de classes
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-cache-unserialize
Classe: Injeção de objeto Alcance: Remoto (escrita em Redis/Memcached) / Local (FileEngine)
CakePHP 5.4.2 (887b641) - Cake Software Foundation
A versão 5.3.7 adicionou allowedClasses ao Redis por esse motivo e manteve o padrão em true (permitir tudo) para compatibilidade retroativa. O RedisEngine ainda chama unserialize() puro quando esse padrão se mantém. O FileEngine não tem lista de permissões alguma. O MemcachedEngine ainda define serialize como php por padrão.
Quem consegue DEFINIR uma chave cake_ no Redis, ou escrever um arquivo de cache, planta um objeto serializado. O próximo Cache::read (ou um carregamento de CacheSession) o executa no worker PHP.
| ID | sem CVE ainda |
| Classe | Injeção de objeto |
| Alcance | Remoto quando o Redis ou o Memcached é gravável pelo atacante. Local para o FileEngine em tmp/cache. |
| CWE | CWE-502 |
| CVSS | Alto: 8.8 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (requer escrita no armazenamento de cache). O FileEngine é AV:L. |
| Produto | CakePHP |
| Afetado | 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) |
| Autenticação | escrita no armazenamento de cache (Redis SET, Memcached set, ou um arquivo de cache). Não é HTTP não autenticado para um Redis localhost restrito. |
| Licença | GNU Affero GPL v3.0 |
| Lab | somente 127.0.0.1 |
Plantar um objeto serializado no cache da aplicação. A próxima leitura executa PHP no worker. Este lab para em um arquivo testemunha. Ele não demonstra uma cadeia de gadgets nem um shell.
Comprovado no lab (integridade)
cake_<key> e depois Cache::read. O prefixo padrão é cake_. O unserialize() do RedisEngine usa unserialize($value) puro quando allowedClasses está no padrão true. Um objeto LabMarker plantado, instanciado como LabMarker, e __wakeup escreveu o arquivo testemunha com o UID do PHP.{expiry}\n{serialize($obj)} sob o caminho de cache. O FileEngine::get() ainda chama unserialize($data) puro. Mesmo marcador, mesma testemunha. O nome do arquivo é prefix mais a chave (cake_labfile).allowedClasses => false recusa objetos. O mesmo payload do Redis voltou como __PHP_Incomplete_Class. O __wakeup não foi executado. O arquivo testemunha estava ausente.Inferido (não exercitado aqui)
CacheSession::read é o mesmo sink quando a sessão usa defaults => cache. Cada carregamento de sessão se torna um unserialize de bytes de cache controlados pelo atacante.serialize => php é a mesma classe de bug. Este lab não iniciou o Memcached.Fechado / não é este resquício
cakephp/app padrão que não expõe o Redis.allowedClasses => false (ou uma lista restrita) já bloqueia objetos quando um operador o define. Não é o padrão.Mesmos resquícios do pin: window SQLi, collate SQLi, attachment CRLF.
Eu fixei o cakephp/cakephp 5.4.2 após a onda de 2026 de FunctionsBuilder / jsonValue / Smtp CRLF / View element. O RedisEngine documenta a opção e ainda a deixa aberta por padrão:
* - `allowedClasses` ... Defaults to `true` (allow all) for backwards compatibility.
...
'allowedClasses' => true,
Caminho de leitura:
protected function unserialize(string $value): mixed
{
if (preg_match('/^[-]?\d+$/', $value)) {
return (int)$value;
}
$allowedClasses = $this->getConfig('allowedClasses');
if ($allowedClasses !== true) {
return unserialize($value, ['allowed_classes' => $allowedClasses]);
}
return unserialize($value);
}
O FileEngine não tem opção equivalente:
if ($data !== '' && !empty($this->_config['serialize'])) {
$data = unserialize($data);
Caminhos errados já registrados: tratar um Redis localhost restrito como RCE HTTP não autenticado; entregar uma cadeia de gadgets; afirmar que o FileEngine é remoto; um reverse shell. Teatro. O oráculo é um __wakeup de marcador que escreve um arquivo de flag.
python3 cakephp-cache-unserialize-Abraxas-Labs.py
ou
cd lab
./run.sh
Alvo somente 127.0.0.1:19470. CakePHP 5.4.2 ao vivo a partir da tag 5.4.2 dentro do Docker. Redis 7 com protected-mode desligado para que o host possa fazer SET. PHP 8.3 com ext-redis. Classe marcadora LabMarker com __wakeup que escreve /tmp/CAKEPHP-CACHE-UNSERIALIZE-WITNESS. Sem shell.
SUCCESS cakephp-cache-unserialize redis-default-wakeup=yes redis-denied-wakeup=no file-wakeup=yes pin=5.4.2 sha=887b641de9f7aba06071f98f17edcbc573cbcf63 how=live CAKEPHP-CACHE-UNSERIALIZE-WITNESS
Definir o allowedClasses do Redis como false por padrão, ou como uma lista restrita de classes que a aplicação realmente armazena. Adicionar a mesma opção ao FileEngine e honrá-la em get(). Definir o serialize do Memcached como json por padrão. Documentar que o CacheSession herda a política de unserialize do motor de cache.
887b641de9f7aba06071f98f17edcbc573cbcf63)RedisEngine.php · FileEngine.phpGNU Affero GPL v3.0. Veja LICENSE. Somente lab em loopback. Sem garantia.