Security issue fixed in NinjaFirewall (WP Edition).

Today, we released version 4.3.4 of our NinjaFirewall (WP/WP+ Edition) plugin which includes a minor security fix.

Which versions are affected?

All versions below 4.3.4.

Are you at risk?

No, you aren’t. Even if you are running a version lower than 4.3.4, you aren’t at risk because all our users are protected by the firewall.
Additionally, we don’t even expect anyone to attempt to exploit it, not only because of the above reason but also because:
1. You must be running an older, unsupported version of PHP < 8.0 (the attack cannot work on the current version of PHP).
2. The conditions and privileges required to do so make it highly improbable, and the same attack could be performed without NinjaFirewall, simply by using the WordPress built-in theme or plugin editor.

What is it?

An authenticated admin phar deserialization.

How does it work?

There are several conditions in order to exploit it:

1. You must be a logged-in administrator, have both the manage_options and unfiltered_html capabilities, be in NinjaFirewall’s whitelist and, if you are restricting access to the firewall, you must be in that list too. Any other user, admin or not, authenticated or not, cannot perform the attack.

2. You must have another application (theme or plugin), installed and activated on the blog, that has a vulnerable POP chain. This is mandatory because neither WordPress nor NinjaFirewall are vulnerable.

3. You need to create a special phar:// archive with some malicious PHP code that will be used to exploit the vulnerable plugin or theme, and you must upload it on the blog.

4. Still as a admin, you need to access NinjaFirewall’s File Check and enter the path (absolute or relative) to the malicious phar file:

5. When saving the changes, NinjaFirewall will validate the input:

   if (! file_exists($_POST['snapdir']) ) {
      return sprintf( __('The directory %s does not exist.', 'ninjafirewall'), '<code>'. htmlspecialchars($_POST['snapdir']) .'</code>');
   if (! is_readable($_POST['snapdir']) ) {
      return sprintf( __('The directory %s is not readable.', 'ninjafirewall'), '<code>'. htmlspecialchars($_POST['snapdir']) .'</code>');

Ironically, it is the validation of that input that can trigger the issue. When checking if the directory exits and if it is readable, it will call the file_exists and is_readable functions. Because they are filesystem functions, this operation can trigger phar deserialization, which in turn could be used to exploit a vulnerable plugin or theme installed on the blog.

Phar’s metadata can be unserialized by any filesystem function, such as file_get_contents, file_put_contents and even fileatime, fileinode, stat or parse_ini_file among many other PHP functions. However, as indicated above, this applies to older PHP versions only.

Can NinjaFirewall protect against phar wrappers?

Yes. NinjaFirewall has a built-in policy to protect against several PHP wrappers, including phar archives and it is enabled by defaut. But, it doesn’t apply to admin users who are whitelisted by the firewall and thus can perform almost any type of action, which includes entering a PHP wrapper in an input field if they want to.


Thanks to Chloe Chamberland who responsively disclosed this issue to us.