内容简介:One week before Christmas 2017, I received an email with the following subject: "A file from Sebastian Bergmann PHP Unit leads to total failure" (sic).The sender of this email was the employee responsible for the operation of the online shop at a jeweler.
One week before Christmas 2017, I received an email with the following subject: "A file from Sebastian Bergmann PHP Unit leads to total failure" (sic).
The sender of this email was the employee responsible for the operation of the online shop at a jeweler. In his e-mail he wrote, among other things, "[our hosting provider] closed our domain in the middle of Christmas sales" and further "a lot of business is lost here".
What happened? Is PHPUnit a security risk? Did I even try to hack the jeweler's online shop? In this article I would like to investigate these questions.
From the email I was able to conclude that the jeweler's online shop is based on Wordpress as well as the WooCommerce plugin for Wordpress and the Google Product Feed plugin for WooCommerce. One or more of these components was delivered together with (an outdated version of) PHPUnit, which contained the file eval-stdin.php
. In the course of an automated scan for files that contain known security vulnerabilities such as CVE-2017-9841 , the jeweler's hosting provider discovered the file eval-stdin.php
, took the jeweler's host offline, and then informed the person responsible about this measure.
The jeweler's employee responsible for the online shop looked at the contents of the file eval-stdin.php
. After all, the hosting provider had explicitly referred to this file. In this file he found the copyright header "This file is part of PHPUnit. (C) Sebastian Bergmann <sebastian@phpunit.de>" Then he wrote me his email. Believing that I was responsible for the file being on his web server.
The story of eval-stdin.php
The file eval-stdin.php
was added to PHPUnit in November 2015 in order to be able to run tests in separate PHP processes even if the PHP debugger phpdbg
is used instead of the regular command line interpreter ( php
).
eval-stdin.php
originally only contained a single line of PHP code:
1 |
eval ( '?>' . \ file_get_contents ( 'php://input' ) ) ; |
If the line of code shown above is executed by the PHP command line interpreter, it does exactly what you may have already guessed by reading the file's name: it executes PHP code that is read from the standard data stream for input ( stdin
). This execution of code that is read from stdin
does not pose a security risk when PHPUnit is used in a development environment on the command line. And only there should PHPUnit be executed.
The PHPUnit documentation is clear on this:
PHPUnit is a framework for writing as well as a commandline tool for running tests. Writing and running tests is a development-time activity. There is no reason why PHPUnit should be installed on a webserver. If you upload PHPUnit to a webserver then your deployment process is broken. On a more general note, if your vendor directory is publicly accessible on your webserver then your deployment process is also broken. Please note that if you upload PHPUnit to a webserver “bad things” may happen. You have been warned.
If you make eval-stdin.php
publicly accessible on a web server, this file can be used for a Remote Code Execution attack, since in this context php://input
provides access to, for example, HTTP POST payload data that is sent from the HTTP client to the web server. On June 27, 2017, the entry CVE-2017-9841 for this attack vector was added to the Common Vulnerabilities and Exposures database.
But how can CVE-2017-9841 be exploited?
First we create a directory in which we work:
$ mkdir /tmp/CVE-2017-9841 $ cd /tmp/CVE-2017-9841
Now we install PHPUnit 5.6.2 using Composer:
$ composer require --dev phpunit/phpunit:5.6.2
PHPUnit 5.6.2, released on October 25, 2016 , is the last version of PHPUnit which accesses php://input
in eval-stdin.php
.
Now we can use the web server built into the PHP command line interpreter to make the contents of our directory accessible via localhost:8080
:
$ php -S localhost:8080 -t .
In another shell, we can finally send an HTTP POST request with PHP code as a payload:
$ curl --data "<?php print str_rot13('V pna erzbgryl rkrphgr CUC pbqr ba lbhe freire');" http://localhost:8080/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
The output we get proves that remote code execution is possible:
I can remotely execute PHP code on your server
On November 13, 2016 the single line of code in eval-stdin.php
was changed:
1 |
eval ( '?>' . \ file_get_contents ( 'php://stdin' ) ) ; |
The change from php://input
to php://stdin
addressed a problem that is not relevant from a security perspective: tests that are supposed to be run in a separate PHP process were not run at all. But just because a code change is not motivated by a security problem does not mean that it does not have an impact on security aspects.
On December 10, 2019 I added an explicit safeguard to the script which allows the execution of eval-stdin.php
only in the context of the command line:
1 2 3 4 5 |
if ( \ PHP_SAPI !== 'cli' && \ PHP_SAPI !== 'phpdbg' ) { exit ( 1 ) ; } eval ( '?>' . \ file_get_contents ( 'php://stdin' ) ) ; |
I did not make this change because I was aware of a way to exploit the code as it was before this change for remote code execution. I only learned of this possibility later in a comment on the commit that changed the code as shown above.
The php://input
stream allows access to an HTTP post request's payload regardless of how PHP is integrated with the web server. It does not matter whether PHP is loaded as a module in the Apache HTTPD or used by a web server such as nginx via FastCGI, for example.
An HTTP post payload can only be accessed via the php://stdin
stream if PHP is used by the web server via CGI or FastCGI. I was not sure if php://stdin
really behaves like this, so I reached out to PHP core developers. Joe Watkins and Christoph M. Becker were able to confirm that php://stdin
behaves like this and that its implementation is based on the specifications for CGI and FastCGI, which mandate access to the request payload via the standard input stream.
In retrospect, you are always smarter and it would probably have made sense to limit the execution of eval-stdin.php
to cli
and phpdbg
from the start. However, such a limitation should not be necessary at all, since there is no reason to run PHPUnit outside the context of the development environment and command line. Rather, it is irresponsible if PHPUnit is available in contexts other than those mentioned.
Late Consequences
I was contacted by the vendor of PrestaShop, an Open Source E-Commerce software, on January 6, 2020. They informed me that eval-stdin.php
can be exploited for remote code execution when PHPUnit is publicly available on the web server and FastCGI is used to integrate PHP with that web server.
On January 7, 2020, a critical vulnerability in PrestaShop was made public. The root cause for this security vulnerability was the fact that PrestaShop was distributed with PHPUnit and therefore contained the eval-stdin.php
script.
I investigated what it would take to remove eval-stdin.php
from PHPUnit once and for all. I was surprised to learn that the file had not been used since July 2018 .
It irks me that I did not notice that eval-stdin.php
can be deleted back in July 2018. As of PHPUnit 7.5.20 and PHPUnit 8.5.2 , released on January 8, 2020, the file eval-stdin.php
is finally no longer a part of PHPUnit.
And the moral of this story
Regardless of how PHP is integrated with a web server, for instance as a module for Apache HTTPD or via PHP-FPM with nginx, there are two important rules to be followed when deploying PHP applications:
vendor
I feel pity for everyone who has installed a version of PHPUnit with a vulnerable eval-stdin.php
file on their web server by installing PrestaShop or WooCommerce, for example.
However, I find it irresponsible and reckless if the vendors of such standard solutions, which have end users such as the jeweler mentioned at the beginning as their target audience, deliver their software with dependencies such as PHPUnit in their vendor
directory and prompt the users to upload them to the document root of a public webserver.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。