A critical security bug in Ninja Forms (1+ million installations) was patched by the plugin's developers this week.
The security bug posed a high risk, as it could result in unauthenticated object injection. Successful attacks could create arbitrary Classes within WordPress (and execute a function or method defined within).
The WordPress.org plugins team took action at the request of the plugin author to ensure all sites running insecure versions of Ninja Forms received the security patch ASAP.
All users with the Ninja Forms Contact Form (The Drag and Drop Form Builder for WordPress) received a security patch on their websites, with no action needed by the site owner.
This forced update was performed at the request of the Ninja Forms developer because the patch addresses a critical security bug that could have led to limited code execution. The WordPress.org plugin team reviewed the patch, and forced websites to update the plugin, ensuring all sites with Ninja Forms installed received a timely update, even if they have opted out of auto-updates on the plugin.
The developers of Ninja Forms and the WordPress.org volunteers had a shared goal: Protect websites running Ninja Forms from compromise. This is a good thing.
Some people may take a contrarian position that forcing updates is an overstep of authority, but this is a waste of time.
This patch and the subsequent update had an inarguable intent to protect websites from a high risk of compromise. The action was taken at the request of the plugin author, whose motivation was to secure the end user's websites from attack. The Ninja Forms developers were looking out for their users, and the WordPress.org volunteers were able to help.
Reviewing the patch's diff, I can confirm it addresses a security bug that could have been easily weaponized into existing bot-nets, and a successful attack could compromise a site with a single request.
The action of forcing the update, minimized the time it took to secure websites to just a few days. It shortened the patch window and diminished any malicious party's window of opportunity to compromise websites using this vulnerability.
Ninja Forms has over 1 million installations, so we can conservatively suspect that thousands - possibly tens or hundreds of thousands - of websites may have had auto-updating turned off and the update may have been delayed.
The coordinated efforts of the plugin developer and the WordPress.org volunteers pushing this forced update helped secure these websites.
The security bug which was patched by the Ninja Forms developers was listed as Object Injection.
It's an attack I am familiar with and spoke about in Patchstack Weekly's episode on PHP Object Injection, Insecure Unserialize.
There was one difference though - the function that was validating the user-controlled data used the is_callable() function to determine if the given method is part of the proper scope or not. If this was not the case, the user-controlled data would be executed as a variable function outside of the $this scope which makes it a more dynamic attack vector than deserialize().
Just like the insecure usage of deserialize(), is_callable() should not be used with user input or user-controlled data if this user input controlled data is then used to execute a variable function.
There is no large warning in is_callable()'s documentation, like there is in serialize() but, the closest I found to a warning for developers when using is_callable() is the following under Notes:
This function may trigger autoloading if called with the name of a class. If an object implements __call(), then this function will return true for any method on that object, even if the method is not defined.
With the help of Patchstack's CTO, Dave Jong, Patchstack had a working Proof of Concept in short order.
We then used what we learned from writing the PoC to build a virtual patch for all Patchstack App Pro and Business customers.
Of course, sites should already have received the security patch for the reasons I explained earlier, but we pushed the virtual patch because we would never leave the protection of a critical vulnerability up to chance. There is a security best practice to have multiple layers of protection, also known as defense in depth, and that's why we pushed a virtual patch.
Thanks to the quick action by the Ninja Forms developers and the WordPress.org plugins team, you may already be patched. It may still be a good idea to double-check the patch was applied successfully (and without error too).
Simply log in to your WordPress admin dashboard, open the Plugin tab and check that you are running the most recent release of Ninja Forms. There are backport patches so you should make sure you are running one of the following versions which I have confirmed all include the patch to address this Object Injection security bug:
If you manage multiple sites, you will need to log in to each WP admin dashboard manually.
You may get great value if you use the Patchstack App. The app has a dashboard that provides a security operation center (SOC), or birds-eye view of all of your websites and will quickly identify for you which sites are running insecure components. This makes the process a whole lot easier for power users who manage security for multiple WordPress websites.
WordPress and PHP developers can learn from this incident as well.
Much like insecure usage of unserialize() can lead to Object Injection, you should never send user input to a function that may instantiate PHP Classes or Objects.
I only learned this week that this includes is_callable() if used with variable functions, so this may be new knowledge for you as well.
Take a minute and double-check your code-base - you may end up identifying and patching a security bug before the attackers have a check to exploit it.
That is the sort of goal we should all be able to agree on: the goal of securing websites before they're compromised.