Blog of Daniel Ruf

#wordpress | #php

you shall pass - secret login URL leaked by WordPress-Plugin

11.12.2021

Recently I was informed by a contact, that he observed some weird behavior with a plugin (WPS Hide Login), that should normally hide his “secret” login URL. In his case he was accidentally redirected to this special URL.

The technical details are also described at https://wordpress.org/support/topic/bypass-security-issue/.

I took a brief look at the provided URLs and after a few seconds I found out, that the plugin reveals the “secret” login URL when I set a random Referer header and send a request to something like https://example.com/wp-admin/options.php.

I quickly set up a local test instance of WordPress using https://github.com/DanielRuf/run-local-wordpress (sometimes I also use php -S localhost:8080 -t . when I already have such a test instance) and installed the mentioned plugin to verify it with a new WordPress instance and prepare a Proof of Concept for reporting the vulnerability.

curl --referer "something" -sIXGET https://example.com/wp-admin/options.php

HTTP/2 302 
...
location: https://example.com/secret-login/?redirect_to=%2Fwp-admin%2Fsomething&reauth=1 

In my test case I used secret-login but as you can see, it is successfully revealed.

The code was a bit complex and hard to understand since there were multiple things happening, when this bypass was successful. For example the same request was passed through different functions multiple times.

I checked with the WP Rollback plugin, which version of WPS Hide Login first introduced this flaw. For this I had to test almost all versions until version 1.3. It seems in version 1.3 this does not happen, only in version 1.3.1 and newer. So the vulnerability was probably introduced in these changes (* Fix : redirect change admin email) on 16th of April 2018:

https://plugins.trac.wordpress.org/changeset?new=1858960%40wps-hide-login%2Ftrunk&old=1857195%40wps-hide-login%2Ftrunk

diff -ur ./wps-hide-login-1.3 ./wps-hide-login-1.3.1

diff -ur ./wps-hide-login-1.3/classes/plugin.php ./wps-hide-login-1.3.1/classes/plugin.php
--- ./wps-hide-login-1.3/classes/plugin.php	2018-04-12 14:06:24.000000000 +0200
+++ ./wps-hide-login-1.3.1/classes/plugin.php	2018-04-16 10:09:56.000000000 +0200
@@ -410,13 +410,13 @@
 
 			global $pagenow;
 
-			if ( is_admin() && ! is_user_logged_in() && ! defined( 'DOING_AJAX' ) && $pagenow !== 'admin-post.php' ) {
+			$request = parse_url( $_SERVER['REQUEST_URI'] );
+
+			if ( is_admin() && ! is_user_logged_in() && ! defined( 'DOING_AJAX' ) && $pagenow !== 'admin-post.php' && ( isset( $_GET ) && empty( $_GET['adminhash'] ) && $request['path'] !== '/wp-admin/options.php' ) ) {
 				wp_safe_redirect( home_url( '/404' ) );
 				die();
 			}
 
-			$request = parse_url( $_SERVER['REQUEST_URI'] );
-
 			if ( $pagenow === 'wp-login.php'
 			     && $request['path'] !== $this->user_trailingslashit( $request['path'] )
 			     && get_option( 'permalink_structure' ) ) {
diff -ur ./wps-hide-login-1.3/readme.txt ./wps-hide-login-1.3.1/readme.txt
--- ./wps-hide-login-1.3/readme.txt	2018-04-12 14:06:24.000000000 +0200
+++ ./wps-hide-login-1.3.1/readme.txt	2018-04-16 10:09:56.000000000 +0200
@@ -4,7 +4,7 @@
 Tags: rename, login, wp-login, wp-login.php, custom login url
 Requires at least: 4.1
 Tested up to: 4.9
-Stable tag: 1.3
+Stable tag: 1.3.1
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 
@@ -140,6 +140,9 @@
 
 == Changelog ==
 
+= 1.3.1 =
+* Fix : redirect change admin email
+
 = 1.3 =
 * Fix : redirect wp-register.php
 
diff -ur ./wps-hide-login-1.3/wps-hide-login.php ./wps-hide-login-1.3.1/wps-hide-login.php
--- ./wps-hide-login-1.3/wps-hide-login.php	2018-04-12 14:06:24.000000000 +0200
+++ ./wps-hide-login-1.3.1/wps-hide-login.php	2018-04-16 10:09:56.000000000 +0200
@@ -4,7 +4,7 @@
 Description: Protect your website by changing the login URL and preventing access to wp-login.php page and wp-admin directory while not logged-in
 Author: WPServeur, NicolasKulka, tabrisrp
 Author URI: https://wpserveur.net
-Version: 1.3
+Version: 1.3.1
 Requires at least: 4.1
 Tested up to: 4.9
 License: GPLv2 or later
@@ -17,7 +17,7 @@
 }
 
 // Plugin constants
-define( 'WPS_HIDE_LOGIN_VERSION', '1.3' );
+define( 'WPS_HIDE_LOGIN_VERSION', '1.3.1' );
 define( 'WPS_HIDE_LOGIN_FOLDER', 'wps-hide-login' );
 
 define( 'WPS_HIDE_LOGIN_URL', plugin_dir_url( __FILE__ ) );

I reported the issue with the PoC to WPScan and they contacted to the plugin developers. After a few days they released a new version, which prevents this simple bypass.

diff -ur ./wps-hide-login-1.9 ./wps-hide-login-1.9.1

diff -ur ./wps-hide-login-1.9/classes/plugin.php ./wps-hide-login-1.9.1/classes/plugin.php
--- ./wps-hide-login-1.9/classes/plugin.php	2021-10-19 11:16:18.000000000 +0200
+++ ./wps-hide-login-1.9.1/classes/plugin.php	2021-11-02 16:08:06.000000000 +0100
@@ -538,6 +538,11 @@
 				die();
 			}
 
