November 9, 2020 (updated September 19, 2023)

At the end of August, a critical security bug was discovered and immediately fixed in one of the popular PHP frameworks, Nette. Although the author of the framework, David Grudl, did everything possible, some did not learn about the bug in time and did not update their sites and web apps. Let me tell you a few tips not only for PHP, that will help you to know about similar problems as soon as possible.

The bug, discovered by Cyku Hong from Taiwan, can, under certain circumstances when using a specially crafted URL, lead to remote code execution (RCE). Such bugs are always fun, except when you run the site.

David Grudl, the creator of the framework, has done everything correctly to address the issue: due to the high severity of the bug, new versions were released “silently” first, including updates to versions that are no longer supported for 6 years already, an announcement was sent to supporters and only after more than a month, information was published in a blog post, on a forum and in the Common Vulnerabilities and Exposures list under CVE-2020-15227. (David has also asked me if he wasn't sure about something 🤓)

Patched versions as listed in the blog post, some very old and otherwise not supported

On a massive scale and without Composer

A week after the information about the bug has been published, not only testing tools and PoC (Proof-of-Concept) exploits have started appearing but also developers that previously had no idea that such bug exists and has been already fixed. After noticing this, I realized that maybe, a simple script is needed that would find vulnerable files on the server's drive and possibly fix them by replacing one line of code with another.

The intended users of the script would be companies offering shared hosting: they would notify their particular customers, or even resolve the issue for them. I've created two shell scripts (they both run in Bash in Linux & FreeBSD), David has followed up with a pure PHP implementation. And just like that, Lukáš V. has even donated $10 for those scripts, thanks!

Here's Active24 tweeting (in Czech) they've found and patched 2500+ potentially vulnerable sites using my script, similar to what Blueboard, WEDOS and some others have said

This alone might give you some idea what to do when a critical security bug is found in your framework, package, or library. Equally important is to notify the users of your code about the issue and the need to update their sites. But how can they learn about the bug as soon as possible?

Support and follow the developers

Here are some options for you to learn that you need to update the library or a framework you use, like in the Nette Framework case, and this is probably the most important one.

Support the developers of the tools you use. Not only for a good feeling but the developers will also have a way and an address to email you to let you know you should update, and the money you donate. Win-win situation. When we've started with the Nette bug, yes, I do support Nette personally and I'm sort of easy to spot in the list of backers. You'll find Scott Helme there as well because we also use Nette on Report URI, which I happen to work on with him. The author of the framework sent an update advisory to the supporters already on August 25, just a few hours after the fixed versions became available, and more than a month before he went public with the bug.

Follow the developers on Twitter, follow their blogs & forums (see the Nette blog post and a forum post). If the developers are using GitHub, you can watch just new releases or security alerts and GitHub will send you a notification in case there's one:

You can watch new releases or security alerts only (list of what you're watching)

GitHub Dependabot

If you're using GitHub to host your repositories, and Composer to manage your dependencies, I suggest you learn about Dependabot – a robot that helps you to keep your dependencies up-to-date. Dependabot knows about PHP and handful of other languages and package managers. You can set it up in your repository, in Settings > Security & analysis:

Explore the Security > Dependabot alerts section as well, it lists all open and closed alerts created by Dependabot:

I've put this alert away by clicking Dismiss, the code is not running anywhere

The section also contains links to automatically created pull requests, see the example. This abandoned project is not deployed anywhere, so nette/application doesn't really need to be updated here. I want to leave the pull request open and not merged so you can see how a Dependabot-created pull request would look like in your repository.

Automatically created pull request with a dependency update

Dependabot can also check your GitHub actions, see my config, an auto-created pull request to update one such action, and documentation.

If you'd ever need to get a CVE number for your own code to get the information about a vulnerability out, you'll find the Security Advisories section useful. The number of the Nette bug, CVE-2020-15227, is also listed in all published advisories for nette/applica­tion. There's a similar record in the all GitHub advisories database.

GitLab also offers dependency scanning but it's included only in the most expensive plan. Other Dependabot alternatives are for example Snyk or Renovate.

Roave Security Advisories

The next tool that can help you discover outdated PHP libraries with security issues is Roave Security Advisories package. Let's try to install it, we'll use my old and unused project again. First, let's install the dependencies listed in composer.lock, whereas it would more apropriate to use composer update but that would install the already patched version which we don't want right now:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
[...]
  - Installing nette/application (v2.3.7): Downloading (100%)
[...]

Version 2.3.7 contains the security bug (up to and including 2.3.13 in the 2.3 line), so installing the Roave Security Advisories package with

composer require --dev roave/security-advisories:dev-latest

fails because of a conflict with version 2.3.7 of nette/application:

$ composer require --dev roave/security-advisories:dev-latest
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7].
    - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7].
    - roave/security-advisories dev-latest conflicts with nette/application[v2.3.7].
    - Installation request for roave/security-advisories dev-latest -> satisfiable by roave/security-advisories[dev-latest].
    - Installation request for nette/application (locked at v2.3.7, required as ~2.3.1) -> satisfiable by nette/application[v2.3.7].


