|
Gadget chains in Laravel |
As we have seen in the previous article https://fenrisk.com/Gadgets-chain-in-Wordpress/, very simple gadget chains can be found in major projects. But sometimes finding popchain may be more difficult. This article presents another pretty interesting gadget chain we found in the Laravel project on version 10.34 that leads to remote command execution.
Serializing is a process that generates a storage representation of an object state. Unserializing is the opposite process, generating an object from its serialized representation. If an object implements magic methods, PHP will call an implicit execution of these methods depending on the method specificity.
During our research in the source code of Laravel, we found a class, InvokableComponentVariable, that implements an __invoke magic method as shown below:

This __invoke method executes call_user_func on a class attribute callable that could be controlled during the unserializing process. However, the __invoke magic method is implicitly called when the object is handled as a function, and we did not find any way to trigger this method implicitly. We noticed that the __call magic method executes the __invoke and we tried to find a way to build a popchain around this portion of code.
To be able to exploit this code, we need to find a magic method implicitly called during the unserializing process that allows us to trigger the __call method of our desired class. We targeted three candidates: __destruct, __wakeup and __unserialize (called by PHP > 7.4)
The __call magic method is implicitly executed when invoking an inaccessible instance method of an object. We found that the Sleep class allows us to do it:

This class implements a __destruct method that calls copy() on the object stored in the duration class attribute. Calling unserialize on a serialized instance of the Sleep class built with an instance of InvokableComponentVariable class as duration attribute will do the job.
In other words, leveraging this destruct method will make a call to InvokableComponentVariable->copy() which does not exist. Consequently, it will trigger the execution of the __call magic method required to reach our __invoke destination.
Thus, we are able to execute call_user_func on a single controlled parameter. For instance, it's already possible to execute a phpinfo as follows:

This behavior is pretty interesting; we are able to call an arbitrary method, but it does not allow us to execute arbitrary commands on the system because we are not able to control the parameters of the function being called.
Luckily, call_user_func is a funny PHP function. It offers multiple ways to call it. For instance, it also accepts its parameter to be an Array. The first position requires an instance of a callable, and the second, the method we want to call on this later object:
But call_user_func also accepts as callback the representation of the namespace and the class name of the targeted method:
This behavior allows us to call any callable method of loaded classes using the namespace representation NAMESPACE\Class::Method() or the Array representation [Callable, 'method']. However, the only restriction is that we need to find an object with an interesting method, but we can't provide it any parameter.
To be able to transform this popchain that allows us to call an arbitrary method without parameters to a popchain that executes arbitrary code during the unserializing process, we need to find a method doing vulnerable stuff on controlled input, such as class attributes or on php://input for example.
After a quick search, we found the Terminal class of Laravel\Prompts namespace that implements two interesting methods. The first one, exec, executes system commands using proc_open with the parameter command:

As we were just saying, we are not able to control the method parameter; however, the Terminal class also implements a restoreTty method that calls exec instance method using the instance attribute initialTtyMode without any sanitization process:

We are now able to build a popchain that triggers, during the unserializing process, a system call with a command injection. We just need to build the popchain, calling, with call_user_func, an instance of Terminal class with an initialTtyMode breaking the legitimate command and injecting a rogue system command such as ;mycommand;#. The final gadget chain is shown below:

This exploitation vector is now available in the phpggc tool as Laravel/RCE19 and was tested on Laravel version 10.34. It was a pretty fun pop chain to dig through, as it uses much less common primitives such as __call.
Maxime Rinaudo est le co-fondateur de Fenrisk ainsi que l'un de ses experts en sécurité. Il est également passionné par la sécurité des applications web. Après avoir travaillé dix années au sein du Ministère des armées et 3 ans en tant que consultant à Paris, Maxime à décidé de rejoindre Julien dans son aventure afin de partager leur vision de la sécurité offensive.