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
Análise: https://abraxaslabs.tech/research/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) por compatibilidade retroativa. O RedisEngine ainda chama unserialize() puro quando esse padrão prevalece. O FileEngine não possui allowlist alguma. O MemcachedEngine ainda define serialize como php por padrão.
Quem puder definir uma chave cake_ no Redis, ou gravar um arquivo de cache, planta um objeto serializado. O próximo Cache::read (ou um carregamento de CacheSession) o executa no worker PHP.
| ID | ainda sem CVE |
| Classe | Injeção de objeto |
| Alcance | Remoto quando o Redis ou o Memcached são graváveis 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 |
| Laboratório | somente 127.0.0.1 |
Plantar um objeto serializado no cache da aplicação. A próxima leitura executa PHP no worker. Este laboratório para em um arquivo testemunha. Ele não demonstra uma cadeia de gadgets nem um shell.
Comprovado no laboratório (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 gravou 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 laboratório 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 de pin: window SQLi, collate SQLi, attachment CRLF.
Fiz o pin do cakephp/cakephp 5.4.2 após a onda de 2026 de FunctionsBuilder / jsonValue / Smtp CRLF / elemento View. 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,
Leia o caminho:
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);
Erros já registrados: tratar o Redis localhost bloqueado como RCE HTTP não autenticado; entregar uma cadeia de gadgets; afirmar que o FileEngine é remoto; um reverse shell. Teatro. O oráculo é um marcador __wakeup que grava um arquivo de flag.
python3 cakephp-cache-unserialize-Abraxas-Labs.py
ou
cd lab
./run.sh
Aponte apenas para 127.0.0.1:19470. CakePHP 5.4.2 em execução a partir da tag 5.4.2 dentro do Docker. Redis 7 com protected-mode desativado para que o host possa executar 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 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 respeitá-la em get(). Definir serialize do Memcached como json por padrão. Documentar que CacheSession herda a política de unserialize do motor de cache.
887b641de9f7aba06071f98f17edcbc573cbcf63)RedisEngine.php · FileEngine.phpGNU Affero GPL v3.0. Consulte LICENSE. Apenas laboratório loopback. Sem garantias.