security: restrict allowed_classes in ClosureJob::run() to prevent PHP Object Injection#41568
Conversation
…P 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.
|
Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a changelog item based on your changes. |
|
|
|
I have read the CLA Document and I hereby sign the CLA |
you need to follow the link to cla-assistent.io above and sign there .... |
Code ReviewOverviewThis PR fixes a PHP Object Injection vulnerability in CorrectnessThe fix is correct for The Security AssessmentThe fix is insufficient as a complete remediation — the same vulnerability exists in sibling files that were not addressed:
This PR fixes one of four vulnerable call sites. The PR description does not acknowledge the others. Completeness / Test Coverage
Code Style
Suggestions
SummaryThe fix itself is correct and safe for |
|
Thanks for the thorough review — really appreciate the level of detail. You're right that the fix is incomplete as submitted. I'll expand the PR to cover For On tests — agreed, a regression test covering both the happy path and the blocked-class case should be part of a security fix. I'll add that alongside the expanded fix. On the CLA — understood, I'll follow the link to cla-assistant.io and sign there directly. I'll push an updated commit addressing all of the above. |
|
Signed the CLA via cla-assistant.io directly — should reflect now. Expanded the fix to cover |
that did not work out
did you forget to push - I see no change in this pr. Thank you! |
Summary
ClosureJob::run()calls\unserialize($serializedCallable)without anallowed_classesrestriction. The serialized job payload is stored in the database; an attacker who can write to the job table (e.g. via SQL injection or a compromised admin account) can inject a PHP Object Injection payload that triggers a gadget chain during deserialization, potentially leading to Remote Code Execution.Root Cause
AsyncBus::push()always wraps closures in aLaravel\SerializableClosure\SerializableClosure(line 115 ofAsyncBus.php). The class that should appear after deserialization is alwaysSerializableClosure— no other class is legitimate here.Fix
This prevents instantiation of arbitrary gadget classes during deserialization without changing behaviour for any legitimate job payload.
Impact
unserialize()→ gadget chain → RCENo Behaviour Change
All legitimate
ClosureJobentries are serializedSerializableClosureobjects; restricting to exactly that class does not affect them.