Laravel: Added RCE/19, which targets Laravel versions 5.6 <= 10.x #172
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I would like to add my Laravel gadgets chain to PHPGGC.
Why?
When we search for gadgets chain, we audit the code of all objects in the framework and, more specifically, the code of the following magic methods:
__wakeup()
__destruct()
__toString()
__call()
__get()
The purpose of this pull request is to help noephites like me by clarifying the possibility of using the magic method
_destruct()
as a proxy to reach other magic methods, such as__toString()
,__call()
or__get()
.The chain I've identified uses the following objects:
Illuminate\Routing\PendingResourceRegistration
(serves as a proxy to reachIlluminate\Validation\Rules\RequiredIf::__toString()
)Illuminate\Routing\ResourceRegistrar
(used to triggerIlluminate\Routing\ResourceRegistrar::register()
)Illuminate\Validation\Rules\RequiredIf
(serves as a proxy to reachcall_user_func()
whose first argument is controlled)Illuminate\Auth\RequestGuard
(final call tocall_user_func()
whose all arguments are controlled)When an object is destroyed, its magic method
__destruct()
is called by default.File: src/Illuminate/Routing/PendingResourceRegistration.php
Class:
PendingResourceRegistration
Functions:
__destruct()
,register()
We can see that
$this->registrar
must at least be defined and be an object of classResourceRegistrar
in order to call its functionregister()
. Moreover, it is clear that we control all the parameters of the functionregister()
($this->name
,$this->controller
,$this->options
).File: src/Illuminate/Routing/ResourceRegistrar.php
Class:
ResourceRegistrar
Function:
register()
Function call
Str::contains()
triggers function__toString()
from$name
which we defined as anIlluminate\Validation\Rules\RequiredIf
object.File: src/Illuminate/Validation/Rules/RequiredIf.php
Class:
PendingResourceRegistration
Function:
__toString()
When we look at the function
call_user_func()
, we realize that we can pass it an array as first parameter (as shown in the example below from the PHP official documentation).File: Example #4 Using a class method with call_user_func()
So we need an object which, when we call one of its methods without parameters, allows us to obtain code execution.
File: src/Illuminate/Auth/RequestGuard.php
Class:
RequestGuard
Function:
user()
We can take a look at the function
Illuminate\Auth\GuardHelpers::getProvider()
to ensure that we control all the parameters of this last call tocall_user_func()
.File: src/Illuminate/Auth/GuardHelpers.php
Class:
GuardHelpers
Function:
getProvider()
All we have to do now is to implement it within PHPGGC:
Now we can generate our new gadgets chain with PHPGGC:
In the end, we just look at the Laravel code for different versions to determine which version of Laravel is exploitable with this gadgets chain.
Illuminate\Auth\RequestGuard::user()
Illuminate\Validation\Rules\RequiredIf::__toString()
Illuminate\Routing\ResourceRegistrar::register()
Illuminate\Routing\PendingResourceRegistration::__destruct()