Advertisement
AndrewHaxalot

PHP openssl_x509_parse() Memory Corruption Vulnerability

Dec 19th, 2013
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 11.09 KB | None | 0 0
  1. SektionEins GmbH
  2.                         www.sektioneins.de
  3.  
  4.                      -= Security  Advisory =-
  5.  
  6.      Advisory: PHP openssl_x509_parse() Memory Corruption Vulnerability
  7.  Release Date: 2013/12/13
  8. Last Modified: 2013/12/13
  9.        Author: Stefan Esser [stefan.esser[at]sektioneins.de]
  10.  
  11.   Application: PHP 4.0.6 - PHP 4.4.9
  12.                PHP 5.0.x
  13.                PHP 5.1.x
  14.                PHP 5.2.x
  15.                PHP 5.3.0 - PHP 5.3.27
  16.                PHP 5.4.0 - PHP 5.4.22
  17.                PHP 5.5.0 - PHP 5.5.6
  18.      Severity: PHP applications using openssl_x509_parse() to parse a
  19.                malicious x509 certificate might trigger a memory
  20.                corruption that might result in arbitrary code execution
  21.          Risk: Critical
  22. Vendor Status: Vendor has released PHP 5.5.7, PHP 5.4.23 and PHP 5.3.28
  23.                that contain a fix for this vulnerability
  24.     Reference:
  25. http://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
  26.  
  27. Overview:
  28.  
  29.   Quote from http://www.php.net
  30.   "PHP is a widely-used general-purpose scripting language that
  31.   is especially suited for Web development and can be embedded
  32.   into HTML."
  33.  
  34.   The PHP function openssl_x509_parse() uses a helper function
  35.   called asn1_time_to_time_t() to convert timestamps from ASN1
  36.   string format into integer timestamp values. The parser within
  37.   this helper function is not binary safe and can therefore be
  38.   tricked to write up to five NUL bytes outside of an allocated
  39.   buffer.
  40.  
  41.   This problem can be triggered by x509 certificates that contain
  42.   NUL bytes in their notBefore and notAfter timestamp fields and
  43.   leads to a memory corruption that might result in arbitrary
  44.   code execution.
  45.  
  46.   Depending on how openssl_x509_parse() is used within a PHP
  47.   application the attack requires either a malicious cert signed
  48.   by a compromised/malicious CA or can be carried out with a
  49.   self-signed cert.
  50.  
  51. Details:
  52.  
  53.   The PHP function openssl_x509_parse() is used by PHP applications
  54.   to parse additional information out of x509 certificates, usually
  55.   to harden SSL encrypted communication channels against MITM
  56.   attacks. In the wild we have seen the following use cases for this
  57.   function:
  58.  
  59.    * output certificate debugging information
  60.      (e.g. cacert.org/analyse.php)
  61.    * webmail application with SMIME support
  62.    * client certificate handling
  63.    * certificate pinning
  64.    * verification of other certificate properties
  65.      (e.g. a default Wordpress install if ext/curl is not loaded)
  66.      
  67.   When we backported security fixes for some previous security
  68.   vulnerabilities in PHP's openssl to PHP 4.4.9 as part of our
  69.  PHP security backport services that we provide to customers,
  70.  we performed a quick audit of openssl_x509_parse() and all the
  71.  functions it calls, which led to the discovery of a memory
  72.  corruption vulnerability.
  73.  
  74.  Within the function openssl_x509_parse() the helper function
  75.  asn1_time_to_time_t() is called two times to parse the
  76.  notBefore and notAfter ASN1 string timestamps from the cert
  77.  into integer time_t values as you can see below:
  78.  
  79.     add_assoc_long(return_value, "validFrom_time_t",
  80. asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
  81.     add_assoc_long(return_value, "validTo_time_t",
  82. asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
  83.  
  84.  When you take a look into this helper function you will see
  85.  that it only contains a quickly hacked parser that was never
  86.  really improved since its introduction in PHP 4.0.6. The author
  87.  of this parser was even aware of its hackishness as you can see
  88.  from the error message contained in the code:
  89.  
  90.  static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /*
  91. {{{ */
  92.  {
  93.  /*
  94.     This is how the time string is formatted:
  95.     snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
  96.        ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
  97.  */
  98.  
  99.     time_t ret;
  100.     struct tm thetime;
  101.     char * strbuf;
  102.     char * thestr;
  103.     long gmadjust = 0;
  104.  
  105.     if (timestr->length < 13) {
  106.        php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author
  107. too lazy to parse %s correctly", timestr->data);
  108.        return (time_t)-1;
  109.     }
  110.  
  111. However the actual problem of the code should become obvious when
  112. you read the rest of the parsing code that attempts to first
  113. duplicate the timestamp string and then parses the timestamp by
  114. going through the copy in reverse order and writing five NUL bytes
  115. into the duplicated string.
  116.  
  117.     strbuf = estrdup((char *)timestr->data);
  118.  
  119.     memset(&thetime, 0, sizeof(thetime));
  120.  
  121.     /* we work backwards so that we can use atoi more easily */
  122.  
  123.     thestr = strbuf + timestr->length - 3;
  124.  
  125.     thetime.tm_sec = atoi(thestr);
  126.     *thestr = '\0';
  127.     thestr -= 2;
  128.     thetime.tm_min = atoi(thestr);
  129.     *thestr = '\0';
  130.     thestr -= 2;
  131.     thetime.tm_hour = atoi(thestr);
  132.     *thestr = '\0';
  133.     thestr -= 2;
  134.     thetime.tm_mday = atoi(thestr);
  135.     *thestr = '\0';
  136.     thestr -= 2;
  137.     thetime.tm_mon = atoi(thestr)-1;
  138.     *thestr = '\0';
  139.     thestr -= 2;
  140.     thetime.tm_year = atoi(thestr);
  141.  
  142.  The problem with this code is that ASN1 strings can contain NUL
  143.  bytes, while the parser is not binary safe. This means if a
  144.  timestamp string inside a x509 certificate contains a NUL byte
  145.  at e.g. position 13 the estrdup() will only allocate 14 bytes
  146.  for a copy of the string, but the parser will attempt to write
  147.  five NUL bytes to memory addressed by the ASN1 length of the
  148.  string. If the real string length is longer than 16 bytes this
  149.  will result in writes of NUL bytes outside of the allocated
  150.  buffer.
  151.  
  152.  Because of PHP's deterministic heap memory layout that can be
  153.   controlled a lot by sending e.g. POST variables and using
  154.   duplicate variable names to poke memory holes this vulnerability
  155.   must be considered exploitable. However the actual exploit will
  156.   depend a lot on how the PHP application uses openssl_x509_parse()
  157.   and a lot of other factors.
  158.  
  159.   Depending on which of the actual use cases the function is used
  160.   for by an application, an attacker can trigger the memory
  161.   corruption with a self-signed certificate. An example for this
  162.   is the public analyse.php x509 cert debugging script provided
  163.   by CACert on their webserver.
  164.  
  165.   Other applications like Wordpress use openssl_x509_parse() to
  166.   further verify SSL certificates whenever Wordpress connects to
  167.   a HTTPS URL (in case ext/curl is not loaded which is the default
  168.   for several linux distributions). Because the parsing only
  169.   happens after the initial SSL connection is established this
  170.   can only be abused by attackers controlling a malicious trusted
  171.   cert. However recent disclosures of alleged NSA capabilities,
  172.   the French incident and disclosures about fully compromised
  173.   trusted CAs in the past years have shown that this capability
  174.   might be in the reach of malicious attackers.
  175.  
  176.  
  177. Proof of Concept:
  178.  
  179.   The following x509 certificate demonstrates the out of bounds write:
  180.  
  181.   -----BEGIN CERTIFICATE-----
  182.   MIIEpDCCA4ygAwIBAgIJAJzu8r6u6eBcMA0GCSqGSIb3DQEBBQUAMIHDMQswCQYD
  183.   VQQGEwJERTEcMBoGA1UECAwTTm9yZHJoZWluLVdlc3RmYWxlbjEQMA4GA1UEBwwH
  184.   S8ODwrZsbjEUMBIGA1UECgwLU2VrdGlvbkVpbnMxHzAdBgNVBAsMFk1hbGljaW91
  185.   cyBDZXJ0IFNlY3Rpb24xITAfBgNVBAMMGG1hbGljaW91cy5zZWt0aW9uZWlucy5k
  186.   ZTEqMCgGCSqGSIb3DQEJARYbc3RlZmFuLmVzc2VyQHNla3Rpb25laW5zLmRlMHUY
  187.   ZDE5NzAwMTAxMDAwMDAwWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  188.   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  189.   AAAAAAAXDTE0MTEyODExMzkzNVowgcMxCzAJBgNVBAYTAkRFMRwwGgYDVQQIDBNO
  190.   b3JkcmhlaW4tV2VzdGZhbGVuMRAwDgYDVQQHDAdLw4PCtmxuMRQwEgYDVQQKDAtT
  191.   ZWt0aW9uRWluczEfMB0GA1UECwwWTWFsaWNpb3VzIENlcnQgU2VjdGlvbjEhMB8G
  192.   A1UEAwwYbWFsaWNpb3VzLnNla3Rpb25laW5zLmRlMSowKAYJKoZIhvcNAQkBFhtz
  193.   dGVmYW4uZXNzZXJAc2VrdGlvbmVpbnMuZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
  194.   DwAwggEKAoIBAQDDAf3hl7JY0XcFniyEJpSSDqn0OqBr6QP65usJPRt/8PaDoqBu
  195.   wEYT/Na+6fsgPjC0uK9DZgWg2tHWWoanSblAMoz5PH6Z+S4SHRZ7e2dDIjPjdhjh
  196.   0mLg2UMO5yp0V797Ggs9lNt6JRfH81MN2obXWs4NtztLMuD6egqpr8dDbr34aOs8
  197.   pkdui5UawTZksy5pLPHq5cMhFGm06v65CLo0V2Pd9+KAokPrPcN5KLKebz7mLpk6
  198.   SMeEXOKP4idEqxyQ7O7fBuHMedsQhu+prY3si3BUyKfQtP5CZnX2bp0wKHxX12DX
  199.   1nfFIt9DbGvHTcyOuN+nZLPBm3vWxntyIIvVAgMBAAGjQjBAMAkGA1UdEwQCMAAw
  200.   EQYJYIZIAYb4QgEBBAQDAgeAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEF
  201.   BQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAG0fZYYCTbdj1XYc+1SnoaPR+vI8C8CaD
  202.   8+0UYhdnyU4gga0BAcDrY9e94eEAu6ZqycF6FjLqXXdAboppWocr6T6GD1x33Ckl
  203.   VArzG/KxQohGD2JeqkhIMlDomxHO7ka39+Oa8i2vWLVyjU8AZvWMAruHa4EENyG7
  204.   lW2AagaFKFCr9TnXTfrdxGVEbv7KVQ6bdhg5p5SjpWH1+Mq03uR3ZXPBYdyV8319
  205.   o0lVj1KFI2DCL/liWisJRoof+1cR35Ctd0wYBcpB6TZslMcOPl76dwKwJgeJo2Qg
  206.   Zsfmc2vC1/qOlNuNq/0TzzkVGv8ETT3CgaU+UXe4XOVvkccebJn2dg==
  207.   -----END CERTIFICATE-----
  208.  
  209.  
  210. Disclosure Timeline:
  211.  
  212.   01. December 2013 - Notified security@php.net
  213.                       Provided description, POC cert, demo
  214.                       valgrind output and patch
  215.   02. December 2013 - security@php.net acknowledges and
  216.                       says thank you for report and patch
  217.   02. December 2013 - security@php.net announces that planned
  218.                       release date is 12th December
  219.   03. December 2013 - Notification from RedHat Security that
  220.                       CVE-2013-6420 was assigned to this issue
  221.   09. December 2013 - RedHat Security tells php.net that they
  222.                       should commit the fix silently and add
  223.                       info about it only after release
  224.                       They further tell php.net to tell us to
  225.                       not discuss the vulnerability in public
  226.                       prior to patches being available
  227.   10. December 2013 - security@php.net fixes the vulnerability
  228.                       openly and does not attempt to hide that
  229.                       the commit is a security fix as RedHat
  230.                       Security suggested
  231.   11. December 2013 - RedHat Security Announces that they now
  232.                       consider this vulnerability public and
  233.                       sends out their own patches with big
  234.                       announcement one day before php.net is
  235.                       ready to release their own fixes
  236.   12. December 2013 - security@php.net pushes PHP updates to
  237.                       the PHP 5.3, PHP 5.3 and PHP 5.5 branches
  238.                       to the mirros as was previously agreed upon
  239.   13. December 2013 - New PHP releases are announce on php.net
  240.   13. December 2013 - Public Disclosure of this advisory
  241.  
  242.  
  243. Recommendation:
  244.  
  245.   It is recommended to upgrade to the latest version of PHP
  246.   which also fixes additional non security problems reported
  247.   by third parties.
  248.  
  249.   Grab your copy at:
  250.   http://www.php.net/get/php-5.5.7.tar.bz2/from/a/mirror
  251.  
  252.  
  253. CVE Information:
  254.  
  255.   The Common Vulnerabilities and Exposures project (cve.mitre.org) has
  256.   assigned the name CVE-2013-6420 to this vulnerability.
  257.  
  258.  
  259. GPG-Key:
  260.  
  261.   pub   4096R/D6A3FE46 2013-11-06 Stefan Esser
  262.   Key fingerprint = 0A04 AB88 90D2 E67C 3D3D  86E1 AA39 B97F D6A3 FE46
  263.  
  264.  
  265. Copyright 2013 SektionEins GmbH. All rights reserved.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement