265 lines
No EOL
11 KiB
Text
265 lines
No EOL
11 KiB
Text
SektionEins GmbH
|
|
www.sektioneins.de
|
|
|
|
-= Security Advisory =-
|
|
|
|
Advisory: PHP openssl_x509_parse() Memory Corruption Vulnerability
|
|
Release Date: 2013/12/13
|
|
Last Modified: 2013/12/13
|
|
Author: Stefan Esser [stefan.esser[at]sektioneins.de]
|
|
|
|
Application: PHP 4.0.6 - PHP 4.4.9
|
|
PHP 5.0.x
|
|
PHP 5.1.x
|
|
PHP 5.2.x
|
|
PHP 5.3.0 - PHP 5.3.27
|
|
PHP 5.4.0 - PHP 5.4.22
|
|
PHP 5.5.0 - PHP 5.5.6
|
|
Severity: PHP applications using openssl_x509_parse() to parse a
|
|
malicious x509 certificate might trigger a memory
|
|
corruption that might result in arbitrary code execution
|
|
Risk: Critical
|
|
Vendor Status: Vendor has released PHP 5.5.7, PHP 5.4.23 and PHP 5.3.28
|
|
that contain a fix for this vulnerability
|
|
Reference:
|
|
http://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
|
|
|
|
Overview:
|
|
|
|
Quote from http://www.php.net
|
|
"PHP is a widely-used general-purpose scripting language that
|
|
is especially suited for Web development and can be embedded
|
|
into HTML."
|
|
|
|
The PHP function openssl_x509_parse() uses a helper function
|
|
called asn1_time_to_time_t() to convert timestamps from ASN1
|
|
string format into integer timestamp values. The parser within
|
|
this helper function is not binary safe and can therefore be
|
|
tricked to write up to five NUL bytes outside of an allocated
|
|
buffer.
|
|
|
|
This problem can be triggered by x509 certificates that contain
|
|
NUL bytes in their notBefore and notAfter timestamp fields and
|
|
leads to a memory corruption that might result in arbitrary
|
|
code execution.
|
|
|
|
Depending on how openssl_x509_parse() is used within a PHP
|
|
application the attack requires either a malicious cert signed
|
|
by a compromised/malicious CA or can be carried out with a
|
|
self-signed cert.
|
|
|
|
Details:
|
|
|
|
The PHP function openssl_x509_parse() is used by PHP applications
|
|
to parse additional information out of x509 certificates, usually
|
|
to harden SSL encrypted communication channels against MITM
|
|
attacks. In the wild we have seen the following use cases for this
|
|
function:
|
|
|
|
* output certificate debugging information
|
|
(e.g. cacert.org/analyse.php)
|
|
* webmail application with SMIME support
|
|
* client certificate handling
|
|
* certificate pinning
|
|
* verification of other certificate properties
|
|
(e.g. a default Wordpress install if ext/curl is not loaded)
|
|
|
|
When we backported security fixes for some previous security
|
|
vulnerabilities in PHP's openssl to PHP 4.4.9 as part of our
|
|
PHP security backport services that we provide to customers,
|
|
we performed a quick audit of openssl_x509_parse() and all the
|
|
functions it calls, which led to the discovery of a memory
|
|
corruption vulnerability.
|
|
|
|
Within the function openssl_x509_parse() the helper function
|
|
asn1_time_to_time_t() is called two times to parse the
|
|
notBefore and notAfter ASN1 string timestamps from the cert
|
|
into integer time_t values as you can see below:
|
|
|
|
add_assoc_long(return_value, "validFrom_time_t",
|
|
asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC));
|
|
add_assoc_long(return_value, "validTo_time_t",
|
|
asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC));
|
|
|
|
When you take a look into this helper function you will see
|
|
that it only contains a quickly hacked parser that was never
|
|
really improved since its introduction in PHP 4.0.6. The author
|
|
of this parser was even aware of its hackishness as you can see
|
|
from the error message contained in the code:
|
|
|
|
static time_t asn1_time_to_time_t(ASN1_UTCTIME * timestr TSRMLS_DC) /*
|
|
{{{ */
|
|
{
|
|
/*
|
|
This is how the time string is formatted:
|
|
snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
|
|
ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
|
|
*/
|
|
|
|
time_t ret;
|
|
struct tm thetime;
|
|
char * strbuf;
|
|
char * thestr;
|
|
long gmadjust = 0;
|
|
|
|
if (timestr->length < 13) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "extension author
|
|
too lazy to parse %s correctly", timestr->data);
|
|
return (time_t)-1;
|
|
}
|
|
|
|
However the actual problem of the code should become obvious when
|
|
you read the rest of the parsing code that attempts to first
|
|
duplicate the timestamp string and then parses the timestamp by
|
|
going through the copy in reverse order and writing five NUL bytes
|
|
into the duplicated string.
|
|
|
|
strbuf = estrdup((char *)timestr->data);
|
|
|
|
memset(&thetime, 0, sizeof(thetime));
|
|
|
|
/* we work backwards so that we can use atoi more easily */
|
|
|
|
thestr = strbuf + timestr->length - 3;
|
|
|
|
thetime.tm_sec = atoi(thestr);
|
|
*thestr = '\0';
|
|
thestr -= 2;
|
|
thetime.tm_min = atoi(thestr);
|
|
*thestr = '\0';
|
|
thestr -= 2;
|
|
thetime.tm_hour = atoi(thestr);
|
|
*thestr = '\0';
|
|
thestr -= 2;
|
|
thetime.tm_mday = atoi(thestr);
|
|
*thestr = '\0';
|
|
thestr -= 2;
|
|
thetime.tm_mon = atoi(thestr)-1;
|
|
*thestr = '\0';
|
|
thestr -= 2;
|
|
thetime.tm_year = atoi(thestr);
|
|
|
|
The problem with this code is that ASN1 strings can contain NUL
|
|
bytes, while the parser is not binary safe. This means if a
|
|
timestamp string inside a x509 certificate contains a NUL byte
|
|
at e.g. position 13 the estrdup() will only allocate 14 bytes
|
|
for a copy of the string, but the parser will attempt to write
|
|
five NUL bytes to memory addressed by the ASN1 length of the
|
|
string. If the real string length is longer than 16 bytes this
|
|
will result in writes of NUL bytes outside of the allocated
|
|
buffer.
|
|
|
|
Because of PHP's deterministic heap memory layout that can be
|
|
controlled a lot by sending e.g. POST variables and using
|
|
duplicate variable names to poke memory holes this vulnerability
|
|
must be considered exploitable. However the actual exploit will
|
|
depend a lot on how the PHP application uses openssl_x509_parse()
|
|
and a lot of other factors.
|
|
|
|
Depending on which of the actual use cases the function is used
|
|
for by an application, an attacker can trigger the memory
|
|
corruption with a self-signed certificate. An example for this
|
|
is the public analyse.php x509 cert debugging script provided
|
|
by CACert on their webserver.
|
|
|
|
Other applications like Wordpress use openssl_x509_parse() to
|
|
further verify SSL certificates whenever Wordpress connects to
|
|
a HTTPS URL (in case ext/curl is not loaded which is the default
|
|
for several linux distributions). Because the parsing only
|
|
happens after the initial SSL connection is established this
|
|
can only be abused by attackers controlling a malicious trusted
|
|
cert. However recent disclosures of alleged NSA capabilities,
|
|
the French incident and disclosures about fully compromised
|
|
trusted CAs in the past years have shown that this capability
|
|
might be in the reach of malicious attackers.
|
|
|
|
|
|
Proof of Concept:
|
|
|
|
The following x509 certificate demonstrates the out of bounds write:
|
|
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIEpDCCA4ygAwIBAgIJAJzu8r6u6eBcMA0GCSqGSIb3DQEBBQUAMIHDMQswCQYD
|
|
VQQGEwJERTEcMBoGA1UECAwTTm9yZHJoZWluLVdlc3RmYWxlbjEQMA4GA1UEBwwH
|
|
S8ODwrZsbjEUMBIGA1UECgwLU2VrdGlvbkVpbnMxHzAdBgNVBAsMFk1hbGljaW91
|
|
cyBDZXJ0IFNlY3Rpb24xITAfBgNVBAMMGG1hbGljaW91cy5zZWt0aW9uZWlucy5k
|
|
ZTEqMCgGCSqGSIb3DQEJARYbc3RlZmFuLmVzc2VyQHNla3Rpb25laW5zLmRlMHUY
|
|
ZDE5NzAwMTAxMDAwMDAwWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAXDTE0MTEyODExMzkzNVowgcMxCzAJBgNVBAYTAkRFMRwwGgYDVQQIDBNO
|
|
b3JkcmhlaW4tV2VzdGZhbGVuMRAwDgYDVQQHDAdLw4PCtmxuMRQwEgYDVQQKDAtT
|
|
ZWt0aW9uRWluczEfMB0GA1UECwwWTWFsaWNpb3VzIENlcnQgU2VjdGlvbjEhMB8G
|
|
A1UEAwwYbWFsaWNpb3VzLnNla3Rpb25laW5zLmRlMSowKAYJKoZIhvcNAQkBFhtz
|
|
dGVmYW4uZXNzZXJAc2VrdGlvbmVpbnMuZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
|
DwAwggEKAoIBAQDDAf3hl7JY0XcFniyEJpSSDqn0OqBr6QP65usJPRt/8PaDoqBu
|
|
wEYT/Na+6fsgPjC0uK9DZgWg2tHWWoanSblAMoz5PH6Z+S4SHRZ7e2dDIjPjdhjh
|
|
0mLg2UMO5yp0V797Ggs9lNt6JRfH81MN2obXWs4NtztLMuD6egqpr8dDbr34aOs8
|
|
pkdui5UawTZksy5pLPHq5cMhFGm06v65CLo0V2Pd9+KAokPrPcN5KLKebz7mLpk6
|
|
SMeEXOKP4idEqxyQ7O7fBuHMedsQhu+prY3si3BUyKfQtP5CZnX2bp0wKHxX12DX
|
|
1nfFIt9DbGvHTcyOuN+nZLPBm3vWxntyIIvVAgMBAAGjQjBAMAkGA1UdEwQCMAAw
|
|
EQYJYIZIAYb4QgEBBAQDAgeAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEF
|
|
BQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAG0fZYYCTbdj1XYc+1SnoaPR+vI8C8CaD
|
|
8+0UYhdnyU4gga0BAcDrY9e94eEAu6ZqycF6FjLqXXdAboppWocr6T6GD1x33Ckl
|
|
VArzG/KxQohGD2JeqkhIMlDomxHO7ka39+Oa8i2vWLVyjU8AZvWMAruHa4EENyG7
|
|
lW2AagaFKFCr9TnXTfrdxGVEbv7KVQ6bdhg5p5SjpWH1+Mq03uR3ZXPBYdyV8319
|
|
o0lVj1KFI2DCL/liWisJRoof+1cR35Ctd0wYBcpB6TZslMcOPl76dwKwJgeJo2Qg
|
|
Zsfmc2vC1/qOlNuNq/0TzzkVGv8ETT3CgaU+UXe4XOVvkccebJn2dg==
|
|
-----END CERTIFICATE-----
|
|
|
|
|
|
Disclosure Timeline:
|
|
|
|
01. December 2013 - Notified security@php.net
|
|
Provided description, POC cert, demo
|
|
valgrind output and patch
|
|
02. December 2013 - security@php.net acknowledges and
|
|
says thank you for report and patch
|
|
02. December 2013 - security@php.net announces that planned
|
|
release date is 12th December
|
|
03. December 2013 - Notification from RedHat Security that
|
|
CVE-2013-6420 was assigned to this issue
|
|
09. December 2013 - RedHat Security tells php.net that they
|
|
should commit the fix silently and add
|
|
info about it only after release
|
|
They further tell php.net to tell us to
|
|
not discuss the vulnerability in public
|
|
prior to patches being available
|
|
10. December 2013 - security@php.net fixes the vulnerability
|
|
openly and does not attempt to hide that
|
|
the commit is a security fix as RedHat
|
|
Security suggested
|
|
11. December 2013 - RedHat Security Announces that they now
|
|
consider this vulnerability public and
|
|
sends out their own patches with big
|
|
announcement one day before php.net is
|
|
ready to release their own fixes
|
|
12. December 2013 - security@php.net pushes PHP updates to
|
|
the PHP 5.3, PHP 5.3 and PHP 5.5 branches
|
|
to the mirros as was previously agreed upon
|
|
13. December 2013 - New PHP releases are announce on php.net
|
|
13. December 2013 - Public Disclosure of this advisory
|
|
|
|
|
|
Recommendation:
|
|
|
|
It is recommended to upgrade to the latest version of PHP
|
|
which also fixes additional non security problems reported
|
|
by third parties.
|
|
|
|
Grab your copy at:
|
|
http://www.php.net/get/php-5.5.7.tar.bz2/from/a/mirror
|
|
|
|
|
|
CVE Information:
|
|
|
|
The Common Vulnerabilities and Exposures project (cve.mitre.org) has
|
|
assigned the name CVE-2013-6420 to this vulnerability.
|
|
|
|
|
|
GPG-Key:
|
|
|
|
pub 4096R/D6A3FE46 2013-11-06 Stefan Esser
|
|
Key fingerprint = 0A04 AB88 90D2 E67C 3D3D 86E1 AA39 B97F D6A3 FE46
|
|
|
|
|
|
Copyright 2013 SektionEins GmbH. All rights reserved. |