Advisory 02/2009: PHPIDS unserialize() Vulnerability

                         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.de/en/advisories/advisory-022009-phpids-unserialize-vulnerability/index.html
               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.