17 Feb

Reflected Cross-Site Scripting (XSS) Vulnerability in Time Sheets

We recently found that the Time Sheets plugin contains a reflected cross-site scripting (XSS) vulnerability on one the plugin’s admin pages, Old Timesheets.

As of version 1.3.1, in the file /entry.php the GET inputs “start_date”, “end_date”, and “include_completed” were echo’d out with being sanitized or escaped to prevent malicious code from being placed on the page:

$start_date = $_GET['start_date'];
$end_date = $_GET['end_date'];

if ($start_date=='') {
 $start_date = date('Y-m-d', strtotime("-1 Year"));
}
if ($end_date=='') {
 $end_date = date('Y-m-d', strtotime("+1 Day"));
}

echo '<form method="get">';
echo "Enter Range To Search: ";
echo "<input type='text' name='start_date' size='10' value='{$start_date}'>";
echo " to ";
echo "<input type='text' name='end_date' size='10' value='{$end_date}'>";
echo "<br><input type='checkbox' name='include_completed' value='checked' {$_GET['include_completed']}> Include Completed Timesheets";

We notified the developer of the issue, but we haven’t heard back from them. Subsequent to that version 1.4.0 was released, which sanitizes two of those GET inputs,  “start_date” and “end_date”, by running them through a couple of the plugin’s functions:

772
773
$start_date = $common-&gt;f_date($common-&gt;clean_from_db($_GET['start_date']));
$end_date = $common-&gt;f_date($common-&gt;clean_from_db($_GET['end_date']));

No change was made related to third GET input “include_completed”, so the change with the others could be unrelated to our notifying the developer of the issue.

Proof of Concept

The following proof of concept will cause any available cookies to 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.

http://[path to WordPress]/wp-admin/admin.php?start_date=2017-02-02&end_date=2017-02-03&submit=Search&page=search_timesheet&include_completed='><script>alert(document.cookie);</script>

Timeline

  • February 2, 2017: Developer notified.
  • February 17, 2017 – WordPress.org Plugin Directory notified.
  • February 17, 2017 – Plugin removed from WordPress.org Plugin Directory.
17 Feb

Open Redirect Vulnerability in GTranslate

Recently while looking in to what turned out to be unrelated probing from a hacker for WordPress plugins we took a look at the plugin GTranslate and found that it has an open redirect vulnerability.

In the file /url_addon/gtranslate.php a redirect will occur if two variables are the same:

30
31
32
33
if($glang == $main_lang) {
    header('Location: ' . $page_url, true, 301);
    exit;
}

The variable $main_lang is currently hardcoded to “en” in the file /url_addon/config.php and the value of the other is set to the value of the GET input “glang”:

7
$glang = $_GET['glang'];

The value of $page_url, which is where the redirect occurs to is generated with the following code:

12
13
14
15
16
17
18
$page_url = '/'.$_GET['gurl'];
 
$page_url_segments = explode('/', $page_url);
foreach($page_url_segments as $i => $segment) {
    $page_url_segments[$i] = rawurlencode($segment);
}
$page_url = implode('/', $page_url_segments);

By specifying a value for the GET input “gurl” that starts with a “/” and the includes a URL without the protocol, the value of $page_url will be the URL with “//” before it, which tells the web browser to redirect to URL with whatever protocol the current page has. The protocol would either be http:// or https:// depending on which one you used with the request to /url_addon/config.php.

We contacted the developer about the issue and got an automated response that they would respond soon, but we haven’t heard from them since.

Proof of Concept

The following proof of concept will redirect you to our website.

Make sure to replace “[path to WordPress]” with the location of WordPress.

http://[path to WordPress]/wp-content/plugins/gtranslate/url_addon/gtranslate.php?glang=en&gurl=/www.pluginvulnerabilities.com

Timeline

  • February 3, 2017 – Developer notified.
  • February 17, 2017 – WordPress.org Plugin Directory notified.
  • February 17, 2017 – Removed from WordPress.org Plugin Directory.
13 Feb

Authenticated Local File Inclusion (LFI) Vulnerability in Posts in Page

One of the things we do to make sure our customers have the best data on vulnerabilities in WordPress plugins is to monitor hacking attempts on our websites. Through that we recently came across a request for a file, /wp-content/plugins/posts-in-page/assets/posts_in_page_help_view.php, from the plugin Posts in Page.