Installation failed, reverting ./composer.json to its original content.

We'd need to first update nette/application to 2.3.14 or newer and then it'd possible to install roave/security-advisories:dev-latest. Since then, it would block installing any package with a known security vulnerability, while doing composer require or composer update.

PHP Security Advisories Database

Roave Security Advisories contains no code, the know-how lies in the conflict section in its composer.json, which presents a list of all packages and versions with a known security bug. The source of those is a database called PHP Security Advisories Database (see Contributing) which can be used in a few more ways. It can also be used as a GitHub Action (see my config) to check dependencies on each repository push for example (or even hourly like in my example).

Starting with Composer 2.4, you can also use composer audit.

It would be cool if hosting companies would also use this database somehow to notify their customers about a required update.

disable_functions

The only real defense against bugs like RCE is probably timely updates. Of course, your firewall can also block some URL patterns but that's more a reactive mitigation. You can at least lower the impact of the attack – you know, it's already quite bad when the attacker can run some PHP code in your app but it's much badder when they can run some external program from your PHP. I'd strongly suggest to disable these PHP function calls that can execute external programs:

  • exec – execute an external program
  • passthru – execute an external program and display raw output
  • proc_open – execute a command and open file pointers for input/output
  • shell_exec – execute command via shell and return the complete output as a string
  • system – execute an external program and display the output
  • pcntl_exec – executes specified program in current process space
  • popen – opens process file pointer

You can disable them only in php.ini (even for a specified server or path only) with disable_functions or in a FPM pool config with php_admin_value[disable_functions], see my example.

To check if you're not calling those functions in your own PHP code, use a static analyzer like PHPStan with my custom rules to flag disallowed function & method calls. Just include bundled disallowed-execution-calls.neon in your PHPStan config.

Also don't forget to follow me on Twitter, I tweet about critical problems like the one found in Nette too.

One more thing: if you use Nette and haven't updated yet it's almost for sure that someone has uploaded some remote shells or backdoors into your site or web app by now. You should initiate recovery and cleanup process, maybe even look for some data leaks. And if you've updated already, you can have fun with Easter eggs on my site.

Updates

September 19, 2023 Dependabot can also check and update your GitHub actions

January 25, 2023 Added a link to a post about composer audit

January 17, 2023 You can now watch and be notified only on security alerts on GitHub

January 16, 2021 GitLab dependency scanning and other Dependabot alternatives

November 20, 2020 GitHub has moved new Releases notifications setup to Custom category

Michal Špaček

Michal Špaček

I build web applications and I'm into web application security. I like to speak about secure development. My mission is to teach web developers how to build secure and fast web applications and why.

Public trainings

Come to my public trainings, everybody's welcome: