From 9572b8fdfdd9980323fc2333d016e5537905df7f Mon Sep 17 00:00:00 2001 From: XananasX7 Date: Sun, 31 May 2026 02:46:59 +0000 Subject: [PATCH] security: restrict unserialize() allowed_classes in TaskProcessing cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getAvailableTaskTypes() stores a serialized array of ShapeDescriptor, ShapeEnumValue, and EShapeType values in the distributed cache and reads them back with bare unserialize() — no allowed_classes restriction. An attacker who can write to the distributed cache backend (e.g., via an unauthenticated Redis instance, SSRF to the cache server, or a cache-poisoning vulnerability) can inject a crafted PHP serialized payload containing a gadget chain and achieve Remote Code Execution when getAvailableTaskTypes() is next called. Restrict allowed_classes to the three value-object types actually stored in this cache entry (ShapeDescriptor, ShapeEnumValue, EShapeType). Any other class in the serialized string will become a harmless __PHP_Incomplete_Class without executing constructors or magic methods. --- lib/private/TaskProcessing/Manager.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 6821aee65a002..dc6a2cec0865b 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -939,7 +939,17 @@ public function getAvailableTaskTypes(bool $showDisabled = false, ?string $userI if ($this->availableTaskTypes === null) { $cachedValue = $this->distributedCache->get($cacheKey); if ($cachedValue !== null) { - $this->availableTaskTypes = unserialize($cachedValue); + // Restrict deserialization to the exact value-object classes stored in + // this cache entry. Bare unserialize() would allow an attacker who + // can write to the distributed cache backend to trigger PHP Object + // Injection via arbitrary gadget chains loaded in the process. + $this->availableTaskTypes = unserialize($cachedValue, [ + 'allowed_classes' => [ + ShapeDescriptor::class, + ShapeEnumValue::class, + EShapeType::class, + ], + ]); } } // Either we have no cache or showDisabled is turned on, which we don't want to cache, ever.