Wordfence Security – Firewall & Malware Scan - Version 5.2.5

Version Description

  • Security release. Update immediately. Thanks to Julio Potier.
  • Code hardening including improved sanitization and an additional nonce for unlock email form. Special thanks to Ryan Satterfield for the hard work.
  • Stability of auto-update improved for LiteSpeed customers. We auto-detect if you don't have E=noabort:1 in your .htaccess and give you instructions.
  • Auto-update also disabled now for LiteSpeed customers who don't have E=noabort:1 and you will get an email alert with an explanation.
  • Fixed a bug that may cause you to have advanced blocking patterns disabled with falcon engine enabled that should not be disabled.
  • Removed a benign warning in wfCache.php.
  • Added clarity to the banned URL option on the options page. All URL's must be relative.
  • Added a primary key to the wp_wfStatus table which is required for certain incremental backup plugins and utilities.
  • Fixed advanced country blocking which was not correctly displaying advanced options.
  • Migrated to using wp_kses() for sanitization.
  • Prevent IP spoofing in default Wordfence IP configuration.
  • Change explanations of how Wordfence gets IP's to make it clear which to use to prevent spoofing.
  • Make it clear that the option to have IP's immediately blocked when they access a URL requires relative URL's starting with a forward slash.
  • Whitelist Sucuri's scanning IP addresses which were getting blocked because they triggered Wordfence blocking during a scan.
  • Improved Wordfence's code that acquires the visitor IP to block certain spoofing attacks, be more platform agnostic and deal with visits from private IP's more elegantly.
Download this release

Release Info

Developer mmaunder
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 5.2.5
Comparing to
See all releases

Code changes from version 5.2.4 to 5.2.5

lib/IPTraf.php CHANGED
@@ -4,7 +4,7 @@
4
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
  <link rel='stylesheet' id='wordfence-main-style-css' href='<?php echo wfUtils::getBaseURL(); ?>/css/iptraf.css?ver=<?php echo WORDFENCE_VERSION; ?>' type='text/css' media='all' />
6
  <body>
7
- <h1>Wordfence: All recent hits for IP address <?php echo htmlspecialchars($IP, ENT_QUOTES, 'UTF-8'); if($reverseLookup){ echo '[' . htmlspecialchars($reverseLookup, ENT_QUOTES, 'UTF-8') . ']'; } ?></h1>
8
  <table border="0" cellpadding="2" cellspacing="0" style="width: 900px;">
