• Vulnerability: Reflected XSS
  • Affected Software: Anti-Malware and Brute-Force Security by ELI (WordPress Plugin)
  • Affected Version: 4.15.17 (probably also prior versions)
  • Patched Version: 4.15.20
  • Risk: Medium
  • Vendor Contacted: 2015-05-06
  • Vendor Fix: 2015-05-09
  • Public Disclosure: 2015-05-15

There are multiple reflected XSS vulnerabilities in the current version (4.15.17) of the Anti-Malware and Brute-Force Security by ELI WordPress plugin.

Reflected XSS can lead to execution of arbitrary JavaScript in the victims browser, which can lead to key logging, phishing, stealing of cookies, changing of data, and so on. The fact that these are present in an admin area does not weaken the attack, as the most interesting victim will be an admin.

XSS 1

POC

http://localhost/wordpress/wp-admin/admin.php?page=GOTMLS-settings&GOTMLS_msg=xsstest<script>alert(1)</script>

Code

    index.php:1326
        if (isset($_GET["GOTMLS_msg"]))
            die(GOTMLS_html_tags(array("html" => array("body" =>
$_GET["GOTMLS_msg"].'
'.__("saved.",'gotmls').(implode($GLOBALS["GOTMLS"]["tmp"]["settings_array"]["msg_position"])
== implode($GLOBALS["GOTMLS"]["tmp"]["default"]["msg_position"])?"":' <a
href="'.GOTMLS_script_URI.'&GOTMLS_msg='.urlencode($GLOBALS["GOTMLS_msg"]).'">['.$GLOBALS["GOTMLS_msg"].']</a>'))),
$properties));

simple fix

replace "body" => $_GET["GOTMLS_msg"] with "body" => htmlentities($_GET["GOTMLS_msg"]).

XSS 2 & 3

POC

http://localhost/wordpress/wp-admin/admin.php?page=GOTMLS-settings&scan_what=1&scan_type=xsstest<script>alert(1)</script>

Code

    index.php:913
                echo '
            <form method="POST" target="GOTMLS_iFrame"
name="GOTMLS_Form_clean"><input type="hidden" id="GOTMLS_fixing"
name="GOTMLS_fixing" value="1">
            <script type="text/javascript">
                showhide("inside_'.md5(GOTMLS_Scan_Settings_LANGUAGE).'");
            </script>'.GOTMLS_box($_REQUEST["scan_type"].' Status', '<div
id="status_text"><img src="'.GOTMLS_images_path.'wait.gif" height=16
width=16 alt="..."> '.GOTMLS_Loading_LANGUAGE.'</div><div
id="status_bar"></div><p id="pause_button" style="display: none;
position: absolute; left: 0; text-align: center; margin-left: -30px;
padding-left: 50%;"><input type="button" value="Pause"
class="button-primary" onclick="pauseresume(this);" id="resume_button"
/></p><div id="status_counts"></div><p id="fix_button" style="display:
none; text-align: center;"><input id="repair_button" type="submit"
value="'.GOTMLS_Automatically_Fix_LANGUAGE.'" class="button-primary"
onclick="loadIframe(\'Examine Results\');" /></p>');
                $scan_groups_UL = "";

and

    index.php:918
    GOTMLS_update_scan_log(array("scan" => array("dir" => $dir, "start" =>
time(), "type" => $_REQUEST["scan_type"])));

simple fix

use htmlentities($_REQUEST["scan_type"]) both times. Please see also the next section.

Note

The data is not only echoed twice to the user, but also stored in a log. When viewing the log at localhost/wordpress/wp-admin/admin.php?page=GOTMLS-View-Quarantine&Scanlog, it is again echoed without any sanitation which presents a stored XSS attack (although in practice it works just like a reflected one, as an attacker would have to let the admin inject the payload). I did not find the place where this is happening though.

XSS 4

POC

http://localhost/wordpress/wp-admin/admin.php?page=GOTMLS-settings&GOTMLS_fixing=2&GOTMLS_fix[]=PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==

PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg== represents base64 of <script>echo(1)</script>.

Code

    index.php:1268
        foreach ($_REQUEST["GOTMLS_fix"] as $clean_file) {
            $path = GOTMLS_decode($clean_file);
            if (is_file($path)) {
                if ($_REQUEST["GOTMLS_fixing"] > 1) {
                    echo "<li>Deleting $path ... ";
                    if
(GOTMLS_trailingslashit($GLOBALS["GOTMLS"]["tmp"]["quarantine_dir"]) ==
substr($path, 0,
strlen(GOTMLS_trailingslashit($GLOBALS["GOTMLS"]["tmp"]["quarantine_dir"])))
&& @unlink($path)) {
                        echo __("Deleted!",'gotmls');
                        $li_js .= "/*-->*"."/\nDeletedFile('$clean_file');\n/*<!--*"."/";
                    } elseif (is_file(dirname($path)."/index.php") &&
($GOTMLS_file_contents =
@file_get_contents(dirname($path)."/index.php")) &&
strlen($GOTMLS_file_contents) > 0 && GOTMLS_file_put_contents($path,
$GOTMLS_file_contents) && (@rename($path, dirname($path)."/index.php")
|| GOTMLS_file_put_contents($path, "") !== false)) {
                        echo __("Removed file contents!",'gotmls');
                        $li_js .= "/*-->*"."/\nfixedFile('$clean_file');\n/*<!--*"."/";
                    } else {
                        echo __("Failed to delete!",'gotmls');
                        $li_js .= "/*-->*"."/\nfailedFile('$clean_file');\n/*<!--*"."/";
                    }
                } else {
                    echo "<li>Fixing $path ... ";
                    $li_js .= GOTMLS_scanfile($path);
                }
                echo "</li>\n$li_js/*-->*"."/\n$callAlert\n</script>\n";
                $li_js = "<script type=\"text/javascript\">\n/*<!--*"."/";
            } else
                echo "<li>".__("File $path not found!",'gotmls')."</li>";
        }

Mitigation

The value is echoed in three different places, all of which would need to be sanitized with htmlentities or the appropriate WordPresses functions .