CakePHP 5.4.2 - Élevé - À distance - Désérialisation - les moteurs de cache désérialisent des objets sans liste blanche de classes
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-cache-unserialize
Classe : Injection d'objet Portée : Distante (écriture Redis/Memcached) / Locale (FileEngine)
CakePHP 5.4.2 (887b641) - Cake Software Foundation
5.3.7 a ajouté allowedClasses pour Redis pour cette raison et a laissé la valeur par défaut à true (tout autoriser) pour la compatibilité ascendante. RedisEngine appelle toujours unserialize() nu lorsque cette valeur par défaut est conservée. FileEngine n'a aucune liste d'autorisation. MemcachedEngine conserve par défaut serialize à php.
Quiconque peut DÉFINIR une clé cake_ sur Redis, ou écrire un fichier de cache, place un objet sérialisé. Le prochain Cache::read (ou un chargement CacheSession) l'exécute dans le worker PHP.
| ID | pas encore de CVE |
| Classe | Injection d'objet |
| Portée | Distante lorsque Redis ou Memcached est accessible en écriture depuis l'attaquant. Locale pour FileEngine sur tmp/cache. |
| CWE | CWE-502 |
| CVSS | Élevé : 8.8 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (nécessite l'écriture dans le magasin de cache). FileEngine est AV:L. |
| Produit | CakePHP |
| Affecté | 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) |
| Auth | écriture dans le magasin de cache (Redis SET, Memcached set, ou un fichier de cache). Pas de HTTP non authentifié vers un Redis localhost verrouillé. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Placer un objet sérialisé dans le cache de l'application. La lecture suivante exécute du PHP dans le worker. Ce lab s'arrête à un fichier témoin. Il ne démontre pas de chaîne de gadgets ni de shell.
Prouvé en lab (intégrité)
cake_<key> puis Cache::read. Le préfixe par défaut est cake_. Le unserialize() de RedisEngine utilise unserialize($value) nu lorsque allowedClasses est à la valeur par défaut true. Un objet LabMarker placé, instancié en tant que LabMarker, et __wakeup a écrit le fichier témoin avec l'UID PHP.{expiry}\n{serialize($obj)} sous le chemin de cache. FileEngine::get() appelle toujours unserialize($data) nu. Même marqueur, même témoin. Le nom de fichier est prefix plus la clé (cake_labfile).allowedClasses => false refuse les objets. La même charge utile Redis est revenue en __PHP_Incomplete_Class. __wakeup ne s'est pas exécuté. Le fichier témoin était absent.Déduit (non exercé ici)
CacheSession::read est le même sink lorsque la session defaults => cache. Chaque chargement de session devient un unserialize d'octets de cache contrôlés par l'attaquant.serialize => php est la même classe de bug. Ce lab n'a pas démarré Memcached.Fermé / pas ce reliquat
cakephp/app standard qui n'expose pas Redis.allowedClasses => false (ou une liste restreinte) bloque déjà les objets lorsqu'un opérateur le définit. Ce n'est pas la valeur par défaut.Mêmes reliquats de pin : window SQLi, collate SQLi, attachment CRLF.
J'ai épinglé cakephp/cakephp 5.4.2 après la vague 2026 FunctionsBuilder / jsonValue / Smtp CRLF / View element. RedisEngine documente l'option et la laisse toujours ouverte par défaut :
* - `allowedClasses` ... Defaults to `true` (allow all) for backwards compatibility.
...
'allowedClasses' => true,
Chemin de lecture :
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 n'a pas d'option équivalente :
if ($data !== '' && !empty($this->_config['serialize'])) {
$data = unserialize($data);
Fausses pistes déjà consignées : traiter un Redis localhost verrouillé comme un RCE HTTP non authentifié ; livrer une chaîne de gadgets ; prétendre que FileEngine est distant ; un reverse shell. Du théâtre. L'oracle est un marqueur __wakeup qui écrit un fichier drapeau.
python3 cakephp-cache-unserialize-Abraxas-Labs.py
ou
cd lab
./run.sh
Cible uniquement 127.0.0.1:19470. CakePHP 5.4.2 live depuis le tag 5.4.2 dans Docker. Redis 7 avec protected-mode désactivé pour que l'hôte puisse SET. PHP 8.3 avec ext-redis. Classe marqueur LabMarker avec __wakeup qui écrit /tmp/CAKEPHP-CACHE-UNSERIALIZE-WITNESS. Pas de 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
Mettre allowedClasses de Redis par défaut à false, ou à une liste restreinte de classes que l'application stocke réellement. Ajouter la même option à FileEngine et l'honorer dans get(). Mettre serialize de Memcached par défaut à json. Documenter que CacheSession hérite de la politique d'unserialize du moteur de cache.
887b641de9f7aba06071f98f17edcbc573cbcf63)RedisEngine.php · FileEngine.phpGNU Affero GPL v3.0. Voir LICENSE. Lab en loopback uniquement. Aucune garantie.