Description
ProcessDriver::run() instantiates an exception class from JSON-decoded subprocess output without validating the class name:
File: src/Illuminate/Concurrency/ProcessDriver.php lines 57-62
$result = json_decode($output, true);
if (! $result['successful']) {
throw new $result['exception']( // Arbitrary class name from JSON
...(! empty(array_filter($result['parameters']))
? $result['parameters']
: [$result['message']])
);
}
The $result['exception'] value comes from the subprocess output. If an attacker can control or tamper with the subprocess stdout (shared /tmp directories, symlink attacks on the PHP binary path, or in multi-tenant environments), they can specify any class name to instantiate.
Additionally, line 65 has an unprotected unserialize():
return [$key => unserialize($result['result'])];
Impact
- Arbitrary class instantiation — any class with side effects in its constructor can be triggered
- Object injection via unserialize — the
$result['result'] value is unserialized without allowed_classes, enabling POP gadget chains
Suggested Fix
- Validate the exception class against a whitelist or ensure it extends
\Throwable
- Add
allowed_classes to the unserialize call
// Fix 1: Validate exception class
if (! $result['successful']) {
$exceptionClass = $result['exception'];
if (! is_subclass_of($exceptionClass, \Throwable::class)) {
throw new \RuntimeException($result['message'] ?? 'Unknown error');
}
throw new $exceptionClass(...);
}
// Fix 2: Restrict unserialize
return [$key => unserialize($result['result'], ['allowed_classes' => false])];
Versions
- Laravel 13.x (current main branch)
Description
ProcessDriver::run()instantiates an exception class from JSON-decoded subprocess output without validating the class name:File:
src/Illuminate/Concurrency/ProcessDriver.phplines 57-62The
$result['exception']value comes from the subprocess output. If an attacker can control or tamper with the subprocess stdout (shared /tmp directories, symlink attacks on the PHP binary path, or in multi-tenant environments), they can specify any class name to instantiate.Additionally, line 65 has an unprotected
unserialize():Impact
$result['result']value is unserialized withoutallowed_classes, enabling POP gadget chainsSuggested Fix
\Throwableallowed_classesto the unserialize callVersions