SektionEins GmbH www.sektioneins.de -= Security Advisory =- Advisory: PHPIDS Unserialize() Vulnerability Release Date: 2009/12/09 Last Modified: 2009/12/09 Author: Stefan Esser [stefan.esser[at]sektioneins.de] Application: PHPIDS <= 0.6.2 Severity: PHPIDS unserializes() user input which allows an attacker to send a carefully crafted cookie that when unserialized can utilize existing classes which e.g. can lead to upload of arbitrary files or execution of arbitrary PHP code in Zend Framework Applications Risk: Critical Vendor Status: PHPIDS 0.6.3.1 was released which fixes this vulnerability Reference: http://www.sektioneins.com/en/advisories/advisory-022009-phpids-unserialize-vulnerability/ http://www.suspekt.org/downloads/RSS09-WebApplicationFirewallBypassesAndPHPExploits.pdf http://www.suspekt.org/downloads/POC2009-ShockingNewsInPHPExploitation.pdf Overview: Quote from http://www.php-ids.org "PHPIDS (PHP-Intrusion Detection System) is a simple to use, well structured, fast and state-of-the-art security layer for your PHP based web application. The IDS neither strips, sanitizes nor filters any malicious input, it simply recognizes when an attacker tries to break your site and reacts in exactly the way you want it to. Based on a set of approved and heavily tested filter rules any attack is given a numerical impact rating which makes it easy to decide what kind of action should follow the hacking attempt. This could range from simple logging to sending out an emergency mail to the development team, displaying a warning message for the attacker or even ending the user’s session." During our research in unserialize() vulnerabilities it was discovered that PHPIDS's centrifuge detection unserializes every piece of user input that looks like being serialized. This allows an attacker to crash the PHP interpreter or to utilize existing classes for attacks. In combination with the classes available in the Zend Framework this results in file upload and PHP code execution vulnerabilities. Taken in consideration the research in interruption vulnerability exploits that was demonstrated by SektionEins at Syscan and Blackhat this vulnerability has to be considered an arbitrary code execution vulnerability. Details: SektionEins recently demonstrated how it is sometimes possible to execute arbitrary PHP code in an application using unserialize() on user supplied data. In detail various exploits were shown that work against all Zend Framework based applications that unserialize() user input. Part of this research was to find popular PHP open source applications that are vulnerable to this. During our search it was discovered that PHPIDS did unserialize() every piece of user input that looked like being seríalized. public static function runCentrifuge($value, IDS_Monitor $monitor = null) { $threshold = 3.49; $unserialized = false; if(preg_match('/^\w:\d+:\{/', $value)) { $unserialized = @unserialize($value); } This will unserialize() any user input supplied to an application using PHPIDS. Therefore an exploit against applications using the Zend Framework is pretty straight forward. When trying to exploit an unserialize() vulnerability in a PHP application the first step is to enumerate the objects that contain __wakeup() or __destruct() methods and read their code to analyze if these methods are doing something interesting. When looking at the Zend Framework one particular class can be found that can be used in an code execution attack. This class is called Zend_Log and contains the following code. public function __destruct() { foreach($this->_writers as $writer) { $writer->shutdown(); } } The Zend_Log destructor iterates through an array which it expects inside the _writers property. Each element of this array is then expected to have a method called shutdown() which is then executed. The next step in creating an exploit is to find classes that contain a shutdown method. The best fitting class is the Zend_Log_Writer_Mail public function shutdown() { // If there are events to mail, use them as message body. Otherwise, // there is no mail to be sent. if (empty($this->_eventsToMail)) { return; } if ($this->_subjectPrependText !== null) { // Tack on the summary of entries per-priority to the subject // line and set it on the Zend_Mail object. $numEntries = $this->_getFormattedNumEntriesPerPriority(); $this->_mail->setSubject( "{$this->_subjectPrependText} ({$numEntries})"); } // Always provide events to mail as plaintext. $this->_mail->setBodyText(implode('', $this->_eventsToMail)); // If a Zend_Layout instance is being used, set its "events" // value to the lines formatted for use with the layout. if ($this->_layout) { // Set the required "messages" value for the layout. Here we // are assuming that the layout is for use with HTML. $this->_layout->events = implode('', $this->_layoutEventsToMail); // If an exception occurs during rendering, convert it to a notice // so we can avoid an exception thrown without a stack frame. try { $this->_mail->setBodyHtml($this->_layout->render()); } catch (Exception $e) { ... } // Finally, send the mail. If an exception occurs, convert ... // warning-level message so we can avoid an exception thrown ... // stack frame. try { $this->_mail->send(); } catch (Exception $e) { ... } } This shutdown method will check if there are events not yet mailed and if there are, it will mail them to the address specified in the Zend_Mail object which has to be within the _mail property. This allows anyone to send out arbitrary spam to arbitrary mail addresses. However there is a more interesting exploitation path hidden that utilizes the HTML rendering. Therefore an attacker has to find a class that contains a render method. The most promising class is Zend_Layout which comes with a render method. public function render($name = null) { if (null === $name) { $name = $this->getLayout(); } if ($this->inflectorEnabled() && (null !== ($inflector = $this->getInflector()))) { $name = $this->_inflector->filter(array('script' => $name)); } ... } The code snippet above does not do much aside from calling the filter method of an object in the _inflector property. Usually this would be an inflector object. However to achieve arbitrary code execution a different object type is used. The best candidate for this is Zend_Filter_PregReplace that can be used to execute arbitrary PHP code with the help of the /e modifier. public function filter($value) { if ($this->_matchPattern == null) { require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception(get_class($this) . ' does not have a valid MatchPattern set.'); } return preg_replace($this->_matchPattern, $this->_replacement, $value); } So to summarize the attack: By sending a single serialized string to any application based on the Zend Framework using PHPIDS it is possible to utilize Zend Frameworks's own objects and execute arbitrary PHP code by supplying the arguments to a preg_replace() function call. Proof of Concept: SektionEins GmbH is not going to release a proof of concept exploit for this vulnerability. Disclosure Timeline: 19. October 2009 - Notified PHPIDS vendor 22. October 2009 - PHPIDS developers released PHPIDS 0.6.3.1 09. December 2009 - Public Disclosure Recommendation: It is recommended to upgrade to the latest version of PHPIDS. Grab your copy at: http://php-ids.org/files/phpids-0.6.3.1.tar.bz2 CVE Information: The Common Vulnerabilities and Exposures project (cve.mitre.org) has not assigned a name to this vulnerability. GPG-Key: pub 1024D/15ABDA78 2004-10-17 Stefan Esser Key fingerprint = 7806 58C8 CFA8 CE4A 1C2C 57DD 4AE1 795E 15AB DA78 Copyright 2009 SektionEins GmbH. All rights reserved.