Nothing in that file looks like it could be exploited, so it looks like that request was instead probing for usage of the plugin. In looking over the rest of the code of the plugin we did find one vulnerability, that might be of more interest to hackers with the recent exploitation of a vulnerability in WordPress 4.7.0 and 4.7.1 that allowed an attacker to modify posts.

In the file /lib/page_posts.php we first found that it looked like it might be possible for arbitrary files to be included through the following that requires a specified file:

186
187
188
require ( $file_path = self::has_theme_template() )
	? $file_path // use template file in theme
	: POSTSPAGE_DIR . '/posts_loop_template.php'; // use default plugin template file

The function has_theme_template called there looks like this:

164
165
166
167
168
169
170
protected function has_theme_template() {
	$template_file = ( $this->args['template'] )
		? get_stylesheet_directory()  . '/' . $this->args['template'] // use specified template file
		: get_stylesheet_directory() . '/posts_loop_template.php'; // use default template file
 
	return ( file_exists( $template_file ) ) ? $template_file : false;
}

The value of $this->args[‘template’] in that comes from the “template” attribute of the plugin’s shortcode. There is no restriction on who can use that shortcode or attribute, so contributor-level and above users can cause any file on the website to be included through the use of directory traversal, which is an authenticated local file inclusion (LFI) vulnerability.

For author-level and above users there is a more serious issue as they have ability to upload media through WordPress. It is possible to upload media file that contain PHP code in them. Normally that code would not be able to run because the file extension of the file would not be one that causes the web server to execute it, but including such a file through a vulnerability like this would allow it to run.

Proof of Concept

When logged in as user that can add new posts, adding the following proof of concept to a post’s content will include a file named test.php from the root directory when viewing the resulting post (it will be included even when previewing the post):

[ic_add_posts template='../../../test.php']

Timeline

  • February 8, 2016 –  Developer notified.
  • February 13, 2017 – WordPress.org Plugin Directory notified.
  • February 13, 2017 – Vulnerability added to the free data included with our services’s companion plugin.
  • February 14, 2017 – Version 1.3.0 released, which fixes vulnerability.
07 Feb

Persistent Cross-Site Scripting (XSS) Vulnerability in XO Security

When it comes to trying to improve the security surrounding WordPress one of the big impediments is the security industry. One of the things we see them doing is providing misleading and sometimes outright false information to the public about security. One outright falsehood that has been widely spread is that there are lots of brute force attacks against WordPress admin passwords, when based on security companies own evidence are not happening at all. What is really happening are dictionary attacks, which involve an attacker try to login using common passwords. That type of attack is easily protected against by using a strong password, something that WordPress does a good job of helping you do. What might explain why security companies are saying something that isn’t true here is so that they can use the false claim to promote their plugins and services, like we have found Wordfence doing. The problem with this is that every plugin on the website introduces the possibility of a vulnerability, including security plugins.

Take the plugin XO Security that we recently ran across, which is promoted as providing “enhanced login security.” It provides a number of features and by default it will log login attempts. That involves storing and outputting user input data, which needs to be properly handled, but in that wasn’t happening, which was allowing for persistent cross-site scripting (XSS).

When a failed login attempts occurs the plugin’s function failed_login(), in the file /main.php, is run. If the failed login attempt doesn’t involve a username that already exists on the website, the password was logged without being sanitized in version 1.5.2:

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
function failed_login( $user_name ) {
	global $wpdb;
 
	$login_time = current_time( 'mysql' );
	$ip_address = $this->get_ipaddress();
	$lang = $this->get_language();
	$user_agent = $this->get_user_agent();
	$password = filter_input ( INPUT_POST, 'pwd' );
 
	if ( get_user_by( 'login', $user_name ) ) {
		$password = null;
	}
 
	$wpdb->insert( $this->loginlog_table, array(
		'success' => false,
		'login_time' => $login_time,
		'ip_address' => $ip_address,
		'lang' => $lang,
		'user_agent' => $user_agent,
		'user_name' => $user_name,
		'failed_password' =>F% $password
	) );

When the data was output on the page /wp-admin/users.php?page=xo-security-login-log in 1.5.2  there was no escaping of the password value (in the file /admin.php):

159
160
161
162
163
164
165
$datas = $wpdb->get_results( $wpdb->prepare(
	"SELECT `id`,`success`,`login_time` as `logintime`,`ip_address` as `ipaddress`,`lang`,`user_agent` as `useragent`,`user_name` as `loginname`,`failed_password` as `failedpassword`" .
	"FROM {$loginlog_table}{$where} ORDER BY `{$orderby}` {$order} LIMIT %d, %d;", $start, $per_page
), 'ARRAY_A' );
$this->items = $datas;
 
$this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil( $total_items / $per_page ) ) );