+			if ( ! is_user_logged_in() && $request['path'] === '/wp-admin/options.php' ) {
+				header('Location: ' . $this->new_redirect_url() );
+				die;
+			}
+
 			if ( $pagenow === 'wp-login.php'
 			     && $request['path'] !== $this->user_trailingslashit( $request['path'] )
 			     && get_option( 'permalink_structure' ) ) {
diff -ur ./wps-hide-login-1.9/readme.txt ./wps-hide-login-1.9.1/readme.txt
--- ./wps-hide-login-1.9/readme.txt	2021-10-19 11:16:18.000000000 +0200
+++ ./wps-hide-login-1.9.1/readme.txt	2021-11-02 16:08:06.000000000 +0100
@@ -6,7 +6,7 @@
 Requires at least: 4.1
 Tested up to: 5.8
 Requires PHP: 7.0
-Stable tag: 1.9
+Stable tag: 1.9.1
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 
@@ -148,6 +148,9 @@
 
 == Changelog ==
 
+= 1.9.1 =
+* Fix : by-pass security issue allowing an unauthenticated user to get login page by setting a random referer string via curl request.
+
 = 1.9 =
 * Fix : redirect ajax add_to_cart
 
diff -ur ./wps-hide-login-1.9/wps-hide-login.php ./wps-hide-login-1.9.1/wps-hide-login.php
--- ./wps-hide-login-1.9/wps-hide-login.php	2021-10-19 11:16:18.000000000 +0200
+++ ./wps-hide-login-1.9.1/wps-hide-login.php	2021-11-02 16:08:06.000000000 +0100
@@ -5,7 +5,7 @@
 Donate link: https://www.paypal.me/donateWPServeur
 Author: WPServeur, NicolasKulka, wpformation
 Author URI: https://wpserveur.net
-Version: 1.9
+Version: 1.9.1
 Requires at least: 4.1
 Tested up to: 5.8
 Requires PHP: 7.0
@@ -21,7 +21,7 @@
 }
 
 // Plugin constants
-define( 'WPS_HIDE_LOGIN_VERSION', '1.9' );
+define( 'WPS_HIDE_LOGIN_VERSION', '1.9.1' );
 define( 'WPS_HIDE_LOGIN_FOLDER', 'wps-hide-login' );
 
 define( 'WPS_HIDE_LOGIN_URL', plugin_dir_url( __FILE__ ) );

CVEs

CVE-2021-24917 - WPS Hide Login < 1.9.1 - Protection Bypass with Referer-Header

Timeline

2021-10-27 12:18 message from contact regarding possible secret URL leak
2021-10-27 12:29 local test and successful creation of a PoC
2021-10-27 12:37 issue reported to WPScan
2021-10-27 15:45 WPScan requests more details to reproduce the reported issue
2021-10-27 18:28 confirmed and vendor contacted by WPScan
2021-11-02 17:08 reported vulnerability is fixed in WPS Hide Login 1.9.1
2021-11-02 17:25 report published by WPScan