CakePHP 5.4.2 - Alta - Remota - Unserialize - los motores de caché deserializan objetos sin lista de clases permitidas
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-cache-unserialize
Análisis: https://abraxaslabs.tech/research/cakephp-cache-unserialize
Clase: Inyección de objetos Alcance: Remoto (escritura en Redis/Memcached) / Local (FileEngine)
CakePHP 5.4.2 (887b641) - Cake Software Foundation
La versión 5.3.7 añadió allowedClasses para Redis por esta razón y dejó el valor predeterminado en true (permitir todo) por compatibilidad con versiones anteriores. RedisEngine sigue llamando a unserialize() sin más cuando se mantiene ese valor predeterminado. FileEngine no tiene ninguna lista de permitidos. MemcachedEngine sigue usando php como valor predeterminado de serialize.
Quien pueda ESTABLECER una clave cake_ en Redis, o escribir un archivo de caché, implanta un objeto serializado. La siguiente llamada a Cache::read (o una carga de CacheSession) lo ejecuta en el worker de PHP.
| ID | aún sin CVE |
| Clase | Inyección de objetos |
| Alcance | Remoto cuando Redis o Memcached son escribibles por el atacante. Local para FileEngine en 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 (requiere escritura en el almacén de caché). FileEngine es AV:L. |
| Producto | CakePHP |
| Afectado | 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) |
| Autenticación | escritura en el almacén de caché (Redis SET, Memcached set o un archivo de caché). No es HTTP sin autenticación contra un Redis localhost restringido. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Implantar un objeto serializado en la caché de la aplicación. La siguiente lectura ejecuta PHP en el worker. Este laboratorio se detiene en un archivo testigo. No demuestra una cadena de gadgets ni una shell.
Probado en laboratorio (integridad)
cake_<key> y luego Cache::read. El prefijo predeterminado es cake_. El unserialize() de RedisEngine usa unserialize($value) sin más cuando allowedClasses tiene el valor predeterminado true. Un objeto LabMarker implantado, instanciado como LabMarker, y __wakeup escribió el archivo testigo con el UID de PHP.{expiry}\n{serialize($obj)} bajo la ruta de caché. FileEngine::get() sigue llamando a unserialize($data) sin más. Mismo marcador, mismo testigo. El nombre de archivo es prefix más la clave (cake_labfile).allowedClasses => false rechaza objetos. El mismo payload de Redis volvió como __PHP_Incomplete_Class. __wakeup no se ejecutó. El archivo testigo no estaba presente.Inferido (no ejercitado aquí)
CacheSession::read es el mismo sink cuando la sesión usa defaults => cache. Cada carga de sesión se convierte en un unserialize de bytes de caché controlados por el atacante.serialize => php es la misma clase de fallo. Este laboratorio no inició Memcached.Cerrado / no es este resto
cakephp/app estándar que no expone Redis.allowedClasses => false (o una lista estricta) ya bloquea objetos cuando un operador lo configura. No es el valor predeterminado.Restos de pines similares: window SQLi, collate SQLi, attachment CRLF.
Fijé cakephp/cakephp 5.4.2 después de la oleada de 2026 de FunctionsBuilder / jsonValue / Smtp CRLF / elementos View. RedisEngine documenta la opción y aún la deja abierta por defecto:
* - `allowedClasses` ... Defaults to `true` (allow all) for backwards compatibility.
...
'allowedClasses' => true,
Leer ruta:
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);
}
FileEngine no tiene una opción equivalente:
if ($data !== '' && !empty($this->_config['serialize'])) {
$data = unserialize($data);
Giros equivocados ya registrados: tratar un Redis localhost bloqueado como RCE HTTP sin autenticación; enviar una cadena de gadgets; afirmar que FileEngine es remoto; una reverse shell. Teatro. El oráculo es un marcador __wakeup que escribe un archivo de bandera.
python3 cakephp-cache-unserialize-Abraxas-Labs.py
o
cd lab
./run.sh
Apunta solo a 127.0.0.1:19470. CakePHP 5.4.2 en vivo desde el tag 5.4.2 dentro de Docker. Redis 7 con protected-mode desactivado para que el host pueda hacer SET. PHP 8.3 con ext-redis. Clase marcadora LabMarker con __wakeup que escribe /tmp/CAKEPHP-CACHE-UNSERIALIZE-WITNESS. Sin 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
Establecer allowedClasses de Redis por defecto en false, o en una lista restringida de clases que la aplicación realmente almacena. Añadir la misma opción a FileEngine y respetarla en get(). Establecer serialize de Memcached por defecto en json. Documentar que CacheSession hereda la política de unserialize del motor de caché.
887b641de9f7aba06071f98f17edcbc573cbcf63)RedisEngine.php · FileEngine.php