After we notified the developer of the vulnerability they released version 1.5.3, which fixed it by running the password value through esc_html() when saving it and when outputting it.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in an alert box when visiting the page /wp-admin/users.php?page=xo-security-login-log.

Attempt to login into WordPress with a username that doesn’t exist on the website and the following as the password:

<script>alert(document.cookie);</script>

Timeline

  • February 6, 2017 – Developer Notified
  • February 7, 2017 – Version 1.5.3 released, which fixes vulnerability.
30 Jan

Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in ABASE

We recently found that the ABASE plugin contains a cross-site request forgery (CSRF)/cross-site scripting (XSS) vulnerability on the plugin’s setting pages, /wp-admin/options-general.php?page=abase.

The CSRF potion of the vulnerability was due to a lack of a nonce on the page and a lack of a check for a valid one when processing a request to change the plugin’s settings.

For the XSS issue, in the file /abase_plugin_options.php starting at line 31 in version 2.6, settings are saved and there is no sanitization done. For example, the file File Upload Directory setting is saved with this code:

57
58
$dbfiles = $_POST['bus311mtd_dbfiles'];
update_option('bus311mtd_dbfiles', $dbfiles);

When the values are outputted on the page through the same file they were not escaped. For example, for the File Upload Directory setting is echo’d on line 246:

<td colspan=3>http://<?php echo $_SERVER['HTTP_HOST']; ?>/<input type="text" name="bus311mtd_dbfiles<?php echo $dbn;?>" value="<?php echo $dfiles; ?>" <?php if($dfiles==''){echo 'placeholder="'.$bus311mtd_default_file_upload_directory.'"';};?> size="20">/&lt;table_name&gt;/&lt;column_name&gt;/&lt;primary_index&gt;/&lt;file_name&gt;</td>

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/options-general.php?page=abase, when submitted as an Administrator.

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=abase" method="POST">
<input type="hidden" name="bus311mtd_hidden" value="Y" />
<input type="hidden" name="bus311mtd_show" value="1" />
<input type="hidden" name="bus311mtd_dbfiles" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

The plugin’s website is no longer accessible, so we couldn’t contact the plugin’s developer and we are notifying the Plugin Directory of our disclosure.

Timeline

  • January 30, 2017 – WordPress.org Plugin Directory notified.
  • January 30, 2017- Plugin removed from WordPress.org Plugin Directory.
23 Jan

Open Redirect Vulnerability in moreAds SE

We have recently been increasing the amount of new vulnerabilities we include our data through better monitoring of changes made to plugins, so that in more cases where there hasn’t been a report released on the vulnerability we can still include the vulnerability. Combined with that we have increased the number of post we have put out detailing those vulnerabilities. Seeing as we often find that vulnerabilities have been only partially fixed or not fixed at all, that also is likely to mean we will find more vulnerabilities that haven’t been fixed, despite an attempt to do so.

That was the case when we looked in to a reflected cross-site scripting vulnerability in the plugin moreAds SE. First we noticed that the vulnerability had not been fixed, but then we noticed that there was another vulnerability in the same code.

In version 1.4.8 of the plugin, the file /lib/Ads/html/bypass_stage_2.php takes a user specified value and places it in a JavaScript code that redirects to another location:

<script type="text/javascript">
 window.top.location = "<?php echo isset($_GET['i']) ? str_replace('"', '', $_GET['i']) : ''; ?>";
</script>

Since there is no restriction placed on what the value can be (other than removing any double quotes), you could cause a request sent through this to redirect to any web address, which is an open redirect.

After we notified the developer of the issue they released version 1.4.9, which fixes the issue by requiring an additional unique value generated by the plugin to be provided with the requests that cause a redirect to happen:

<?php
 $current_url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
 $is_valid = MASE_UrlSigning::verifySignedUrl($current_url, MASE::$URLSIGNING_KEY);
 $url = $is_valid ? $_GET['i'] : '/';
?>

window.top.location = "<?php echo esc_url($url); ?>";

Proof of Concept

The following proof of concept will cause you to come to our homepage.

