- 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 .