CVE-2015-9253
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its FastCGI Process Manager (php-fpm) runs a master process that starts pools of child processes, and the children accept web server connections and execute PHP scripts.
A Denial of Service (DoS) vulnerability (CVE-2015-9253) has been identified in the php-fpm server API, which allows a script running in any php-fpm pool to make every child of the master fail as soon as it starts, so the master restarts children in an endless loop, uses all available CPU and fills the error log. PHP tracks this issue as bug #73342.
Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others. If a service receives a very large number of requests, it may cease to be available to legitimate users. In the same way, a service may stop if a programming vulnerability is exploited, or the way the service handles resources it uses.
This issue affects PHP before 7.1.20 and PHP 7.2 before 7.2.8, which includes the End-of-Life PHP 5.6 release line through its final release (5.6.40).
Details
Module Info
- Product: PHP
- Affected packages:
php-src(sapi/fpm) - Affected versions: <7.1.20, >=7.2.0 <7.2.8
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 7.1.20, 7.2.8
- NES for PHP 5.6.1 (based on PHP 5.6.40)
Vulnerability Info
This Medium-severity vulnerability is found in the php-fpm server API of PHP before 7.1.20 and PHP 7.2 before 7.2.8, including the End-of-Life PHP 5.6 release line, which never received the upstream fix. The CVE record, assigned by MITRE, carries no CVSS score, and CISA-ADP gives none either; NVD scores it 6.5 under CVSS 3.0 (CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H).
When php-fpm starts a child process for a pool, the child sets up its standard streams before it begins serving requests. In the affected versions, the child copied its pool's listening socket onto its standard input and then accepted FastCGI connections on that descriptor. In PHP 7.1.19, fpm_stdio_init_child() ends with:
if (wp->listening_socket != STDIN_FILENO) {
if (0 > dup2(wp->listening_socket, STDIN_FILENO)) {
zlog(ZLOG_SYSERROR, "failed to init child stdio: dup2()");
return -1;
}
}
return 0;Because the listening socket was also the script's standard input, a PHP script could change its flags through php://stdin. A script that switched php://stdin to non-blocking mode, directly or through a program it started with a program execution function such as exec(), system(), passthru() or shell_exec(), made the socket non-blocking for every child sharing it. Each child's accept() then failed immediately when no connection was waiting, the child exited, and the master started a replacement that failed the same way. The CVE record describes the result as the master process consuming 100% of the CPU and filling disk space with error log entries.
Exploitation needs the ability to run a PHP script, or a program started from one, inside a php-fpm pool. The main concern is shared hosting, where one customer can use this to stop PHP service for their own pool and, through the master's restart loop, degrade service for the other pools on the same php-fpm master; the CVE record notes an attack of this kind by a customer of a shared-hosting provider. HeroDevs' analysis confirms that only php-fpm is affected; other server APIs are not, and OPcache does not change reachability.
The fix stops copying the listening socket onto standard input, which stays connected to /dev/null, and gives each child its own duplicate of the listening socket to accept connections on:
fpm_globals.max_requests = wp->config->pm_max_requests;
fpm_globals.listening_socket = dup(wp->listening_socket);Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6 and 7.0 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, run code from untrusted users under its own php-fpm master instead of one shared with other sites, so a restart loop it causes cannot take down their pools.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 7.1.20, 7.2.8, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- xuavis (finder)
- Nikita Popov (remediation)