Make sure to replace “[path to WordPress]” with the location of WordPress.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=mase_cst_redir&i=https://www.pluginvulnerabilities.com

Timeline

  • January 20, 2017: Notified developer.
  • January 23, 2017: Version 1.4.9 released, which fixes vulnerability.
17 Jan

Reflected Cross-Site Scripting (XSS) Vulnerability in WangGuard

We recently introduced a new feature where we do security reviews of plugins that are selected by our customers. The first review was of WangGuard. The most serious issue we found in that review is a reflected cross-site scripting (XSS) vulnerability.

In the file /wangguard-user-info.php the value of the GET input “userIP” is set as the value of the variable $userIP without any sanitization:

11
$userIP = $_GET["userIP"];

That value is then printed without it being escaped:

33
34
printf( __('User IP: %s <br />'), $userIP);
printf( __('User nicename: %s <br />'), $user_info->user_nicename);

Proof of Concept

The following proof of concept will cause any available cookies to be shown in an 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.

http://[path to WordPress]/wp-admin/admin.php?page=wangguard_users_info&userIP=<script>alert(document.cookie);</script>

Timeline

  • January 2, 2017 – Developer notified.
  • January 17, 2017 – WordPress.org Plugin Directory notified.
  • January 18, 2017 – Version 1.7.3 released, which fixes vulnerability.
12 Jan

Authenticated Persistent Cross-Site Scripting (XSS) Vulnerability in Chained Quiz

When adding a vulnerability to our data set we actually look in to it to confirm that a vulnerability actual existed, what versions of the plugin had the vulnerability, and that it has been fully fixed. Recently while looking over changes made in version 0.9.9 of the plugin Chained Quiz, which was listed as having “Fixed various XSS issues”, we noticed that one of the cross-site scripting (XSS) issues was only partially resolved.

Several of the changes made sanitized title fields for various pieces of the plugin’s quizzes. By default only Administrator-level user have access to the pages with those fields and for those users it wouldn’t have really been a vulnerability for the fields to not be sanitized since that level of user normally have the unfiltered_html capability, which allows them to do the equivalent of cross-site scripting. The plugin does provides the option to make those pages as well as the Social Sharing page accessible to lower level users, which would not have that capability, which would make this a vulnerability.

In looking over the relevant files what we noticed was that the rest of the text input is not being sanitized, so the vulnerability still exists on those pages.

An example of that is when creating a new question, you can see that in version 0.9.9 the value “$vars[‘title’]” is sanitized but the other text inputs “$vars[‘question’]” and “$vars[‘qtype’]” are not (the database field for “$vars[‘qtype’]” is limited to 20 characters making it difficult to use it for malicious code) :

25
26
27
28
29
$vars['title'] = sanitize_text_field($vars['title']);
 
$result = $wpdb->query($wpdb->prepare("UPDATE ".CHAINED_QUESTIONS." SET
	question=%s, qtype=%s, title=%s, autocontinue=%d WHERE id=%d", 
	$vars['question'], $vars['qtype'], $vars['title'], @$vars['autocontinue'], $id));

The text inputs on Social Sharing page are also not sanitized or escaped.

We contacted the developer about the issue and seeing as they had just fixed part of the issue and another related vulnerability, we figured they would be receptive (that is usually the case in this type of situation). Instead we got a very different response. It began:

The contents of the questions should not be filtered: it has to allow HTML and scripts if the site managers want to use them.
All these issues are “Self-XSS” that we are not interested to hear about: no one has any interest to hack their own site or give management access to people they don’t trust.

As far as we can tell self-XSS actually refers to a social engineering attack, not a vulnerability, which these are.

In our reply to that we explained that unless users have the unfiltered_html capability they are not allowed to use unfiltered HTML, so they needed to make sure users without the capability had their input sanitized. We haven’t gotten any response in the week since we sent that reply and the vulnerability hasn’t been resolved.

Proof of Concept

The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=chainedquiz_social_sharing, when logged in to WordPress with an account that has access to the page.

  1. As an Administrator access /wp-admin/admin.php?page=chainedquiz_options and set it so that Subscribers can manage quizzes.
  2. Log in as a Subscriber.
  3. Visit /wp-admin/admin.php?page=chainedquiz_social_sharing.
  4. In the “Your Facebook App ID” field enter ‘”><script>alert(document.cookie);</script>’ (without the single quotes around it).
  5. Click “Save All Settings”.

Timeline

  • January 4, 2017 – Developer notified.
  • January 5, 2017 – Developer responds.
  • January 12, 2017 – WordPress.org Plugin Directory notified.
  • January 12, 2017 – Removed from WordPress.org Plugin Directory.
  • January 12, 2017 – Version 1.0 submitted to WordPress.org Plugin Directory, which fixes issue.
02 Jan

Information Disclosure Vulnerability in Pike Firewall

In our testing of WordPress security plugins to see what, if any, protection they provide against the exploitation of actual vulnerabilities in other plugins the results haven’t been good so far. Most of the plugins tested haven’t provided any protection against those vulnerabilities. That hasn’t really surprised us, as much of what these plugins do doesn’t have any impact on what hackers actually try to do. One example is that many of these plugins check if you have change the database prefix to something other than the default “wp_”, but knowing the database prefix is rarely needed for vulnerabilities we see being exploited. If knowing the database prefix was a big deal then the vulnerability we recently found in a security plugin would be a big deal, as the vulnerability exposes that.

While doing a few quick security checks over the plugin Pike Firewall we noticed that it has the capability to log login attempts. We and others have found that capability in plugins has introduced security vulnerabilities into plugins due to improper handling of user input that comes through that. One of things that has been an issue with other plugins is that malicious JavaScript code placed in the HTTP header field X-Forwarded-For will get displayed on the plugin’s pages unsanitized or unescaped leading to cross-site scripting (XSS). In this case we found it caused another issue when tried logging in with it set to malicious code we got this error:

WordPress database error: []
SHOW FULL COLUMNS FROM `wp_pike_firewall_login`

The database prefix is being shown in that error message.

In looking at the underling code the cause of this is (in the file /pikefirewall.php):

2756
2757
2758
2759
if ( !$wpdb->insert($pike_tables['login'], array('username' => $username, 'user_address' =>; $pike_ip, 'user_agent' => $pike_agent, 'type' => $type, 'success' =&gt; $success), array('%s', '%s', '%s'))) {
	$wpdb>show_errors();
	wp_die($wpdb->print_error());
}

You can see that error reporting is enabled and if there is an error it gets printed, which shouldn’t be happening in a non-development environment since as our example shows it is disclosing non-public information.

We contacted the developer about the issue on December 19, but we have not heard back from them and the vulnerability has not been fixed.

Proof of Concept

With login attempt logging turned on, set the X-Forwarded-For HTTP header to

<script>alert(document.cookie);</script>

and attempt to log in to WordPress (the username/password doesn’t matter).

Timeline

  • December 19, 2016 – Developer notified.
19 Dec

Cross-Site Request Forgery (CSRF)/Database Update Vulnerability in ZX_CSV Upload

We try to include as many plugin vulnerabilities in our data as possible, which involves us looking at reports of vulnerabilities even when almost no one is using them. That includes looking into a report of an authenticated SQL injection vulnerability in ZX_CSV Upload, which is a plugin with less than 10 active installs according to wordpress.org. According to the report you would need to be logged in as an Administrator to exploit this, which generally would rule something out as vulnerability, since administrator would normally have the ability change the database through the capabilities they have (they also would normally be able to modify a plugin to remove any security restrictions), unless you can use cross-site request forgery (CSRF) to cause a logged in administrator to take an action they don’t intend, there wouldn’t be a vulnerability.

When we went to see we could figure out how if that would be possible in this situation we noticed an easier to spot issue. The main functionality of the plugin is to allow you to update database data from an uploaded CSV file. This capability lacks protection against cross-site request forgery so you could cause a logged in Administrators to change information in the database, say add a new Administrator level users to wp_users table. You would need to know what the prefix for the database is, so changing that would actually come in to play with a vulnerability (which it rarely does despite the big deal made about changing it in various security plugins and tutorials).

Proof of Concept

The following proof of concept will cause the selected CSV file to be submitted and the relevant table updated, when submitted as an Administrator.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[database table]” with the table you want updated.

<html>
<body>
<form method="POST" action="http://[path to WordPress]/wp-admin/admin.php?page=upload_new" enctype="multipart/form-data">
<input type="hidden" name="table_select" value="[database table]">
<input type="file" name="uploaded">
<input type="submit" name="update_db">
</form>
</body>
</html>

Timeline

  • December 19, 2016 – WordPress.org Plugin Directory notified.
  • December 19, 2016 – Plugin removed from WordPress.org Plugin Directory.