Fenrisk is a security company specialized in offensive assessments (penetration tests, large scale engagements), security audits and trainings. It helps you secure your assets. Fenrisk strives to issue impactful security assessments.
As we have seen in the previous article https://fenrisk.com/publications/blogpost/2023/11/22/gadgets-chain-in-wordpress/, very simple gadgets chains can be found in major projects. But sometimes finding popchain may be more difficult. This article presents another pretty interesting gadgets 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 his 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 found 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 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 to do it:
This class implements a __destruct method that calls copy() of the object stored in the duration class attribute. Calling unserialize on a serialized instance of Sleep class built with an instance of InvokableComponentVariable class as duration attribute will make the job.
In other words, leveraging this destruct method will make a call to InvokableComponentVariable->copy() which does not exists. Consequently, it will trigger the execution of __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 following:
This behaviour 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 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 behaviour 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 interresting 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 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 command 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 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 gadgets chain is shown below:
This exploitation vector is now available in the tool phpggc 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 is the co-founder of Fenrisk as well as one of its security experts. He's mostly involved in Web and applications security. After 10 years working in French Department of Defense and 3 years in security consulting in Paris, Maxime decided to join Julien to develop together their vision of security.