Mon. Nov 18th, 2019

CVE-2019-11043: PHP-FPM arbitrary code execution vulnerability alert

2 min read

Recently, PHP officially reveals a security vulnerability (CVE-2019-11043) that caused remote code execution in the case of improper nginx configuration. In certain nginx + php-fpm configurations, the bug is possible to trigger from the outside. This means that a web user may get code execution if you have vulnerable config. The vulnerability level is medium-risk and the impact is limited.

Vulnerability Detail

The line 1140 in file sapi/fpm/fpm/fpm_main.c (https://github.com/php/php-src/blob/master/sapi/fpm/fpm/fpm_main.c#L1140) contains pointer arithmetics that assumes that env_path_info has a prefix equal to the path to the php script. However, the code does not check this assumption is satisfied. The absence of the check can lead to an invalid pointer in the “path_info” variable.

Such conditions can be achieved in a pretty standard Nginx configuration. If one has Nginx config like this:

location ~ [^/]\.php(/|$) {

fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;

}

The regexp in `fastcgi_split_path_info` directive can be broken using the newline character (in encoded form, %0a). Broken regexp leads to empty PATH_INFO, which triggers the bug.

This issue leads to code execution. Later in the code, the value of path_info[0] is set to zero (https://github.com/php/php-src/blob/master/sapi/fpm/fpm/fpm_main.c#L1150); then FCGI_PUTENV is called. Using a carefully chosen length of the URL path and query string, an attacker can make path_info point precisely to the first byte of _fcgi_data_seg structure. Putting zero into it moves `char* pos` field backwards, and following FCGI_PUTENV overwrites some data (including other fast cgi variables) with the script path. Using this technique, I was able to create a fake PHP_VALUE fcgi variable and then use a chain of carefully chosen config values to get code execution.

The PoC is available on Github.

Solution

  1. Modify the regular expression of fastcgi_split_path_info in nginx configuration file, do not allow non-displayable characters after .php
  2. Suspend the use of the nginx+php-fpm service
  3. Delete the following configuration
     Fastcgi_split_path_info ^(.+?\.php)(/.*)$;
     Fastcgi_param PATH_INFO $fastcgi_path_info;