Authenticated Persistent Cross-Site Scripting (XSS) Vulnerability in User Login Log
One of the many problems we have with the WordPress security company WordFence is that they are disclosing vulnerabilities in plugins with the key details of the vulnerabilities being held back. They are not doing that for a perceived security purpose, but so they can try to profit off of them by being the only firewall provider who protect against them. They certainly have a right to try to do that, but it doesn’t match with their claim that “security of … the greater WordPress community is of paramount importance to us” since those details are important to improving the security of WordPress plugins. One of the reason for that is that those details often lead to finding more vulnerabilities in the same plugins, in three instances we found that Wordfence had missed vulnerabilities related to the vulnerabilities they found and it would have been easier to find those if Wordfence wasn’t doing what they are doing.
Another important way the details help to improve the security of WordPress plugins is someone else can use the details of a vulnerability to find similar vulnerabilities in other plugins. One such instance occurred for us last week. Last week a persistent cross-site scripting (XSS) vulnerability was disclosed in the plugin Activity Log, which was caused by a failure to sanitize the value of the HTTP header HTTP_X_FORWARDED_FOR. After adding that vulnerability to our data set, we took a quick look at other plugins and found that similar issue exists in the current version, 2.2.1, of User Login Log plugin.
That plugin logs all successful login attempts and makes the logging viewable on a page in the admin area. Like the other plugin the HTTP header HTTP_X_FORWARDED_FOR is not sanitized, so if the value of that is set to malicious JavaScript it will be included on the logging page.
In the code the problem is caused by the fact that the only sanitization done on the value is to run it through esc_sql(), which “prepares a string for use as an SQL query”. So it was being protected against SQL injection, but not against cross-site scripting.
386 | 'ip' => isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? esc_sql($_SERVER['HTTP_X_FORWARDED_FOR']) : esc_sql($_SERVER['REMOTE_ADDR']), |
Because we need to go back through old versions to determine what versions of the plugin are vulnerable for our service’s data, we found something interesting with this vulnerability. Prior to version 2.0, the plugin actually protected against this vulnerability. In earlier version the code used esc_attr() instead of esc_sql when handling the value:
'ip' => isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? esc_attr($_SERVER['HTTP_X_FORWARDED_FOR']) : esc_attr($_SERVER['REMOTE_ADDR']), |
We contacted the developer about the vulnerability, but have not heard back from them.
Proof of Concept
Log in to WordPress with the X_FORWARDED_FOR HTTP header set to “<script>alert(document.cookie);</script>”. That header can be set in Chrome using the ModHeader extension or in Firefox using the X-Forwarded-For Header extension.
Afterwards, when visiting the page /wp-admin/users.php?page=login_log any available cookies will be shown in an alert box.
Timeline
- 7/11/2016 – Developer notified.