9
  <?php foreach($results as $key => $v){ ?>
10
  <tr><th>Time:</th><td><?php echo $v['timeAgo'] ?> ago -- <?php echo date(DATE_RFC822, $v['ctime']); ?> -- <?php echo $v['ctime']; ?> in Unixtime</td></tr>
@@ -12,11 +12,11 @@
12
  <?php if(wfUtils::hasXSS($v['URL'])){ ?>
13
  <tr><th>URL:</th><td><span style="color: #F00;">Possible XSS code filtered out for your security</span></td></tr>
14
  <?php } else { ?>
15
- <tr><th>URL:</th><td><a href="<?php echo $v['URL']; ?>" target="_blank"><?php echo $v['URL']; ?></a></td></tr>
16
  <?php } ?>
17
  <tr><th>Type:</th><td><?php if($v['type'] == 'hit'){ echo 'Normal request'; } else if($v['type'] == '404'){ echo '<span style="color: #F00;">Page not found</span>'; } ?></td></tr>
18
  <?php if($v['referer']){ ?><tr><th>Referrer:</th><td><a href="<?php echo $v['referer']; ?>" target="_blank"><?php echo $v['referer']; ?></a></td></tr><?php } ?>
19
- <tr><th>Full Browser ID:</th><td><?php echo esc_html($v['UA']); ?></td></tr>
20
  <?php if($v['user']){ ?>
21
  <tr><th>User:</th><td><a href="<?php echo $v['user']['editLink']; ?>" target="_blank"><?php echo $v['user']['avatar'] . ' ' . $v['user']['display_name']; ?></a></td></tr>
22
  <?php } ?>
4
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
  <link rel='stylesheet' id='wordfence-main-style-css' href='<?php echo wfUtils::getBaseURL(); ?>/css/iptraf.css?ver=<?php echo WORDFENCE_VERSION; ?>' type='text/css' media='all' />
6
  <body>
7
+ <h1>Wordfence: All recent hits for IP address <?php echo wp_kses($IP, array()); if($reverseLookup){ echo '[' . wp_kses($reverseLookup, array()) . ']'; } ?></h1>
8
  <table border="0" cellpadding="2" cellspacing="0" style="width: 900px;">
9
  <?php foreach($results as $key => $v){ ?>
10
  <tr><th>Time:</th><td><?php echo $v['timeAgo'] ?> ago -- <?php echo date(DATE_RFC822, $v['ctime']); ?> -- <?php echo $v['ctime']; ?> in Unixtime</td></tr>
12
  <?php if(wfUtils::hasXSS($v['URL'])){ ?>
13
  <tr><th>URL:</th><td><span style="color: #F00;">Possible XSS code filtered out for your security</span></td></tr>
14
  <?php } else { ?>
15
+ <tr><th>URL:</th><td><a href="<?php echo wp_kses($v['URL'], array()); ?>" target="_blank"><?php echo $v['URL']; ?></a></td></tr>
16
  <?php } ?>
17
  <tr><th>Type:</th><td><?php if($v['type'] == 'hit'){ echo 'Normal request'; } else if($v['type'] == '404'){ echo '<span style="color: #F00;">Page not found</span>'; } ?></td></tr>
18
  <?php if($v['referer']){ ?><tr><th>Referrer:</th><td><a href="<?php echo $v['referer']; ?>" target="_blank"><?php echo $v['referer']; ?></a></td></tr><?php } ?>
19
+ <tr><th>Full Browser ID:</th><td><?php echo wp_kses($v['UA'], array()); ?></td></tr>
20
  <?php if($v['user']){ ?>
21
  <tr><th>User:</th><td><a href="<?php echo $v['user']['editLink']; ?>" target="_blank"><?php echo $v['user']['avatar'] . ' ' . $v['user']['display_name']; ?></a></td></tr>
22
  <?php } ?>
lib/diffResult.php CHANGED
@@ -15,15 +15,15 @@
15
  ignore this file the next time Wordfence scans your system.
16
  </p>
17
  <table border="0" style="margin: 0 0 20px 0;" class="summary">
18
- <tr><td>Filename:</td><td><?php echo htmlentities($_GET['file']); ?></td></tr>
19
  <tr><td>File type:</td><td><?php
20
  $cType = $_GET['cType'];
21
  if($cType == 'core'){
22
  echo "WordPress Core File</td></tr>";
23
  } else if($cType == 'theme'){
24
- echo "Theme File</td></tr><tr><td>Theme Name:</td><td>" . htmlentities($_GET['cName']) . "</td></tr><tr><td>Theme Version:</td><td>" . htmlentities($_GET['cVersion']) . "</td></tr>";
25
  } else if($cType == 'plugin'){
26
- echo "Plugin File</td></tr><tr><td>Plugin Name:</td><td>" . htmlentities($_GET['cName']) . "</td></tr><tr><td>Plugin Version:</td><td>" . htmlentities($_GET['cVersion']) . "</td></tr>";
27
  } else {
28
  echo "Unknown Type</td></tr>";
29
  }
15
  ignore this file the next time Wordfence scans your system.
16
  </p>
17
  <table border="0" style="margin: 0 0 20px 0;" class="summary">
18
+ <tr><td>Filename:</td><td><?php echo wp_kses($_GET['file'], array()); ?></td></tr>
19
  <tr><td>File type:</td><td><?php
20
  $cType = $_GET['cType'];
21
  if($cType == 'core'){
22
  echo "WordPress Core File</td></tr>";
23
  } else if($cType == 'theme'){
24
+ echo "Theme File</td></tr><tr><td>Theme Name:</td><td>" . wp_kses($_GET['cName'], array()) . "</td></tr><tr><td>Theme Version:</td><td>" . wp_kses($_GET['cVersion'], array()) . "</td></tr>";
25
  } else if($cType == 'plugin'){
26
+ echo "Plugin File</td></tr><tr><td>Plugin Name:</td><td>" . wp_kses($_GET['cName'], array()) . "</td></tr><tr><td>Plugin Version:</td><td>" . wp_kses($_GET['cVersion'], array()) . "</td></tr>";
27
  } else {
28
  echo "Unknown Type</td></tr>";
29
  }
lib/menu_countryBlocking.php CHANGED
@@ -38,20 +38,20 @@ WFAD.countryMap = <?php echo json_encode($wfBulkCountries); ?>;
38
  <option value="redir"<?php if(wfConfig::get('cbl_action') == 'redir'){ echo ' selected'; } ?>>Redirect to the URL below</option>
39
  </select>
40
  </td></tr>
41
- <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" value="<?php if(wfConfig::get('cbl_redirURL')){ echo htmlspecialchars(wfConfig::get('cbl_redirURL')); } ?>" /></td></tr>
42
  <tr><th>Block countries even if they are logged in:</th><td><input type="checkbox" id="wfLoggedInBlocked" value="1" <?php if(wfConfig::get('cbl_loggedInBlocked')){ echo 'checked'; } ?> /></td></tr>
43
  <tr><th>Block access to the login form:</th><td><input type="checkbox" id="wfLoginFormBlocked" value="1" <?php if(wfConfig::get('cbl_loginFormBlocked')){ echo 'checked'; } ?> /></td></tr>
44
  <tr><th>Block access to the rest of the site (outside the login form):</th><td><input type="checkbox" id="wfRestOfSiteBlocked" value="1" <?php if(wfConfig::get('cbl_restOfSiteBlocked')){ echo 'checked'; } ?> /></td></tr>
45
  <tr><td colspan="2"><h2>Advanced Country Blocking Options</h2></td></tr>
46
  <tr><th colspan="2">
47
  If user hits the URL
48
- <input type="text" id="wfBypassRedirURL" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURL', "")); ?>" size="20" />
49
  then redirect that user to
50
- <input type="text" id="wfBypassRedirDest" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURLRedir', "")); ?>" size="20" /> and set a cookie that will bypass all country blocking.
51
  </th></tr>
52
  <tr><th colspan="2">
53
  If user who is allowed to access the site views the URL
54
- <input type="text" id="wfBypassViewURL" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURL', "")); ?>" size="20" />
55
  then set a cookie that will bypass country blocking in future in case that user hits the site from a blocked country.
56
  </th></tr>
57
 
38
  <option value="redir"<?php if(wfConfig::get('cbl_action') == 'redir'){ echo ' selected'; } ?>>Redirect to the URL below</option>
39
  </select>
40
  </td></tr>
41
+ <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" value="<?php if(wfConfig::get('cbl_redirURL')){ echo wp_kses(wfConfig::get('cbl_redirURL'), array()); } ?>" /></td></tr>
42
  <tr><th>Block countries even if they are logged in:</th><td><input type="checkbox" id="wfLoggedInBlocked" value="1" <?php if(wfConfig::get('cbl_loggedInBlocked')){ echo 'checked'; } ?> /></td></tr>
43
  <tr><th>Block access to the login form:</th><td><input type="checkbox" id="wfLoginFormBlocked" value="1" <?php if(wfConfig::get('cbl_loginFormBlocked')){ echo 'checked'; } ?> /></td></tr>
44
  <tr><th>Block access to the rest of the site (outside the login form):</th><td><input type="checkbox" id="wfRestOfSiteBlocked" value="1" <?php if(wfConfig::get('cbl_restOfSiteBlocked')){ echo 'checked'; } ?> /></td></tr>
45
  <tr><td colspan="2"><h2>Advanced Country Blocking Options</h2></td></tr>
46
  <tr><th colspan="2">
47
  If user hits the URL
48
+ <input type="text" id="wfBypassRedirURL" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirURL', ""), array()); ?>" size="20" />
49
  then redirect that user to
50
+ <input type="text" id="wfBypassRedirDest" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirDest', ""), array()); ?>" size="20" /> and set a cookie that will bypass all country blocking.
51
  </th></tr>
52
  <tr><th colspan="2">
53
  If user who is allowed to access the site views the URL
54
+ <input type="text" id="wfBypassViewURL" value="<?php echo wp_kses(wfConfig::get('cbl_bypassViewURL', ""), array()); ?>" size="20" />
55
  then set a cookie that will bypass country blocking in future in case that user hits the site from a blocked country.
56
  </th></tr>
57
 
lib/menu_options.php CHANGED
@@ -47,7 +47,12 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
47
  <tr><td colspan="2">&nbsp;</td></tr>
48
  <tr><th class="wfConfigEnable">Enable automatic scheduled scans</th><td><input type="checkbox" id="scheduledScansEnabled" class="wfConfigElem" name="scheduledScansEnabled" value="1" <?php $w->cb('scheduledScansEnabled'); ?> />&nbsp;Regular scans ensure your site stays secure.</td></tr>
49
  <tr><td colspan="2">&nbsp;</td></tr>
50
- <tr><th class="wfConfigEnable">Update Wordfence automatically when a new version is released?</th><td><input type="checkbox" id="autoUpdate" class="wfConfigElem" name="autoUpdate" value="1" <?php $w->cb('autoUpdate'); ?> />&nbsp;Automatically updates Wordfence to the newest version within 24 hours of a new release.</td></tr>
 
 
 
 
 
51
  <tr><td colspan="2">&nbsp;</td></tr>
52
 
53
  <tr><th>Where to email alerts:</th><td><input type="text" id="alertEmails" name="alertEmails" value="<?php $w->f('alertEmails'); ?>" size="50" />&nbsp;<span class="wfTipText">Separate multiple emails with commas</span></td></tr>
@@ -64,11 +69,11 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
64
  </td></tr>
65
  <tr><th>How does Wordfence get IPs:</th><td>
66
  <select id="howGetIPs" name="howGetIPs">
67
- <option value="">Set this option if you're seeing visitors from fake IP addresses or who appear to be from your internal network but aren't.</option>
68
- <option value="REMOTE_ADDR"<?php $w->sel('howGetIPs', 'REMOTE_ADDR'); ?>>Use PHP's built in REMOTE_ADDR. Use this if you're not using Nginx or any separate front-end proxy or firewall. Try this first.</option>
69
- <option value="HTTP_X_REAL_IP"<?php $w->sel('howGetIPs', 'HTTP_X_REAL_IP'); ?>>Use the X-Real-IP HTTP header which my Nginx, firewall or front-end proxy is setting. Try this next.</option>
70
- <option value="HTTP_X_FORWARDED_FOR"<?php $w->sel('howGetIPs', 'HTTP_X_FORWARDED_FOR'); ?>>Use the X-Forwarded-For HTTP header which my Nginx, firewall or front-end proxy is setting.</option>
71
- <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel('howGetIPs', 'HTTP_CF_CONNECTING_IP'); ?>>I'm using Cloudflare so use the "CF-Connecting-IP" HTTP header to get a visitor IP</option>
72
  </select>
73
  </td></tr>
74
  </table>
@@ -261,7 +266,9 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
261
  <tr><th colspan="2" style="color: #999;">Whitelisted IP's must be separated by commas. You can specify ranges using the following format: 123.23.34.[1-50]<br />Wordfence automatically whitelists <a href="http://en.wikipedia.org/wiki/Private_network" target="_blank">private networks</a> because these are not routable on the public Internet.<br /><br /></th></tr>
262
 
263
  <tr><th>Immediately block IP's that access these URLs:</th><td><input type="text" name="bannedURLs" id="bannedURLs" value="<?php echo $w->getHTML('bannedURLs'); ?>" size="40" /></td></tr>
264
- <tr><th colspan="2" style="color: #999;">Separate multiple URL's with commas. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them.<br /><br /></th></tr>
 
 
265
 
266
  <tr><th>Hide WordPress version</th><td><input type="checkbox" id="other_hideWPVersion" class="wfConfigElem" name="other_hideWPVersion" value="1" <?php $w->cb('other_hideWPVersion'); ?> /></td></tr>
267
  <tr><th>Hold anonymous comments using member emails for moderation</th><td><input type="checkbox" id="other_noAnonMemberComments" class="wfConfigElem" name="other_noAnonMemberComments" value="1" <?php $w->cb('other_noAnonMemberComments'); ?> /></td></tr>
47
  <tr><td colspan="2">&nbsp;</td></tr>
48
  <tr><th class="wfConfigEnable">Enable automatic scheduled scans</th><td><input type="checkbox" id="scheduledScansEnabled" class="wfConfigElem" name="scheduledScansEnabled" value="1" <?php $w->cb('scheduledScansEnabled'); ?> />&nbsp;Regular scans ensure your site stays secure.</td></tr>
49
  <tr><td colspan="2">&nbsp;</td></tr>
50
+ <tr><th class="wfConfigEnable">Update Wordfence automatically when a new version is released?</th><td><input type="checkbox" id="autoUpdate" class="wfConfigElem" name="autoUpdate" value="1" <?php $w->cb('autoUpdate'); ?> />&nbsp;Automatically updates Wordfence to the newest version within 24 hours of a new release.<br />
51
+ <?php if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){ ?>
52
+ <span style="color: #F00;">Warning: </span>You are running LiteSpeed web server and you don't have the "noabort" variable set in your .htaccess.<br />
53
+ <a href="https://support.wordfence.com/solution/articles/1000129050-running-wordfence-under-litespeed-web-server-and-preventing-process-killing-or" target="_blank">Please read this article in our FAQ to make an important change that will ensure your site stability during an update.<br />
54
+ <?php } ?>
55
+ </td></tr>
56
  <tr><td colspan="2">&nbsp;</td></tr>
57
 
58
  <tr><th>Where to email alerts:</th><td><input type="text" id="alertEmails" name="alertEmails" value="<?php $w->f('alertEmails'); ?>" size="50" />&nbsp;<span class="wfTipText">Separate multiple emails with commas</span></td></tr>
69
  </td></tr>
70
  <tr><th>How does Wordfence get IPs:</th><td>
71
  <select id="howGetIPs" name="howGetIPs">
72
+ <option value="">Let Wordfence use the most secure method to get visitor IP addresses. Prevents spoofing and works with most sites.</option>
73
+ <option value="REMOTE_ADDR"<?php $w->sel('howGetIPs', 'REMOTE_ADDR'); ?>>Use PHP's built in REMOTE_ADDR and don't use anything else. Very secure if this is compatible with your site.</option>
74
+ <option value="HTTP_X_FORWARDED_FOR"<?php $w->sel('howGetIPs', 'HTTP_X_FORWARDED_FOR'); ?>>Use the X-Forwarded-For HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
75
+ <option value="HTTP_X_REAL_IP"<?php $w->sel('howGetIPs', 'HTTP_X_REAL_IP'); ?>>Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
76
+ <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel('howGetIPs', 'HTTP_CF_CONNECTING_IP'); ?>>Use the Cloudflare "CF-Connecting-IP" HTTP header to get a visitor IP. Only use if you're using Cloudflare.</option>
77
  </select>
78
  </td></tr>
79
  </table>
266
  <tr><th colspan="2" style="color: #999;">Whitelisted IP's must be separated by commas. You can specify ranges using the following format: 123.23.34.[1-50]<br />Wordfence automatically whitelists <a href="http://en.wikipedia.org/wiki/Private_network" target="_blank">private networks</a> because these are not routable on the public Internet.<br /><br /></th></tr>
267
 
268
  <tr><th>Immediately block IP's that access these URLs:</th><td><input type="text" name="bannedURLs" id="bannedURLs" value="<?php echo $w->getHTML('bannedURLs'); ?>" size="40" /></td></tr>
269
+ <tr><th colspan="2" style="color: #999;">Separate multiple URL's with commas. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them.<br />
270
+ All URL's must start with a '/' without quotes and must be relative. e.g. /badURLone/, /bannedPage.html, /dont-access/this/URL/
271
+ <br /><br /></th></tr>
272
 
273
  <tr><th>Hide WordPress version</th><td><input type="checkbox" id="other_hideWPVersion" class="wfConfigElem" name="other_hideWPVersion" value="1" <?php $w->cb('other_hideWPVersion'); ?> /></td></tr>
274
  <tr><th>Hold anonymous comments using member emails for moderation</th><td><input type="checkbox" id="other_noAnonMemberComments" class="wfConfigElem" name="other_noAnonMemberComments" value="1" <?php $w->cb('other_noAnonMemberComments'); ?> /></td></tr>
lib/menu_rangeBlocking.php CHANGED
@@ -16,7 +16,7 @@
16
  </ul>
17
  </div>
18
  <table class="wfConfigForm">
19
- <tr><th>Block anyone that has an IP address in this range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php if( isset( $_GET['wfBlockRange'] ) && $_GET['wfBlockRange']){ echo htmlentities($_GET['wfBlockRange']); } ?>" onkeyup="WFAD.calcRangeTotal();">&nbsp;<span id="wfShowRangeTotal"></span></td></tr>
20
  <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> 192.168.200.200 - 192.168.200.220</td></tr>
21
  <tr><th>...you can also enter a User-Agent (browser) that matches:</th><td><input id="uaRange" type="text" size="30" maxlength="255" >&nbsp;(Case insensitive)</td></tr>
22
  <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> *badRobot*, AnotherBadRobot*, *someKindOfSuffix</td></tr>
16
  </ul>
17
  </div>
18
  <table class="wfConfigForm">
19
+ <tr><th>Block anyone that has an IP address in this range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php if( isset( $_GET['wfBlockRange'] ) && $_GET['wfBlockRange']){ echo wp_kses($_GET['wfBlockRange'], array()); } ?>" onkeyup="WFAD.calcRangeTotal();">&nbsp;<span id="wfShowRangeTotal"></span></td></tr>
20
  <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> 192.168.200.200 - 192.168.200.220</td></tr>
21
  <tr><th>...you can also enter a User-Agent (browser) that matches:</th><td><input id="uaRange" type="text" size="30" maxlength="255" >&nbsp;(Case insensitive)</td></tr>
22
  <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> *badRobot*, AnotherBadRobot*, *someKindOfSuffix</td></tr>
lib/menu_whois.php CHANGED
@@ -28,7 +28,7 @@ if(! function_exists('fsockopen')){
28
  <?php if( isset( $_GET['wfnetworkblock'] ) && $_GET['wfnetworkblock']){ ?>
29
  <h2>How to block a network</h2>
30
  <p style="width: 600px;">
31
- You've chosen to block the network that <span style="color: #F00;"><?php echo htmlentities($_GET['whoisval']); ?></span> is part of.
32
  We've marked the networks we found that this IP address belongs to in red below.
33
  Make sure you read all the WHOIS information so that you see all networks this IP belongs to. We recommend blocking the network with the lowest number of addresses.
34
  You may find this is listed at the end as part of the 'rWHOIS' query which contacts
@@ -51,7 +51,7 @@ if(! function_exists('fsockopen')){
51
  </div>
52
  </script>
53
  <script type="text/javascript">
54
- var whoisval = "<?php if( isset( $_GET['whoisval'] ) ) { echo htmlentities($_GET['whoisval']); } ?>";
55
  if(whoisval){
56
  jQuery(function(){
57
  jQuery('#wfwhois').val(whoisval);
28
  <?php if( isset( $_GET['wfnetworkblock'] ) && $_GET['wfnetworkblock']){ ?>
29
  <h2>How to block a network</h2>
30
  <p style="width: 600px;">
31
+ You've chosen to block the network that <span style="color: #F00;"><?php echo wp_kses($_GET['whoisval'], array()); ?></span> is part of.
32
  We've marked the networks we found that this IP address belongs to in red below.
33
  Make sure you read all the WHOIS information so that you see all networks this IP belongs to. We recommend blocking the network with the lowest number of addresses.
34
  You may find this is listed at the end as part of the 'rWHOIS' query which contacts
51
  </div>
52
  </script>
53
  <script type="text/javascript">
54
+ var whoisval = "<?php if( isset( $_GET['whoisval'] ) ) { echo wp_kses($_GET['whoisval'], array()); } ?>";
55
  if(whoisval){
56
  jQuery(function(){
57
  jQuery('#wfwhois').val(whoisval);
lib/viewFullActivityLog.php CHANGED
@@ -17,7 +17,7 @@ $q = $db->querySelect("select ctime, level, type, msg from $table order by ctime
17
  $timeOffset = 3600 * get_option('gmt_offset');
18
  foreach($q as $r){
19
  if($r['level'] < 4 || $debugOn){
20
- echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
21
  }
22
  }
23
  ?>
17
  $timeOffset = 3600 * get_option('gmt_offset');
18
  foreach($q as $r){
19
  if($r['level'] < 4 || $debugOn){
20
+ echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . wp_kses($r['msg'], array()) . "</div>\n";
21
  }
22
  }
23
  ?>
lib/wfCache.php CHANGED
@@ -154,8 +154,8 @@ class wfCache {
154
  $append .= "Time created on server: " . date('Y-m-d H:i:s T') . ". ";
155
  $append .= "Is HTTPS page: " . (self::isHTTPSPage() ? 'HTTPS' : 'no') . ". ";
156
  $append .= "Page size: " . strlen($buffer) . " bytes. ";
157
- $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? htmlentities($_SERVER['HTTP_HOST']) : htmlentities($_SERVER['SERVER_NAME'])) . ". ";
158
- $append .= "Request URI: " . htmlentities($_SERVER['REQUEST_URI']) . " ";
159
  $appendGzip = $append . " Encoding: GZEncode -->\n";
160
  $append .= " Encoding: Uncompressed -->\n";
161
  }
@@ -193,7 +193,7 @@ class wfCache {
193
  public static function makeDirIfNeeded($file){
194
  $file = preg_replace('/\/[^\/]*$/', '', $file);
195
  if(! is_dir($file)){
196
- mkdir($file, 0755, true);
197
  }
198
  }
199
  public static function logout(){
154
  $append .= "Time created on server: " . date('Y-m-d H:i:s T') . ". ";
155
  $append .= "Is HTTPS page: " . (self::isHTTPSPage() ? 'HTTPS' : 'no') . ". ";
156
  $append .= "Page size: " . strlen($buffer) . " bytes. ";
157
+ $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? wp_kses($_SERVER['HTTP_HOST'], array()) : wp_kses($_SERVER['SERVER_NAME'], array())) . ". ";
158
+ $append .= "Request URI: " . wp_kses($_SERVER['REQUEST_URI'], array()) . " ";
159
  $appendGzip = $append . " Encoding: GZEncode -->\n";
160
  $append .= " Encoding: Uncompressed -->\n";
161
  }
193
  public static function makeDirIfNeeded($file){
194
  $file = preg_replace('/\/[^\/]*$/', '', $file);
195
  if(! is_dir($file)){
196
+ @mkdir($file, 0755, true);
197
  }
198
  }
199
  public static function logout(){
lib/wfConfig.php CHANGED
@@ -464,7 +464,7 @@ class wfConfig {
464
  self::$cache = array();
465
  }
466
  public static function getHTML($key){
467
- return htmlspecialchars(self::get($key));
468
  }
469
  public static function inc($key){
470
  $val = self::get($key, false);
@@ -751,6 +751,16 @@ class wfConfig {
751
  }
752
  public static function autoUpdate(){
753
  try {
 
 
 
 
 
 
 
 
 
 
754
  require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
755
  require_once(ABSPATH . 'wp-admin/includes/misc.php');
756
  /* We were creating show_message here so that WP did not write to STDOUT. This had the strange effect of throwing an error about redeclaring show_message function, but only when a crawler hit the site and triggered the cron job. Not a human. So we're now just require'ing misc.php which does generate output, but that's OK because it is a loopback cron request.
464
  self::$cache = array();
465
  }
466
  public static function getHTML($key){
467
+ return wp_kses(self::get($key), array());
468
  }
469
  public static function inc($key){
470
  $val = self::get($key, false);
751
  }
752
  public static function autoUpdate(){
753
  try {
754
+ if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){
755
+ wordfence::alert("Wordfence Upgrade not run. Please modify your .htaccess", "To preserve the integrity of your website we are not running Wordfence auto-update.\n" .
756
+ "You are running the LiteSpeed web server which has been known to cause a problem with Wordfence auto-update.\n" .
757
+ "Please go to your website now and make a minor change to your .htaccess to fix this.\n" .
758
+ "You can find out how to make this change at:\n" .
759
+ "https://support.wordfence.com/solution/articles/1000129050-running-wordfence-under-litespeed-web-server-and-preventing-process-killing-or\n" .
760
+ "\nAlternatively you can disable auto-update on your website to stop receiving this message and upgrade Wordfence manually.\n"
761
+ );
762
+ return;
763
+ }
764
  require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
765
  require_once(ABSPATH . 'wp-admin/includes/misc.php');
766
  /* We were creating show_message here so that WP did not write to STDOUT. This had the strange effect of throwing an error about redeclaring show_message function, but only when a crawler hit the site and triggered the cron job. Not a human. So we're now just require'ing misc.php which does generate output, but that's OK because it is a loopback cron request.
lib/wfLog.php CHANGED
@@ -164,6 +164,11 @@ class wfLog {
164
  if(wfUtils::isPrivateAddress($IP)){
165
  return true;
166
  }
 
 
 
 
 
167
  $list = wfConfig::get('whitelisted');
168
  if(! $list){ return false; }
169
  $list = explode(',', $list);
@@ -235,7 +240,7 @@ class wfLog {
235
  }
236
  $blockDat = explode('|', $elem['blockString']);
237
  $elem['ipPattern'] = "";
238
- $haveIPBLock = false;
239
  $haveBrowserBlock = false;
240
  if($blockDat[0]){
241
  $haveIPBlock = true;
@@ -862,6 +867,7 @@ class wfLog {
862
  foreach($results as &$rec){
863
  //$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
864
  $rec['date'] = date('M d H:i:s', $rec['ctime'] + $timeOffset);
 
865
  }
866
  return $results;
867
  }
164
  if(wfUtils::isPrivateAddress($IP)){
165
  return true;
166
  }
167
+ //These belong to sucuri's scanning servers which will get blocked by Wordfence as a false positive if you try a scan. So we whitelisted them.
168
+ $externalWhite = array('97.74.127.171','69.164.203.172','173.230.128.135','66.228.34.49','66.228.40.185','50.116.36.92','50.116.36.93','50.116.3.171','198.58.96.212','50.116.63.221','192.155.92.112','192.81.128.31','198.58.106.244','192.155.95.139','23.239.9.227','198.58.112.103','192.155.94.43','162.216.16.33','173.255.233.124','173.255.233.124','192.155.90.179','50.116.41.217','192.81.129.227','198.58.111.80');
169
+ if(in_array($IP, $externalWhite)){
170
+ return true;
171
+ }
172
  $list = wfConfig::get('whitelisted');
173
  if(! $list){ return false; }
174
  $list = explode(',', $list);
240
  }
241
  $blockDat = explode('|', $elem['blockString']);
242
  $elem['ipPattern'] = "";
243
+ $haveIPBlock = false;
244
  $haveBrowserBlock = false;
245
  if($blockDat[0]){
246
  $haveIPBlock = true;
867
  foreach($results as &$rec){
868
  //$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
869
  $rec['date'] = date('M d H:i:s', $rec['ctime'] + $timeOffset);
870
+ $rec['msg'] = wp_kses_data( (string) $rec['msg']);
871
  }
872
  return $results;
873
  }
lib/wfSchema.php CHANGED
@@ -122,6 +122,7 @@ class wfSchema {
122
  KEY k2(endTime)
123
  ) default charset=utf8",
124
  "wfStatus" => "(
 
125
  ctime DOUBLE(17,6) UNSIGNED NOT NULL,
126
  level tinyint UNSIGNED NOT NULL,
127
  type char(5) NOT NULL,
122
  KEY k2(endTime)
123
  ) default charset=utf8",
124
  "wfStatus" => "(
125
+ id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY,
126
  ctime DOUBLE(17,6) UNSIGNED NOT NULL,
127
  level tinyint UNSIGNED NOT NULL,
128
  type char(5) NOT NULL,
lib/wfUnlockMsg.php CHANGED
@@ -1,6 +1,7 @@
1
  If you are a site administrator and have been accidentally locked out, please enter your email in the box below and click "Send". If the email address you enter belongs to a known site administrator or someone set to receive Wordfence alerts, we will send you an email to help you regain access. <a href="http://www.wordfence.com/docs/frequently-asked-questions/#3" target="_blank">Please read our FAQ if this does not work.</a>
2
  <br /><br />
3
  <form method="POST" action="<?php echo wfUtils::getSiteBaseURL(); ?>?_wfsf=unlockEmail">
 
4
  <input type="text" size="50" name="email" value="" maxlength="255" />&nbsp;<input type="submit" name="s" value="Send me an unlock email" />
5
  </form>
6
  <br /><br />
1
  If you are a site administrator and have been accidentally locked out, please enter your email in the box below and click "Send". If the email address you enter belongs to a known site administrator or someone set to receive Wordfence alerts, we will send you an email to help you regain access. <a href="http://www.wordfence.com/docs/frequently-asked-questions/#3" target="_blank">Please read our FAQ if this does not work.</a>
2
  <br /><br />
3
  <form method="POST" action="<?php echo wfUtils::getSiteBaseURL(); ?>?_wfsf=unlockEmail">
4
+ <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('wf-form'); ?>" />
5
  <input type="text" size="50" name="email" value="" maxlength="255" />&nbsp;<input type="submit" name="s" value="Send me an unlock email" />
6
  </form>
7
  <br /><br />
lib/wfUtils.php CHANGED
@@ -106,21 +106,11 @@ class wfUtils {
106
  //return ABSPATH . 'wp-content/plugins/';
107
  }
108
  public static function defaultGetIP(){
109
- $IP = 0;
110
- if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
111
- $IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
112
- if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
113
- }
114
- if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['HTTP_X_REAL_IP'])){
115
- $IP = $_SERVER['HTTP_X_REAL_IP'];
116
- if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
117
- }
118
- if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['REMOTE_ADDR'])){
119
- $IP = $_SERVER['REMOTE_ADDR'];
120
- if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
121
- }
122
  return $IP;
123
  }
 
 
 
124
  public static function isPrivateAddress($addr){
125
  $num = self::inet_aton($addr);
126
  foreach(self::$privateAddrs as $a){
@@ -130,80 +120,78 @@ class wfUtils {
130
  }
131
  return false;
132
  }
133
- public static function makeRandomIP(){
134
- return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
135
- }
136
- public static function getIP(){
137
- //You can use the following examples to force Wordfence to think a visitor has a certain IP if you're testing. Remember to re-comment this out or you will break Wordfence badly.
138
- //return '1.2.33.57';
139
- //return '4.22.23.114';
140
- //return self::makeRandomIP();
141
-
142
- $howGet = wfConfig::get('howGetIPs', false);
143
- if($howGet){
144
- $IP = $_SERVER[$howGet];
145
- if( $howGet == "HTTP_CF_CONNECTING_IP" && (! self::isValidIP($IP)) ){
146
- $IP = $_SERVER['REMOTE_ADDR'];
147
- }
148
- } else {
149
- $IP = wfUtils::defaultGetIP();
150
- }
151
- if(preg_match('/,/', $IP)){
152
- $parts = explode(',', $IP); //Some users have "unknown,100.100.100.100" for example so we take the first thing that looks like an IP.
153
- foreach($parts as $part){
154
- if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $part) && (! self::isPrivateAddress($part)) ){
155
- $IP = trim($part);
156
- break;
157
  }
 
158
  }
159
- } else if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)\s+(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)){
160
- $parts = explode(' ', $IP); //Some users have "unknown 100.100.100.100" for example so we take the first thing that looks like an IP.
161
- foreach($parts as $part){
162
- if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $part) && (! self::isPrivateAddress($part)) ){
163
- $IP = trim($part);
 
 
 
 
 
 
 
 
 
 
164
  break;
165
  }
166
  }
167
-
168
- }
169
- if(preg_match('/:\d+$/', $IP)){
170
- $IP = preg_replace('/:\d+$/', '', $IP);
171
- }
172
- if(self::isValidIP($IP)){
173
- if(wfConfig::get('IPGetFail', false)){
174
- if(self::isPrivateAddress($IP) ){
175
- wordfence::status(1, 'error', "Wordfence is receiving IP addresses, but we received an internal IP of $IP so your config may still be incorrect.");
176
  } else {
177
- wordfence::status(1, 'error', "Wordfence is now receiving IP addresses correctly. We received $IP from a visitor.");
178
  }
179
- wfConfig::set('IPGetFail', '');
180
  }
181
- return $IP;
 
 
182
  } else {
183
- $xFor = "";
184
- if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) ){
185
- $xFor = $_SERVER['HTTP_X_FORWARDED_FOR'];
186
- }
187
- $msg = "Wordfence can't get the IP of clients and therefore can't operate. We received IP: $IP. X-Forwarded-For was: " . $xFor . " REMOTE_ADDR was: " . $_SERVER['REMOTE_ADDR'];
188
- $possible = array();
189
- foreach($_SERVER as $key => $val){
190
- if(is_string($val) && preg_match('/^\d+\.\d+\.\d+\.\d+/', $val) && strlen($val) < 255){
191
- if($val != '127.0.0.1'){
192
- $possible[$key] = $val;
193
- }
194
- }
195
- }
196
- if(sizeof($possible) > 0){
197
- $msg .= " Headers that may contain the client IP: ";
198
- foreach($possible as $key => $val){
199
- $msg .= "$key => $val ";
200
- }
201
- }
202
- wordfence::status(1, 'error', $msg);
203
- wfConfig::set('IPGetFail', 1);
204
  return false;
205
  }
206
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  public static function isValidIP($IP){
208
  if(preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $IP, $m)){
209
  if(
106
  //return ABSPATH . 'wp-content/plugins/';
107
  }
108
  public static function defaultGetIP(){
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  return $IP;
110
  }
111
+ public static function makeRandomIP(){
112
+ return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
113
+ }
114
  public static function isPrivateAddress($addr){
115
  $num = self::inet_aton($addr);
116
  foreach(self::$privateAddrs as $a){
120
  }
121
  return false;
122
  }
123
+ private static function getCleanIP($arr){ //Expects an array of items. The items are either IP's or IP's separated by comma, space or tab. Or an array of IP's.
124
+ // We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found.
125
+ $privates = array(); //Store private addrs until end as last resort.
126
+ for($i = 0; $i < count($arr); $i++){
127
+ $item = $arr[$i];
128
+ if(is_array($item)){
129
+ foreach($item as $j){
130
+ $j = preg_replace('/:\d+$/', '', $j); //Strip off port
131
+ if(self::isValidIP($j)){
132
+ if(self::isPrivateAddress($j)){
133
+ $privates[] = $j;
134
+ } else {
135
+ return $j;
136
+ }
137
+ }
 
 
 
 
 
 
 
 
 
138
  }
139
+ continue; //This was an array so we can skip to the next item
140
  }
141
+ $skipToNext = false;
142
+ foreach(array(',', ' ', "\t") as $char){
143
+ if(strpos($item, $char) !== false){
144
+ $sp = explode($char, $item);
145
+ foreach($sp as $j){
146
+ $j = preg_replace('/:\d+$/', '', $j); //Strip off port
147
+ if(self::isValidIP($j)){
148
+ if(self::isPrivateAddress($j)){
149
+ $privates[] = $j;
150
+ } else {
151
+ return $j;
152
+ }
153
+ }
154
+ }
155
+ $skipToNext = true;
156
  break;
157
  }
158
  }
159
+ if($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything.
160
+
161
+ $item = preg_replace('/:\d+$/', '', $item); //Strip off port
162
+ if(self::isValidIP($item)){
163
+ if(self::isPrivateAddress($item)){
164
+ $privates[] = $item;
 
 
 
165
  } else {
166
+ return $item;
167
  }
 
168
  }
169
+ }
170
+ if(sizeof($privates) > 0){
171
+ return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function.
172
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  return false;
174
  }
175
  }
176
+ public static function getIP(){
177
+ //For debugging.
178
+ //return '105.2.33.57';
179
+ //return self::makeRandomIP();
180
+ $howGet = wfConfig::get('howGetIPs', false);
181
+ if($howGet){
182
+ if($howGet == 'REMOTE_ADDR'){
183
+ $IP = self::getCleanIP(array($_SERVER['REMOTE_ADDR']));
184
+ } else {
185
+ $IP = self::getCleanIP(array($_SERVER[$howGet], $_SERVER['REMOTE_ADDR']));
186
+ }
187
+ } else {
188
+ $IPs = array($_SERVER['REMOTE_ADDR']);
189
+ if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ $IPs[] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
190
+ if(isset($_SERVER['HTTP_X_REAL_IP'])){ $IPs[] = $_SERVER['HTTP_X_REAL_IP']; }
191
+ $IP = self::getCleanIP($IPs);
192
+ }
193
+ return $IP; //Returns a valid IP or false.
194
+ }
195
  public static function isValidIP($IP){
196
  if(preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $IP, $m)){
197
  if(
lib/wfViewResult.php CHANGED
@@ -6,7 +6,7 @@
6
  <body>
7
  <h1>Wordfence: File Viewer</h1>
8
  <table border="0" style="margin: 0 0 20px 0;" class="summary">
9
- <tr><td>Filename:</td><td><?php echo htmlspecialchars($localFile, ENT_QUOTES, 'UTF-8'); ?></td></tr>
10
  <tr><td>File Size:</td><td><?php echo $fileSize; ?></td></tr>
11
  <tr><td>File last modified:</td><td><?php echo $fileMTime; ?></td></tr>
12
  </table>
6
  <body>
7
  <h1>Wordfence: File Viewer</h1>
8
  <table border="0" style="margin: 0 0 20px 0;" class="summary">
9
+ <tr><td>Filename:</td><td><?php echo wp_kses($localFile, array()); ?></td></tr>
10
  <tr><td>File Size:</td><td><?php echo $fileSize; ?></td></tr>
11
  <tr><td>File last modified:</td><td><?php echo $fileMTime; ?></td></tr>
12
  </table>
lib/wordfenceClass.php CHANGED
@@ -298,6 +298,8 @@ class wordfence {
298
  $db->queryWriteIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
299
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileQueue");
300
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileChanges");
 
 
301
 
302
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_options'");
303
  if($optScanEnabled != '0' && $optScanEnabled != '1'){
@@ -533,7 +535,7 @@ class wordfence {
533
  }
534
 
535
  if(! is_array($returnArr)){
536
- error_log("Function $func did not return an array and did not generate an error.");
537
  $returnArr = array();
538
  }
539
  if(isset($returnArr['nonce'])){
@@ -615,7 +617,7 @@ class wordfence {
615
  $user = get_user_by('email', $_POST['user_login']);
616
  if($user){
617
  if(wfConfig::get('alertOn_lostPasswdForm')){
618
- wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email", $IP);
619
  }
620
  }
621
  if(wfConfig::get('loginSecurityEnabled')){
@@ -647,6 +649,9 @@ class wordfence {
647
  public static function veryFirstAction(){
648
  $wfFunc = @$_GET['_wfsf'];
649
  if($wfFunc == 'unlockEmail'){
 
 
 
650
  $numTries = get_transient('wordfenceUnlockTries');
651
  if($numTries > 10){
652
  echo "<html><body><h1>Please wait 3 minutes and try again</h1><p>You have used this form too much. Please wait 3 minutes and try again.</p></body></html>";
@@ -689,7 +694,7 @@ class wordfence {
689
  ));
690
  wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
691
  }
692
- echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>";
693
  exit();
694
  } else if($wfFunc == 'unlockAccess'){
695
  if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', get_transient('wfunlock_' . $_GET['key']))){
@@ -1009,7 +1014,7 @@ class wordfence {
1009
  throw new Exception("We could not fetch a core WordPress file from the Wordfence API.");
1010
  }
1011
  } catch (Exception $e){
1012
- return array('errorMsg' => htmlentities($e->getMessage()));
1013
  }
1014
  }
1015
  public static function ajax_loadAvgSitePerf_callback(){
@@ -1024,8 +1029,8 @@ class wordfence {
1024
  if(! wfConfig::get('isPaid')){
1025
  return array('errorMsg' => 'Cellphone Sign-in is only available to paid members. <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click here to upgrade now.</a>');
1026
  }
1027
- $username = $_POST['username'];
1028
- $phone = $_POST['phone'];
1029
  $user = get_user_by('login', $username);
1030
  if(! $user){
1031
  return array('errorMsg' => "The username you specified does not exist.");
@@ -1037,12 +1042,12 @@ class wordfence {
1037
  try {
1038
  $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $phone));
1039
  } catch(Exception $e){
1040
- return array('errorMsg' => "Could not contact Wordfence servers to generate a verification code: " . htmlentities($e->getMessage()) );
1041
  }
1042
  if(isset($codeResult['ok']) && $codeResult['ok']){
1043
  $code = $codeResult['code'];
1044
  } else if(isset($codeResult['errorMsg']) && $codeResult['errorMsg']){
1045
- return array('errorMsg' => htmlentities($codeResult['errorMsg']));
1046
  } else {
1047
  return array('errorMsg' => "We could not generate a verification code.");
1048
  }
@@ -1055,8 +1060,8 @@ class wordfence {
1055
  );
1056
  }
1057
  public static function ajax_twoFacActivate_callback(){
1058
- $userID = $_POST['userID'];
1059
- $code = $_POST['code'];
1060
  $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
1061
  if(! is_array($twoFactorUsers)){
1062
  $twoFactorUsers = array();
@@ -1071,7 +1076,7 @@ class wordfence {
1071
  $user = $twoFactorUsers[$i];
1072
  break;
1073
  } else {
1074
- return array('errorMsg' => "That is not the correct code. Please look for an SMS containing an activation code on the phone with number: " . htmlentities($twoFactorUsers[$i][1]) );
1075
  }
1076
  }
1077
  }
@@ -1281,7 +1286,7 @@ class wordfence {
1281
  if($r['type'] == 'error'){
1282
  $content .= "\n";
1283
  }
1284
- $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . $r['msg'] . "\n";
1285
  }
1286
  $content .= "\n\n";
1287
 
@@ -1312,7 +1317,7 @@ class wordfence {
1312
  throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
1313
  }
1314
  } catch(Exception $e){
1315
- return array('errorMsg' => "Could not fetch free API key from Wordfence: " . htmlentities($e->getMessage()));
1316
  }
1317
  return array('ok' => 1);
1318
  }
@@ -1622,7 +1627,7 @@ class wordfence {
1622
  }
1623
  }
1624
  if(sizeof($badEmails) > 0){
1625
- return array('errorMsg' => "The following emails are invalid: " . htmlentities(implode(', ', $badEmails)) );
1626
  }
1627
  $opts['alertEmails'] = implode(',', $emails);
1628
  } else {
@@ -1643,7 +1648,7 @@ class wordfence {
1643
  }
1644
  }
1645
  if(sizeof($badWhiteIPs) > 0){
1646
- return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . htmlentities(implode(', ', $badWhiteIPs)) );
1647
  }
1648
  $opts['whitelisted'] = implode(',', $whiteIPs);
1649
  } else {
@@ -1680,7 +1685,7 @@ class wordfence {
1680
  }
1681
 
1682
  if(sizeof($invalidUsers) > 0){
1683
- return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . htmlentities(implode(', ', $invalidUsers)) );
1684
  }
1685
  if(sizeof($validUsers) > 0){
1686
  $opts['liveTraf_ignoreUsers'] = implode(',', $validUsers);
@@ -1700,7 +1705,7 @@ class wordfence {
1700
  }
1701
  }
1702
  if(sizeof($invalidIPs) > 0){
1703
- return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . htmlentities(implode(', ', $invalidIPs)) );
1704
  }
1705
  if(sizeof($validIPs) > 0){
1706
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
@@ -1757,7 +1762,7 @@ class wordfence {
1757
  throw new Exception("We could not understand the Wordfence server's response because it did not contain an 'ok' and 'apiKey' element.");
1758
  }
1759
  } catch(Exception $e){
1760
- return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . htmlentities($e->getMessage()) );
1761
  }
1762
  } else if($opts['apiKey'] != wfConfig::get('apiKey')){
1763
  $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
@@ -1774,7 +1779,7 @@ class wordfence {
1774
  throw new Exception("We could not understand the Wordfence API server reply when updating your API key.");
1775
  }
1776
  } catch (Exception $e){
1777
- return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . htmlentities($e->getMessage()) );
1778
  }
1779
  } else {
1780
  $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
@@ -1853,7 +1858,7 @@ class wordfence {
1853
  }
1854
  $clientIP = wfUtils::inet_aton(wfUtils::getIP());
1855
  if($ip1 <= $clientIP && $ip2 >= $clientIP){
1856
- return array('err' => 1, 'errorMsg' => "You are trying to block yourself. Your IP address is " . htmlentities(wfUtils::getIP()) . " which falls into the range " . htmlentities($ipRange) . ". This blocking action has been cancelled so that you don't block yourself from your website.");
1857
  }
1858
  $ipRange = $ip1 . '-' . $ip2;
1859
  }
@@ -1881,7 +1886,7 @@ class wordfence {
1881
  return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
1882
  }
1883
  if(self::getLog()->isWhitelisted($IP)){
1884
- return array('err' => 1, 'errorMsg' => "The IP address " . htmlentities($IP) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
1885
  }
1886
  if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
1887
  if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){
@@ -1956,7 +1961,7 @@ class wordfence {
1956
  $issues = new wfIssues();
1957
  $jsonData = array(
1958
  'serverTime' => $serverTime,
1959
- 'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1")
1960
  );
1961
  $events = array();
1962
  $alsoGet = $_POST['alsoGet'];
@@ -2001,13 +2006,14 @@ class wordfence {
2001
  return array('ok' => 1, 'email' => $email);
2002
  }
2003
  public static function ajax_bulkOperation_callback(){
2004
- $op = $_POST['op'];
2005
  if($op == 'del' || $op == 'repair'){
2006
  $ids = $_POST['ids'];
2007
  $filesWorkedOn = 0;
2008
  $errors = array();
2009
  $issues = new wfIssues();
2010
  foreach($ids as $id){
 
2011
  $issue = $issues->getIssueByID($id);
2012
  if(! $issue){
2013
  $errors[] = "Could not delete one of the files because we could not find the issue. Perhaps it's been resolved?";
@@ -2017,7 +2023,7 @@ class wordfence {
2017
  $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
2018
  $localFile = realpath($localFile);
2019
  if(strpos($localFile, ABSPATH) !== 0){
2020
- $errors[] = "An invalid file was requested: " . htmlentities($file);
2021
  continue;
2022
  }
2023
  if($op == 'del'){
@@ -2026,7 +2032,7 @@ class wordfence {
2026
  $filesWorkedOn++;
2027
  } else {
2028
  $err = error_get_last();
2029
- $errors[] = "Could not delete file " . htmlentities($file) . ". Error was: " . htmlentities($err['message']);
2030
  }
2031
  } else if($op == 'repair'){
2032
  $dat = $issue['data'];
@@ -2035,21 +2041,21 @@ class wordfence {
2035
  $errors[] = $result['cerrorMsg'];
2036
  continue;
2037
  } else if(! $result['fileContent']){
2038
- $errors[] = "We could not get the original file of " . htmlentities($file) . " to do a repair.";
2039
  continue;
2040
  }
2041
 
2042
  if(preg_match('/\.\./', $file)){
2043
- $errors[] = "An invalid file " . htmlentities($file) . " was specified for repair.";
2044
  continue;
2045
  }
2046
  $fh = fopen($localFile, 'w');
2047
  if(! $fh){
2048
  $err = error_get_last();
2049
  if(preg_match('/Permission denied/i', $err['message'])){
2050
- $errMsg = "You don't have permission to repair " . htmlentities($file) . ". You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
2051
  } else {
2052
- $errMsg = "We could not write to " . htmlentities($file) . ". The error was: " . $err['message'];
2053
  }
2054
  $errors[] = $errMsg;
2055
  continue;
@@ -2059,7 +2065,7 @@ class wordfence {
2059
  flock($fh, LOCK_UN);
2060
  fclose($fh);
2061
  if($bytes < 1){
2062
- $errors[] = "We could not write to " . htmlentities($file) . ". ($bytes bytes written) You may not have permission to modify files on your WordPress server.";
2063
  continue;
2064
  }
2065
  $filesWorkedOn++;
@@ -2090,7 +2096,7 @@ class wordfence {
2090
  }
2091
  }
2092
  public static function ajax_deleteFile_callback(){
2093
- $issueID = $_POST['issueID'];
2094
  $wfIssues = new wfIssues();
2095
  $issue = $wfIssues->getIssueByID($issueID);
2096
  if(! $issue){
@@ -2114,11 +2120,11 @@ class wordfence {
2114
  );
2115
  } else {
2116
  $err = error_get_last();
2117
- return array('errorMsg' => "Could not delete file " . htmlentities($file) . ". The error was: " . htmlentities($err['message']));
2118
  }
2119
  }
2120
  public static function ajax_restoreFile_callback(){
2121
- $issueID = $_POST['issueID'];
2122
  $wfIssues = new wfIssues();
2123
  $issue = $wfIssues->getIssueByID($issueID);
2124
  if(! $issue){
@@ -2164,7 +2170,7 @@ class wordfence {
2164
  self::status(4, 'info', "Ajax request received to start scan.");
2165
  $err = wfScanEngine::startScan();
2166
  if($err){
2167
- return array('errorMsg' => htmlentities($err));
2168
  } else {
2169
  return array("ok" => 1);
2170
  }
@@ -2343,7 +2349,7 @@ EOL;
2343
  return ($a['ctime'] < $b['ctime']) ? -1 : 1;
2344
  }
2345
  public static function wfFunc_view(){
2346
- $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', $_GET['file']);
2347
  if(strpos($localFile, '..') !== false){
2348
  echo "Invalid file requested. (Relative paths not allowed)";
2349
  exit();
@@ -2386,7 +2392,7 @@ EOL;
2386
 
2387
  $result = self::getWPFileContent($_GET['file'], $_GET['cType'], $_GET['cName'], $_GET['cVersion']);
2388
  if( isset( $result['errorMsg'] ) && $result['errorMsg']){
2389
- echo htmlentities($result['errorMsg']);
2390
  exit(0);
2391
  } else if(! $result['fileContent']){
2392
  echo "We could not get the contents of the original file to do a comparison.";
@@ -2483,7 +2489,7 @@ EOL;
2483
  $activationError = substr($activationError, 0, 400) . '...[output truncated]';
2484
  }
2485
  if($activationError){
2486
- echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Wordfence generated an error on activation. The output we received during activation was:</strong> ' . htmlspecialchars($activationError) . '</p></div>';
2487
  }
2488
  delete_option('wf_plugin_act_error');
2489
  }
298
  $db->queryWriteIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
299
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileQueue");
300
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileChanges");
301
+ //Adding primary key to this table because some backup apps use primary key during backup.
302
+ $db->queryWriteIgnoreError("alter table wp_wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
303
 
304
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_options'");
305
  if($optScanEnabled != '0' && $optScanEnabled != '1'){
535
  }
536
 
537
  if(! is_array($returnArr)){
538
+ error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error.");
539
  $returnArr = array();
540
  }
541
  if(isset($returnArr['nonce'])){
617
  $user = get_user_by('email', $_POST['user_login']);
618
  if($user){
619
  if(wfConfig::get('alertOn_lostPasswdForm')){
620
+ wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: " . wp_kses($email, array()), $IP);
621
  }
622
  }
623
  if(wfConfig::get('loginSecurityEnabled')){
649
  public static function veryFirstAction(){
650
  $wfFunc = @$_GET['_wfsf'];
651
  if($wfFunc == 'unlockEmail'){
652
+ if(! wp_verify_nonce(@$_POST['nonce'], 'wf-form')){
653
+ die("Sorry but your browser sent an invalid security token when trying to use this form.");
654
+ }
655
  $numTries = get_transient('wordfenceUnlockTries');
656
  if($numTries > 10){
657
  echo "<html><body><h1>Please wait 3 minutes and try again</h1><p>You have used this form too much. Please wait 3 minutes and try again.</p></body></html>";
694
  ));
695
  wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
696
  }
697
+ echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . wp_kses($email, array()) . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>";
698
  exit();
699
  } else if($wfFunc == 'unlockAccess'){
700
  if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', get_transient('wfunlock_' . $_GET['key']))){
1014
  throw new Exception("We could not fetch a core WordPress file from the Wordfence API.");
1015
  }
1016
  } catch (Exception $e){
1017
+ return array('errorMsg' => wp_kses($e->getMessage(), array()));
1018
  }
1019
  }
1020
  public static function ajax_loadAvgSitePerf_callback(){
1029
  if(! wfConfig::get('isPaid')){
1030
  return array('errorMsg' => 'Cellphone Sign-in is only available to paid members. <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click here to upgrade now.</a>');
1031
  }
1032
+ $username = sanitize_text_field($_POST['username']);
1033
+ $phone = sanitize_text_field($_POST['phone']);
1034
  $user = get_user_by('login', $username);
1035
  if(! $user){
1036
  return array('errorMsg' => "The username you specified does not exist.");
1042
  try {
1043
  $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $phone));
1044
  } catch(Exception $e){
1045
+ return array('errorMsg' => "Could not contact Wordfence servers to generate a verification code: " . wp_kses($e->getMessage(), array()) );
1046
  }
1047
  if(isset($codeResult['ok']) && $codeResult['ok']){
1048
  $code = $codeResult['code'];
1049
  } else if(isset($codeResult['errorMsg']) && $codeResult['errorMsg']){
1050
+ return array('errorMsg' => wp_kses($codeResult['errorMsg'], array()));
1051
  } else {
1052
  return array('errorMsg' => "We could not generate a verification code.");
1053
  }
1060
  );
1061
  }
1062
  public static function ajax_twoFacActivate_callback(){
1063
+ $userID = sanitize_text_field($_POST['userID']);
1064
+ $code = sanitize_text_field($_POST['code']);
1065
  $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
1066
  if(! is_array($twoFactorUsers)){
1067
  $twoFactorUsers = array();
1076
  $user = $twoFactorUsers[$i];
1077
  break;
1078
  } else {
1079
+ return array('errorMsg' => "That is not the correct code. Please look for an SMS containing an activation code on the phone with number: " . wp_kses($twoFactorUsers[$i][1], array()) );
1080
  }
1081
  }
1082
  }
1286
  if($r['type'] == 'error'){
1287
  $content .= "\n";
1288
  }
1289
+ $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
1290
  }
1291
  $content .= "\n\n";
1292
 
1317
  throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
1318
  }
1319
  } catch(Exception $e){
1320
+ return array('errorMsg' => "Could not fetch free API key from Wordfence: " . wp_kses($e->getMessage(), array()));
1321
  }
1322
  return array('ok' => 1);
1323
  }
1627
  }
1628
  }
1629
  if(sizeof($badEmails) > 0){
1630
+ return array('errorMsg' => "The following emails are invalid: " . wp_kses(implode(', ', $badEmails), array()) );
1631
  }
1632
  $opts['alertEmails'] = implode(',', $emails);
1633
  } else {
1648
  }
1649
  }
1650
  if(sizeof($badWhiteIPs) > 0){
1651
+ return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . wp_kses(implode(', ', $badWhiteIPs), array()) );
1652
  }
1653
  $opts['whitelisted'] = implode(',', $whiteIPs);
1654
  } else {
1685
  }
1686
 
1687
  if(sizeof($invalidUsers) > 0){
1688
+ return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . wp_kses(implode(', ', $invalidUsers), array()) );
1689
  }
1690
  if(sizeof($validUsers) > 0){
1691
  $opts['liveTraf_ignoreUsers'] = implode(',', $validUsers);
1705
  }
1706
  }
1707
  if(sizeof($invalidIPs) > 0){
1708
+ return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . wp_kses(implode(', ', $invalidIPs), array()) );
1709
  }
1710
  if(sizeof($validIPs) > 0){
1711
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
1762
  throw new Exception("We could not understand the Wordfence server's response because it did not contain an 'ok' and 'apiKey' element.");
1763
  }
1764
  } catch(Exception $e){
1765
+ return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . wp_kses($e->getMessage(), array()) );
1766
  }
1767
  } else if($opts['apiKey'] != wfConfig::get('apiKey')){
1768
  $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
1779
  throw new Exception("We could not understand the Wordfence API server reply when updating your API key.");
1780
  }
1781
  } catch (Exception $e){
1782
+ return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . wp_kses($e->getMessage(), array()) );
1783
  }
1784
  } else {
1785
  $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
1858
  }
1859
  $clientIP = wfUtils::inet_aton(wfUtils::getIP());
1860
  if($ip1 <= $clientIP && $ip2 >= $clientIP){
1861
+ return array('err' => 1, 'errorMsg' => "You are trying to block yourself. Your IP address is " . wp_kses(wfUtils::getIP(), array()) . " which falls into the range " . wp_kses($ipRange, array()) . ". This blocking action has been cancelled so that you don't block yourself from your website.");
1862
  }
1863
  $ipRange = $ip1 . '-' . $ip2;
1864
  }
1886
  return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
1887
  }
1888
  if(self::getLog()->isWhitelisted($IP)){
1889
+ return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
1890
  }
1891
  if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
1892
  if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){
1961
  $issues = new wfIssues();
1962
  $jsonData = array(
1963
  'serverTime' => $serverTime,
1964
+ 'msg' => wp_kses_data( (string) $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"))
1965
  );
1966
  $events = array();
1967
  $alsoGet = $_POST['alsoGet'];
2006
  return array('ok' => 1, 'email' => $email);
2007
  }
2008
  public static function ajax_bulkOperation_callback(){
2009
+ $op = sanitize_text_field($_POST['op']);
2010
  if($op == 'del' || $op == 'repair'){
2011
  $ids = $_POST['ids'];
2012
  $filesWorkedOn = 0;
2013
  $errors = array();
2014
  $issues = new wfIssues();
2015
  foreach($ids as $id){
2016
+ $id = intval($id); //Make sure input is a number.
2017
  $issue = $issues->getIssueByID($id);
2018
  if(! $issue){
2019
  $errors[] = "Could not delete one of the files because we could not find the issue. Perhaps it's been resolved?";
2023
  $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
2024
  $localFile = realpath($localFile);
2025
  if(strpos($localFile, ABSPATH) !== 0){
2026
+ $errors[] = "An invalid file was requested: " . wp_kses($file, arra());
2027
  continue;
2028
  }
2029
  if($op == 'del'){
2032
  $filesWorkedOn++;
2033
  } else {
2034
  $err = error_get_last();
2035
+ $errors[] = "Could not delete file " . wp_kses($file, array()) . ". Error was: " . wp_kses($err['message'], array());
2036
  }
2037
  } else if($op == 'repair'){
2038
  $dat = $issue['data'];
2041
  $errors[] = $result['cerrorMsg'];
2042
  continue;
2043
  } else if(! $result['fileContent']){
2044
+ $errors[] = "We could not get the original file of " . wp_kses($file, array()) . " to do a repair.";
2045
  continue;
2046
  }
2047
 
2048
  if(preg_match('/\.\./', $file)){
2049
+ $errors[] = "An invalid file " . wp_kses($file, array()) . " was specified for repair.";
2050
  continue;
2051
  }
2052
  $fh = fopen($localFile, 'w');
2053
  if(! $fh){
2054
  $err = error_get_last();
2055
  if(preg_match('/Permission denied/i', $err['message'])){
2056
+ $errMsg = "You don't have permission to repair " . wp_kses($file, array()) . ". You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
2057
  } else {
2058
+ $errMsg = "We could not write to " . wp_kses($file, array()) . ". The error was: " . $err['message'];
2059
  }
2060
  $errors[] = $errMsg;
2061
  continue;
2065
  flock($fh, LOCK_UN);
2066
  fclose($fh);
2067
  if($bytes < 1){
2068
+ $errors[] = "We could not write to " . wp_kses($file, array()) . ". ($bytes bytes written) You may not have permission to modify files on your WordPress server.";
2069
  continue;
2070
  }
2071
  $filesWorkedOn++;
2096
  }
2097
  }
2098
  public static function ajax_deleteFile_callback(){
2099
+ $issueID = intval($_POST['issueID']);
2100
  $wfIssues = new wfIssues();
2101
  $issue = $wfIssues->getIssueByID($issueID);
2102
  if(! $issue){
2120
  );
2121
  } else {
2122
  $err = error_get_last();
2123
+ return array('errorMsg' => "Could not delete file " . wp_kses($file, array()) . ". The error was: " . wp_kses($err['message'], array()));
2124
  }
2125
  }
2126
  public static function ajax_restoreFile_callback(){
2127
+ $issueID = intval($_POST['issueID']);
2128
  $wfIssues = new wfIssues();
2129
  $issue = $wfIssues->getIssueByID($issueID);
2130
  if(! $issue){
2170
  self::status(4, 'info', "Ajax request received to start scan.");
2171
  $err = wfScanEngine::startScan();
2172
  if($err){
2173
+ return array('errorMsg' => wp_kses($err, array()));
2174
  } else {
2175
  return array("ok" => 1);
2176
  }
2349
  return ($a['ctime'] < $b['ctime']) ? -1 : 1;
2350
  }
2351
  public static function wfFunc_view(){
2352
+ $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', sanitize_text_field($_GET['file']));
2353
  if(strpos($localFile, '..') !== false){
2354
  echo "Invalid file requested. (Relative paths not allowed)";
2355
  exit();
2392
 
2393
  $result = self::getWPFileContent($_GET['file'], $_GET['cType'], $_GET['cName'], $_GET['cVersion']);
2394
  if( isset( $result['errorMsg'] ) && $result['errorMsg']){
2395
+ echo wp_kses($result['errorMsg'], array());
2396
  exit(0);
2397
  } else if(! $result['fileContent']){
2398
  echo "We could not get the contents of the original file to do a comparison.";
2489
  $activationError = substr($activationError, 0, 400) . '...[output truncated]';
2490
  }
2491
  if($activationError){
2492
+ echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Wordfence generated an error on activation. The output we received during activation was:</strong> ' . wp_kses($activationError, array()) . '</p></div>';
2493
  }
2494
  delete_option('wf_plugin_act_error');
2495
  }
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching
4
  Requires at least: 3.3.1
5
  Tested up to: 4.0
6
- Stable tag: 5.2.4
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
@@ -163,6 +163,23 @@ cause a security hole on your site.
163
 
164
  == Changelog ==
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  = 5.2.4 =
167
  * Security release. Upgrade immediately.
168
  * This release fixes an XSS vunlerability on Wordfence "view all traffic from IP" page.
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching
4
  Requires at least: 3.3.1
5
  Tested up to: 4.0
6
+ Stable tag: 5.2.5
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
163
 
164
  == Changelog ==
165
 
166
+ = 5.2.5 =
167
+ * Security release. Update immediately. Thanks to Julio Potier.
168
+ * Code hardening including improved sanitization and an additional nonce for unlock email form. Special thanks to Ryan Satterfield for the hard work.
169
+ * Stability of auto-update improved for LiteSpeed customers. We auto-detect if you don't have E=noabort:1 in your .htaccess and give you instructions.
170
+ * Auto-update also disabled now for LiteSpeed customers who don't have E=noabort:1 and you will get an email alert with an explanation.
171
+ * Fixed a bug that may cause you to have advanced blocking patterns disabled with falcon engine enabled that should not be disabled.
172
+ * Removed a benign warning in wfCache.php.
173
+ * Added clarity to the banned URL option on the options page. All URL's must be relative.
174
+ * Added a primary key to the wp_wfStatus table which is required for certain incremental backup plugins and utilities.
175
+ * Fixed advanced country blocking which was not correctly displaying advanced options.
176
+ * Migrated to using wp_kses() for sanitization.
177
+ * Prevent IP spoofing in default Wordfence IP configuration.
178
+ * Change explanations of how Wordfence gets IP's to make it clear which to use to prevent spoofing.
179
+ * Make it clear that the option to have IP's immediately blocked when they access a URL requires relative URL's starting with a forward slash.
180
+ * Whitelist Sucuri's scanning IP addresses which were getting blocked because they triggered Wordfence blocking during a scan.
181
+ * Improved Wordfence's code that acquires the visitor IP to block certain spoofing attacks, be more platform agnostic and deal with visits from private IP's more elegantly.
182
+
183
  = 5.2.4 =
184
  * Security release. Upgrade immediately.
185
  * This release fixes an XSS vunlerability on Wordfence "view all traffic from IP" page.
wordfence.php CHANGED
@@ -4,13 +4,13 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
- Version: 5.2.4
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
- define('WORDFENCE_VERSION', '5.2.4');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 5.2.5
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
+ define('WORDFENCE_VERSION', '5.2.5');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }