Zend Framework background

An untrusted deserialization vulnerability disclosed this week in Zend Framework can be exploited by attackers to achieve remote code execution on PHP sites.

This vulnerability tracked as CVE-2021-3007 may also impact some instances of Laminas Project, Zend’s successor.

Zend Framework consists of PHP packages installed over 570 million times. The framework is used by developers to build object-oriented web applications.

From untrusted deserialization to RCE

This week, security researcher Ling Yizhou has disclosed an untrusted deserialization vulnerability in Zend Framework 3.0.0.

If exploited, the flaw can allow remote attackers to conduct remote code execution (RCE) attacks on vulnerable PHP applications under certain circumstances. 

“Zend Framework 3.0.0 has a deserialization vulnerability that can lead to remote code execution if the content is controllable, related to the __destruct method of the ZendHttpResponseStream class in Stream.php,” states MITRE’s advisory for CVE-2021-3007.

Untrusted deserialization vulnerabilities occur in applications when encoded data being received by the application from a user or a system is not properly validated before it is decoded by the application.

A vulnerable application may deserialize and process the received data of an improper format, which can have consequences ranging from application crashes (Denial of Service) to the attacker being able to run arbitrary commands in the context of the application.

In the case of Zend, the vulnerability stems from the destructor of the Stream class.

In object-oriented programming, constructors and destructors are methods that are respectively called when a new class object is created and destroyed.

For example, a newly created Stream object, in this case, would run a series of commands at its conception via the constructor.

Once the object has served its purpose throughout the program execution workflow, the PHP interpreter will eventually call the object’s destructor and follow another sequence of commands to free up memory, perform cleanup tasks and delete any temporary files, as a good practice.

Yizhou points out the unlink() method called by Stream’s destructor for deleting a file expects a filename as a parameter, which is of the string data type. 

Zend Framework CVE-2021-3007 vulnerable destructor
Vulnerable destructor in Zend Framework and Laminas Project
Source: GitHub

In effect, should the streamName object be of a non-string type, at the end of the application execution it would still get passed to the destructor.

The destructor, which only expects a string value would therefore attempt to call the object’s __toString method, to get its string-equivalent value. 

But, the __toString method can be easily customized by the creator of the object, or rather the creator of the class that the object instantiates.

As an example, Yizhou highlighted the __toString method in the Gravatar class of Zend Framework had been written by its programmers in such a way that it eventually returned values that the attacker had direct control over, to execute arbitrary code.

This means, should the Stream class be passed a Gravator object where streamName is expected, under certain circumstances, the threat actor could run arbitrary commands within vulnerable PHP applications built with Zend. 

The researcher demonstrated at least 2 scenarios in which it was possible to pass serialized objects to Zend, which when parsed by the PHP application would render the output of attacker’s the commands on the rendered webpages.

In a proof-of-concept (PoC) exploit, the researcher demonstrated how the web app’s phpinfo page successfully parsed his system command “whoami” passed through a serialized HTTP request, and returned the Windows account name, “nt authoritysystem.” 

whoami command output zend framework vulnerability
Researcher ran “whoami” command successfully in a demo PoC and obtained “nt authoritysystem” output
Source: Yizhou

Apps built with Laminas may also be impacted

In January 2020, Zend Framework was migrated to Laminas project with a significant amount of code simply having been relocated to the newer codebase.

For example, Zend’s Stream.php class with the aforementioned destructor continues to exist in some versions of Laminas.

“The code may be related to Laminas Project laminas-http. Zend Framework is no longer supported by the maintainer. However, not all Zend Framework 3.0.0 vulnerabilities exist in a Laminas Project release,” states MITRE’s advisory.

While this does not necessarily indicate all applications built with Laminas project are vulnerable, developers are advised to do their due diligence. 

Considering PHP powers about 80% of the internet sites in some capacity, and given the historic popularity of Zend Framework, developers are advised to thoroughly check their web applications for cases of untrusted object deserialization.

Performing thorough security audits of your applications is one way to spot zero-days and vulnerabilities specific to your environment from time to time.