From 5be2ccd25d0513ef33857ab33ec521ee81fb09ea Mon Sep 17 00:00:00 2001 From: XananasX7 Date: Sun, 31 May 2026 05:25:33 +0000 Subject: [PATCH] security: restrict allowed_classes in ClosureJob::run() to prevent PHP Object Injection ClosureJob::run() calls unserialize($serializedCallable) without an allowed_classes restriction. The serialized closure is stored in the database job queue (oc_jobs / oc_clndr_appt_queue etc.); an attacker who can write to those tables could inject a gadget chain. AsyncBus::push() always serializes closures as Laravel\SerializableClosure objects (see AsyncBus.php line 115). Restricting to [SerializableClosure::class] prevents instantiation of arbitrary gadget classes during deserialization without changing behaviour for legitimate jobs. --- lib/private/Command/ClosureJob.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/private/Command/ClosureJob.php b/lib/private/Command/ClosureJob.php index b0964c503ef4..728df11e7d86 100644 --- a/lib/private/Command/ClosureJob.php +++ b/lib/private/Command/ClosureJob.php @@ -21,11 +21,13 @@ namespace OC\Command; +use Laravel\SerializableClosure\SerializableClosure; use OC\BackgroundJob\QueuedJob; class ClosureJob extends QueuedJob { protected function run($serializedCallable) { - $serializedClosure = \unserialize($serializedCallable); + // Restrict to SerializableClosure to prevent PHP Object Injection via the job queue. + $serializedClosure = \unserialize($serializedCallable, ['allowed_classes' => [SerializableClosure::class]]); if (\method_exists($serializedClosure, 'getClosure')) { $callable = $serializedClosure->getClosure(); if (\is_callable($callable)) {