WordPress Plugin Limit Login Attempts Reloaded
1 - Rate Limit Bypass: In a non-standard configuration, the client IP header accepts any arbitrary string. When randomizing the header input, the login count does never reach the maximum allowed retries.
2 - Reflected XSS: When logged as WordPress administrator, the tab URL parameter in /wp-admin/options-general.php?page=limit-login-attempts
- Plugin name: Limit Login Attempts Reloaded
- Affected version: >= 2.13.0
- Plugin Web Page: https://wordpress.org/plugins/limit-login-attempts-reloaded/
This WordPress plugin is aimed to be a bruteforce attack protection mechanism, and is currently installed in more than 1 million of active installations.
However, during a quick auditing of the plugin, I’ve found two issues. One is a rate limiting bypass under a non-default configuration, which effectively defeats the plugin purpose. The other one is a unauthenticated reflected XSS.
We tried to contact the WordPress plugin team and the developer directly, but received no response. As of today, the vulnerabilites are fixed, so I am releasing the full disclosure.
It is recommended to update to the latest version.
1 - Improper Restriction of Excessive Authentication Attempts (Rate Limit Bypass on login page) - CWE-307
- Summary: When the plugin is configured to accept an arbitrary header as client source IP address, a malicious user is not limited to perform a brute force attack, because the client IP header accepts any arbitrary string. When randomizing the header input, the login count does never reach the maximum allowed retries.
- Prerequisites: The plugin needs to be installed and activated on WordPress. On plugin settings page, the Trusted IP Origins has to be configured for an arbitrary header, for example X-Forwarded-For.
- CVE and CVSS Score: CVE-2020-35590 | 9.8 (Critical)
Step-by-step instructions and PoC
A necessary prerequisite is to configure a custom header on the plugin settings page, in the Trusted IP Origins, including for example X-Forwarded-For.
The configuration page can be as the following picture:
Please note that in some networking scenarios this configuration is possible. Indeed, there might be present load balancers or reverse proxies in front of the WordPress instance.
- URL: http://hostname/wp-login.php
- HTTP Parameter: X-Forwarded-For
In normal condition, when there is no custom Trusted IP Origin header configured, or if the header value is always the same value, an attacker is not able to perform an attack, because the login limit is enforced by the plugin.
Indeed, the classic password only brute force is blocked, as depicted by the following pictures:
The attacker can use a word list in the password value:
After few attempts, the plugin in the default configuration successfully stops the attacker:
However, the X-Forwarded-For value is not validated to be an IP address or an array of IP addresses (some load balancers could use a comma-separated list as value).
Indeed, if a malicious user adds the X-Forwarded-For header on the login request, can test for the authentication rate limit bypass.
Below are the evidences with the vulnerability details and the payload used.
The first set of values is the word list used for testing the password value:
The second list can be every string, even simply incrementing the numbers from 1 to 10:
Indeed, the attacker is able to bypass the limit and potentially found the login credentials:
Please note that custom headers are allowed using the following format, as an example:
- Plugin settings value name: HTTP_X_PIPPO
- HTTP request header name: X-Pippo
Payload used to exploit the vulnerability, changing the value of X-Forwarded-For on every attempt:
POST /wp-login.php HTTP/1.1 Host: wordpress User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded X-Forwarded-For: 1 Connection: close Cookie: wordpress_test_cookie=WP+Cookie+check Upgrade-Insecure-Requests: 1 log=admin&pwd=password&wp-submit=Log+In&redirect_to=http%3A%2F%2Fwordpress%2Fwp-admin%2F&testcookie=1
By exploiting this issue an attacker is able to perform an arbitrary number of authentication attempts using different passwords, and eventually gain access to the targeted account.
I have released a brute-force tool on my GitHub repo.
2 - Improper Neutralization of Input During Web Page Generation (Reflected Cross-Site Scripting) - CWE-79
- Summary: An authenticated remote user is able to inject arbitrary web script or HTML due to incorrect sanitization of user-supplied data and achieve a Reflected Cross-Site Scripting attack against the platform administrators.
- Prerequisites: The plugin needs to be installed and activated on WordPress. No special configuration is required to reproduce the issue.
- CVE and CVSS Score: CVE-2020-35589 | 5.4 (Medium)
Step-by-step instructions and PoC
A malicious user can cause an administrator user to supply dangerous content to the vulnerable page, which is then reflected back to the user and executed by the web browser. The most common mechanism for delivering malicious content is to include it as a parameter in a URL that is posted publicly or e-mailed directly to victims.
- URL: http://wordpress/wp-admin/options-general.php?page=limit-login-attempts
- HTTP Parameter: tab
Below are the evidences with the vulnerability details and the payloads used.
While the administrator user is logged in to WordPress, visit the page in the payload section.
The data is read directly from the HTTP request and reflected back in the HTTP response:
Payload used to exploit the vulnerability:
Please note that the options-general.php page is only available to WordPress administrators. Lower privileged users are not affected because the page is forbidden for them (error 403).
By exploiting this issue an attacker is able to target administrator users who are able to access the plugin configuration page within the browser with several type of direct or indirect impacts such as stealing cookies (if the HttpOnly flag is missing from the session cookies), modifying a web page, capturing clipboard contents, keylogging, port scanning, dynamic downloads and other attacks. This type of reflected XSS does require user interaction.
- 14/06/2020: First dislosure to WP Plugin Team firstname.lastname@example.org
- 23/06/2020: Ping request to WP Plugin Team for reading the mail. They reply asking for waiting 60 days for publishing the issues.
- 24/07/2020: Ping request to WP Plugin Team for news. Destination Host Unreachable (Spoiler: No response 😳)
- 15/09/2020: Contacted directly the developer via mail address. No response till day.
- 14/12/2020: Testing if those vulnerabilties apply to latest version to date, 2.17.4. The developer has fixed both, but not notified users. Thus, releasing.
- 21/12/2020: MITRE released
CVE-2020-35589 and CVE-2020-35590.
- 22/12/2020: NVD scored
CVE-2020-35589 as 5.4 (Medium) and CVE-2020-35590 as 9.8 (Critical).
- 24/12/2020: Released exploit for CVE-2020-35590 on my GitHub repo. Find your perfect Christmas gift 🎅