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
Clase: Inyección de objetos Alcance: Remoto (escritura en Redis/Memcached) / Local (FileEngine)
CakePHP 5.4.2 (887b641) - Cake Software Foundation
5.3.7 añadió allowedClasses de Redis por esta razón y dejó el valor predeterminado en true (permitir todo) por compatibilidad hacia atrás. RedisEngine todavía llama a unserialize() sin más cuando se mantiene ese valor predeterminado. FileEngine no tiene ninguna lista de permitidos. MemcachedEngine todavía tiene serialize predeterminado en php.
Quien pueda ESTABLECER una clave cake_ en Redis, o escribir un archivo de caché, planta un objeto serializado. El siguiente Cache::read (o una carga de CacheSession) lo ejecuta en el worker de PHP.
| ID | sin CVE todavía |
| 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 HTTP sin autenticación a un Redis localhost bloqueado. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Plantar 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 es el valor predeterminado true. Un objeto LabMarker plantado, instanciado como LabMarker, y __wakeup escribió el archivo testigo como el UID de PHP.{expiry}\n{serialize($obj)} bajo la ruta de caché. FileEngine::get() todavía llama 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 estaba ausente.Inferido (no ejercitado aquí)
CacheSession::read es el mismo sink cuando la sesión tiene 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 bug. Este laboratorio no inició Memcached.Cerrado / no es este remanente
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.Mismos remanentes del pin: window SQLi, collate SQLi, attachment CRLF.
Fijé cakephp/cakephp 5.4.2 después de la oleada de 2026 de FunctionsBuilder / jsonValue / Smtp CRLF / View element. RedisEngine documenta la opción y todavía la deja abierta por defecto:
* - `allowedClasses` ... Defaults to `true` (allow all) for backwards compatibility.
...
'allowedClasses' => true,
Ruta de lectura:
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);
Caminos equivocados ya registrados: tratar un Redis localhost bloqueado como RCE HTTP sin autenticación; entregar una cadena de gadgets; afirmar que FileEngine es remoto; una reverse shell. Teatro. El oráculo es un __wakeup de marcador que escribe un archivo bandera.
python3 cakephp-cache-unserialize-Abraxas-Labs.py
o
cd lab
./run.sh
Objetivo solo 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 marcador 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
Poner allowedClasses de Redis por defecto en false, o en una lista estricta de clases que la aplicación realmente almacena. Añadir la misma opción a FileEngine y respetarla en get(). Poner serialize de Memcached por defecto en json. Documentar que CacheSession hereda la política de unserialize del motor de caché.
887b641de9f7aba06071f98f17edcbc573cbcf63)RedisEngine.php · FileEngine.phpGNU Affero GPL v3.0. Ver LICENSE. Solo laboratorio en loopback. Sin garantía.