Wordfence Security – Firewall & Malware Scan - Version 6.1.16

Version Description

  • Improvement: Now performing malware scanning on all uploaded files in real-time.
  • Improvement: Added Web Application Firewall activity to Wordfence summary email.
  • Fix: Now using 503 response code in the page displayed when an IP is locked out.
  • Fix: wflogs directory is now correctly removed on uninstall.
  • Fix: Fixed recently introduced bug which caused the Whitelisted 404 URLs feature to no longer work.
  • Fix: Added try/catch to uncaught exception thrown when pinging the API key.
  • Improvement: Improved performance of the Live Traffic page in Firefox.
  • Improvement: Updated GeoIP database.
Download this release

Release Info

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

Code changes from version 6.1.15 to 6.1.16

js/admin.js CHANGED
@@ -2463,6 +2463,16 @@
2463
  var self = this;
2464
  jQuery('.wfTimeAgo-timestamp').each(function(idx, elem) {
2465
  var el = jQuery(elem);
 
 
 
 
 
 
 
 
 
 
2466
  var timestamp = el.data('wfctime');
2467
  if (!timestamp) {
2468
  timestamp = el.attr('data-timestamp');
2463
  var self = this;
2464
  jQuery('.wfTimeAgo-timestamp').each(function(idx, elem) {
2465
  var el = jQuery(elem);
2466
+ var testEl = el;
2467
+ if (typeof jQuery === "function" && testEl instanceof jQuery) {
2468
+ testEl = testEl[0];
2469
+ }
2470
+
2471
+ var rect = testEl.getBoundingClientRect();
2472
+ if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth))) {
2473
+ return;
2474
+ }
2475
+
2476
  var timestamp = el.data('wfctime');
2477
  if (!timestamp) {
2478
  timestamp = el.attr('data-timestamp');
lib/GeoIP.dat CHANGED
Binary file
lib/GeoIPv6.dat CHANGED
Binary file
lib/menu_options.php CHANGED
@@ -299,6 +299,11 @@ $w = new wfConfig();
299
  <td><input type="checkbox" id="alertOn_nonAdminLogin" class="wfConfigElem"
300
  name="alertOn_nonAdminLogin" value="1" <?php $w->cb( 'alertOn_nonAdminLogin' ); ?>/></td>
301
  </tr>
 
 
 
 
 
302
  <tr>
303
  <th>Maximum email alerts to send per hour</th>
304
  <td>&nbsp;<input type="text" id="alert_maxHourly" name="alert_maxHourly"
@@ -324,8 +329,8 @@ $w = new wfConfig();
324
  <th>Email summary frequency:</th>
325
  <td>
326
  <select id="email_summary_interval" class="wfConfigElem" name="email_summary_interval">
 
327
  <option value="weekly"<?php $w->sel( 'email_summary_interval', 'weekly' ); ?>>Once a week</option>
328
- <option value="biweekly"<?php $w->sel( 'email_summary_interval', 'biweekly' ); ?>>Once every 2 weeks</option>
329
  <option value="monthly"<?php $w->sel( 'email_summary_interval', 'monthly' ); ?>>Once a month</option>
330
  </select>
331
  </td>
@@ -824,7 +829,7 @@ $w = new wfConfig();
824
  </td>
825
  </tr>
826
  <tr>
827
- <th>Immediately block the IP of users who try to sign in as these usernames<a
828
  href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_the_IP_of_users_who_try_to_sign_in_as_these_usernames"
829
  target="_blank" class="wfhelp"></a></th>
830
  <td>
@@ -844,7 +849,7 @@ $w = new wfConfig();
844
  </tr>
845
 
846
  <tr>
847
- <th>Whitelisted IP addresses that bypass all rules:<a
848
  href="http://docs.wordfence.com/en/Wordfence_options#Whitelisted_IP_addresses_that_bypass_all_rules"
849
  target="_blank" class="wfhelp"></a></th>
850
  <td><textarea name="whitelisted" id="whitelisted" cols="40" rows="4"><?php echo esc_html(preg_replace('/,/', "\n", $w->get('whitelisted'))); ?></textarea></td>
@@ -857,7 +862,7 @@ $w = new wfConfig();
857
  </tr>
858
 
859
  <tr>
860
- <th>Immediately block IPs that access these URLs:<a
861
  href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_IP.27s_that_access_these_URLs"
862
  target="_blank" class="wfhelp"></a></th>
863
  <td><textarea type="text" name="bannedURLs" id="bannedURLs" cols="40" rows="4"><?php echo esc_html(preg_replace('/,/', "\n", $w->get('bannedURLs'))); ?></textarea></td>
@@ -879,6 +884,22 @@ $w = new wfConfig();
879
  the throttling rules used to limit crawlers.
880
  <br/><br/></th>
881
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
 
883
  <tr>
884
  <th>Hide WordPress version<a
299
  <td><input type="checkbox" id="alertOn_nonAdminLogin" class="wfConfigElem"
300
  name="alertOn_nonAdminLogin" value="1" <?php $w->cb( 'alertOn_nonAdminLogin' ); ?>/></td>
301
  </tr>
302
+ <tr>
303
+ <th>Alert me when there's a large increase in attacks detected on my site</th>
304
+ <td><input type="checkbox" id="wafAlertOnAttacks" class="wfConfigElem"
305
+ name="wafAlertOnAttacks" value="1" <?php $w->cb( 'wafAlertOnAttacks' ); ?>/></td>
306
+ </tr>
307
  <tr>
308
  <th>Maximum email alerts to send per hour</th>
309
  <td>&nbsp;<input type="text" id="alert_maxHourly" name="alert_maxHourly"
329
  <th>Email summary frequency:</th>
330
  <td>
331
  <select id="email_summary_interval" class="wfConfigElem" name="email_summary_interval">
332
+ <option value="daily"<?php $w->sel( 'email_summary_interval', 'daily' ); ?>>Once a day</option>
333
  <option value="weekly"<?php $w->sel( 'email_summary_interval', 'weekly' ); ?>>Once a week</option>
 
334
  <option value="monthly"<?php $w->sel( 'email_summary_interval', 'monthly' ); ?>>Once a month</option>
335
  </select>
336
  </td>
829
  </td>
830
  </tr>
831
  <tr>
832
+ <th style="vertical-align: top;">Immediately block the IP of users who try to sign in as these usernames<a
833
  href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_the_IP_of_users_who_try_to_sign_in_as_these_usernames"
834
  target="_blank" class="wfhelp"></a></th>
835
  <td>
849
  </tr>
850
 
851
  <tr>
852
+ <th style="vertical-align: top;">Whitelisted IP addresses that bypass all rules:<a
853
  href="http://docs.wordfence.com/en/Wordfence_options#Whitelisted_IP_addresses_that_bypass_all_rules"
854
  target="_blank" class="wfhelp"></a></th>
855
  <td><textarea name="whitelisted" id="whitelisted" cols="40" rows="4"><?php echo esc_html(preg_replace('/,/', "\n", $w->get('whitelisted'))); ?></textarea></td>
862
  </tr>
863
 
864
  <tr>
865
+ <th style="vertical-align: top;">Immediately block IPs that access these URLs:<a
866
  href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_IP.27s_that_access_these_URLs"
867
  target="_blank" class="wfhelp"></a></th>
868
  <td><textarea type="text" name="bannedURLs" id="bannedURLs" cols="40" rows="4"><?php echo esc_html(preg_replace('/,/', "\n", $w->get('bannedURLs'))); ?></textarea></td>
884
  the throttling rules used to limit crawlers.
885
  <br/><br/></th>
886
  </tr>
887
+
888
+ <tr>
889
+ <th style="vertical-align: top;">Whitelisted IP addresses for Wordfence Web Application Firewall alerting:</th>
890
+ <td><textarea name="wafAlertWhitelist" id="wafAlertWhitelist" cols="40" rows="4"><?php echo esc_html(preg_replace('/,/', "\n", $w->get('wafAlertWhitelist'))); ?></textarea></td>
891
+ </tr>
892
+ <tr>
893
+ <th colspan="2" style="color: #999;">Whitelisted IPs must be separated by commas or placed on separate lines. These addresses will be ignored from any alerts about increased attacks and can be used to ignore things like standalone website security scanners.<br/><br/></th>
894
+ </tr>
895
+ <tr class="hidden">
896
+ <th style="vertical-align: top;">Minimum number of blocked attacks before sending an alert</th>
897
+ <td><input type="text" name="wafAlertThreshold" id=""wafAlertThreshold" value="<?php $w->f( 'wafAlertThreshold' ); ?>"></td>
898
+ </tr>
899
+ <tr class="hidden">
900
+ <th style="vertical-align: top;">Number of seconds to count the attacks over</th>
901
+ <td><input type="text" name="wafAlertInterval" id=""wafAlertInterval" value="<?php $w->f( 'wafAlertInterval' ); ?>"></td>
902
+ </tr>
903
 
904
  <tr>
905
  <th>Hide WordPress version<a
lib/wfActivityReport.php CHANGED
@@ -96,18 +96,18 @@ class wfActivityReport {
96
  $end_time = 0;
97
 
98
  switch ($interval) {
 
 
 
 
 
 
99
  // Send a report 4pm every Monday
100
  case 'weekly':
101
  $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
102
  $end_time = $start_time + (86400 * 7);
103
  break;
104
 
105
- // Send a report 4pm every other Monday
106
- case 'biweekly':
107
- $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
108
- $end_time = $start_time + (86400 * 14);
109
- break;
110
-
111
  // Send a report at 4pm the first of every month
112
  case 'monthly':
113
  $start_time = gmmktime(16, 0, 0, $month, 1, $year) + (-$offset * 60 * 60);
@@ -139,8 +139,8 @@ class wfActivityReport {
139
  // weekly
140
  $from = $time - (86400 * 7);
141
  switch ($interval) {
142
- case 'biweekly':
143
- $from = $time - (86400 * 14);
144
  break;
145
 
146
  // Send a report at 4pm the first of every month
@@ -157,13 +157,17 @@ class wfActivityReport {
157
  */
158
  public function getFullReport() {
159
  $start_time = microtime(true);
 
 
160
  return array(
161
- 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
162
- 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
163
- 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
164
- 'recently_modified_files' => $this->getRecentFilesModified($this->limit),
165
- 'updates_needed' => $this->getUpdatesNeeded(),
166
- 'microseconds' => microtime(true) - $start_time,
 
 
167
  );
168
  }
169
 
@@ -243,8 +247,8 @@ SQL
243
  public function getTopFailedLogins($limit = 10) {
244
  $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day))';
245
  switch (wfConfig::get('email_summary_interval', 'weekly')) {
246
- case 'biweekly':
247
- $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 14 day))';
248
  break;
249
  case 'monthly':
250
  $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month))';
@@ -316,6 +320,19 @@ SQL
316
  }
317
  return false;
318
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
  /**
321
  * Returns list of files modified within given timeframe.
@@ -342,8 +359,8 @@ SQL
342
  // default to weekly
343
  $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
344
  switch (wfConfig::get('email_summary_interval', 'weekly')) {
345
- case 'biweekly':
346
- $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 14 day)) / 86400)';
347
  break;
348
  case 'monthly':
349
  $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
@@ -466,6 +483,77 @@ SQL
466
  }
467
  }
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
 
470
  class wfRecentlyModifiedFiles extends wfDirectoryIterator {
471
 
@@ -581,4 +669,17 @@ class wfActivityReportView extends wfView {
581
  }
582
  return date_i18n('F j, Y g:ia', $unix_time);
583
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  }
96
  $end_time = 0;
97
 
98
  switch ($interval) {
99
+ // Send a report 4pm every day
100
+ case 'daily':
101
+ $start_time = gmmktime(16, 0, 0, $month, $day_of_month, $year) + (-$offset * 60 * 60);
102
+ $end_time = $start_time + 86400;
103
+ break;
104
+
105
  // Send a report 4pm every Monday
106
  case 'weekly':
107
  $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
108
  $end_time = $start_time + (86400 * 7);
109
  break;
110
 
 
 
 
 
 
 
111
  // Send a report at 4pm the first of every month
112
  case 'monthly':
113
  $start_time = gmmktime(16, 0, 0, $month, 1, $year) + (-$offset * 60 * 60);
139
  // weekly
140
  $from = $time - (86400 * 7);
141
  switch ($interval) {
142
+ case 'daily':
143
+ $from = $time - 86400;
144
  break;
145
 
146
  // Send a report at 4pm the first of every month
157
  */
158
  public function getFullReport() {
159
  $start_time = microtime(true);
160
+ $remainder = 0;
161
+ $recent_firewall_activity = $this->getRecentFirewallActivity($this->limit, $remainder);
162
  return array(
163
+ 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
164
+ 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
165
+ 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
166
+ 'recent_firewall_activity' => $recent_firewall_activity,
167
+ 'omitted_firewall_activity'=> $remainder,
168
+ 'recently_modified_files' => $this->getRecentFilesModified($this->limit),
169
+ 'updates_needed' => $this->getUpdatesNeeded(),
170
+ 'microseconds' => microtime(true) - $start_time,
171
  );
172
  }
173
 
247
  public function getTopFailedLogins($limit = 10) {
248
  $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day))';
249
  switch (wfConfig::get('email_summary_interval', 'weekly')) {
250
+ case 'daily':
251
+ $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day))';
252
  break;
253
  case 'monthly':
254
  $interval = 'UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month))';
320
  }
321
  return false;
322
  }
323
+
324
+ /**
325
+ * Returns list of firewall activity up to $limit number of entries.
326
+ *
327
+ * @param int $limit Max events to return in results
328
+ * @return array
329
+ */
330
+ public function getRecentFirewallActivity($limit = 300, &$remainder) {
331
+ $dateRange = wfActivityReport::getReportDateRange();
332
+ $recent_firewall_activity = new wfRecentFirewallActivity(null, max(604800, $dateRange[1] - $dateRange[0]));
333
+ $recent_firewall_activity->run();
334
+ return $recent_firewall_activity->mostRecentActivity($limit, $remainder);
335
+ }
336
 
337
  /**
338
  * Returns list of files modified within given timeframe.
359
  // default to weekly
360
  $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
361
  switch (wfConfig::get('email_summary_interval', 'weekly')) {
362
+ case 'daily':
363
+ $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 day)) / 86400)';
364
  break;
365
  case 'monthly':
366
  $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
483
  }
484
  }
485
 
486
+ class wfRecentFirewallActivity {
487
+ private $activity = array();
488
+
489
+ private $max_fetch = 2000;
490
+ private $time_range = 604800;
491
+
492
+ public function __construct($max_fetch = null, $time_range = null) {
493
+ if ($max_fetch !== null) {
494
+ $this->max_fetch = $max_fetch;
495
+ }
496
+
497
+ if ($time_range !== null) {
498
+ $this->time_range = $time_range;
499
+ }
500
+ }
501
+
502
+ public function run() {
503
+ global $wpdb;
504
+
505
+ $results = $wpdb->get_results($wpdb->prepare(<<<SQL
506
+ SELECT attackLogTime, IP, URL, UA, actionDescription, actionData
507
+ FROM {$wpdb->prefix}wfHits
508
+ WHERE action = 'blocked:waf' AND attackLogTime > (UNIX_TIMESTAMP() - %d)
509
+ ORDER BY attackLogTime DESC
510
+ LIMIT %d
511
+ SQL
512
+ , $this->time_range, $this->max_fetch));
513
+ if ($results) {
514
+ foreach ($results as &$row) {
515
+ $row->longDescription = "Blocked for " . $row->actionDescription;
516
+
517
+ $actionData = json_decode($row->actionData, true);
518
+ if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
519
+ continue;
520
+ }
521
+
522
+ $paramKey = base64_decode($actionData['paramKey']);
523
+ $paramValue = base64_decode($actionData['paramValue']);
524
+ if (strlen($paramValue) > 100) {
525
+ $paramValue = substr($paramValue, 0, 100) . chr(2026);
526
+ }
527
+
528
+ if (preg_match('/([a-z0-9_]+\.[a-z0-9_]+)(?:\[(.+?)\](.*))?/i', $paramKey, $matches)) {
529
+ switch ($matches[1]) {
530
+ case 'request.queryString':
531
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in query string: ' . $matches[2] . '=' . $paramValue;
532
+ break;
533
+ case 'request.body':
534
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in POST body: ' . $matches[2] . '=' . $paramValue;
535
+ break;
536
+ case 'request.cookie':
537
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in cookie: ' . $matches[2] . '=' . $paramValue;
538
+ break;
539
+ case 'request.fileNames':
540
+ $row->longDescription = "Blocked for a " . $row->actionDescription . ' in file: ' . $matches[2] . '=' . $paramValue;
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ }
546
+
547
+ $this->activity = $results;
548
+ }
549
+
550
+ public function mostRecentActivity($limit, &$remainder = null) {
551
+ if ($remainder !== null) {
552
+ $remainder = count($this->activity) - $limit;
553
+ }
554
+ return array_slice($this->activity, 0, $limit);
555
+ }
556
+ }
557
 
558
  class wfRecentlyModifiedFiles extends wfDirectoryIterator {
559
 
669
  }
670
  return date_i18n('F j, Y g:ia', $unix_time);
671
  }
672
+
673
+ public function attackTime($unix_time = null) {
674
+ if ($unix_time === null) {
675
+ $unix_time = time();
676
+ }
677
+ return date_i18n('F j, Y', $unix_time) . "<br>" . date_i18n('g:ia', $unix_time);
678
+ }
679
+
680
+ public function displayIP($binaryIP) {
681
+ $readableIP = wfUtils::inet_ntop($binaryIP);
682
+ $country = wfUtils::countryCode2Name(wfUtils::IP2Country($readableIP));
683
+ return "{$readableIP} (" . ($country ? $country : 'Unknown') . ")";
684
+ }
685
  }
lib/wfConfig.php CHANGED
@@ -81,6 +81,7 @@ class wfConfig {
81
  'ssl_verify' => array('value' => true, 'autoload' => self::AUTOLOAD),
82
  'ajaxWatcherDisabled_front' => array('value' => false, 'autoload' => self::AUTOLOAD),
83
  'ajaxWatcherDisabled_admin' => array('value' => false, 'autoload' => self::AUTOLOAD),
 
84
  ),
85
  "otherParams" => array(
86
  "scan_include_extra" => "",
@@ -106,9 +107,12 @@ class wfConfig {
106
  'maxScanHits' => "DISABLED",
107
  'maxScanHits_action' => "throttle",
108
  'blockedTime' => "300",
109
- 'email_summary_interval' => 'biweekly',
110
  'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
111
- 'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png",
 
 
 
112
  )
113
  );
114
  public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs');
@@ -264,7 +268,7 @@ class wfConfig {
264
  return;
265
  }
266
 
267
- if (($key == 'apiKey' || $key == 'isPaid') && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) {
268
  try {
269
  wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val);
270
  } catch (wfWAFStorageFileException $e) {
81
  'ssl_verify' => array('value' => true, 'autoload' => self::AUTOLOAD),
82
  'ajaxWatcherDisabled_front' => array('value' => false, 'autoload' => self::AUTOLOAD),
83
  'ajaxWatcherDisabled_admin' => array('value' => false, 'autoload' => self::AUTOLOAD),
84
+ 'wafAlertOnAttacks' => array('value' => true, 'autoload' => self::AUTOLOAD),
85
  ),
86
  "otherParams" => array(
87
  "scan_include_extra" => "",
107
  'maxScanHits' => "DISABLED",
108
  'maxScanHits_action' => "throttle",
109
  'blockedTime' => "300",
110
+ 'email_summary_interval' => 'weekly',
111
  'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
112
+ 'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png\n/browserconfig.xml",
113
+ 'wafAlertWhitelist' => '',
114
+ 'wafAlertInterval' => 600,
115
+ 'wafAlertThreshold' => 100,
116
  )
117
  );
118
  public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs');
268
  return;
269
  }
270
 
271
+ if (($key == 'apiKey' || $key == 'isPaid' || $key == 'other_WFNet') && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) {
272
  try {
273
  wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val);
274
  } catch (wfWAFStorageFileException $e) {
lib/wfLockedOut.php CHANGED
@@ -1,4 +1,9 @@
1
- <?php wfUtils::doNotCache(); ?><!DOCTYPE html>
 
 
 
 
 
2
  <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
3
  <head>
4
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
1
+ <?php
2
+ wfUtils::doNotCache();
3
+ header('HTTP/1.1 503 Service Temporarily Unavailable');
4
+ header('Status: 503 Service Temporarily Unavailable');
5
+ ?>
6
+ <!DOCTYPE html>
7
  <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
8
  <head>
9
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
lib/wfLog.php CHANGED
@@ -203,7 +203,7 @@ class wfLog {
203
  if ($type == '404') {
204
  $allowed404s = wfConfig::get('allowed404s');
205
  if (is_string($allowed404s)) {
206
- $allowed404s = array_filter(explode("\n", $allowed404s));
207
  $allowed404sPattern = '';
208
  foreach ($allowed404s as $allowed404) {
209
  $allowed404sPattern .= preg_replace('/\\\\\*/', '.*?', preg_quote($allowed404, '/')) . '|';
203
  if ($type == '404') {
204
  $allowed404s = wfConfig::get('allowed404s');
205
  if (is_string($allowed404s)) {
206
+ $allowed404s = array_filter(preg_split("/[\r\n]+/", $allowed404s));
207
  $allowed404sPattern = '';
208
  foreach ($allowed404s as $allowed404) {
209
  $allowed404sPattern .= preg_replace('/\\\\\*/', '.*?', preg_quote($allowed404, '/')) . '|';
lib/wfScanEngine.php CHANGED
@@ -158,7 +158,9 @@ class wfScanEngine {
158
  $this->i->emailNewIssues();
159
  }
160
  public function submitMetrics() {
161
- $this->api->call('record_scan_metrics', array(), array('metrics' => $this->metrics));
 
 
162
  }
163
  private function doScan(){
164
  while(sizeof($this->jobList) > 0){
158
  $this->i->emailNewIssues();
159
  }
160
  public function submitMetrics() {
161
+ if (wfConfig::get('other_WFNet', true)) {
162
+ $this->api->call('record_scan_metrics', array(), array('metrics' => $this->metrics));
163
+ }
164
  }
165
  private function doScan(){
166
  while(sizeof($this->jobList) > 0){
lib/wfUpdateCheck.php CHANGED
@@ -92,12 +92,17 @@ class wfUpdateCheck {
92
 
93
  //Check the vulnerability database
94
  if (isset($valsArray['slug']) && isset($valsArray['new_version'])) {
95
- $result = $this->api->call('plugin_vulnerability_check', array(), array(
96
- 'slug' => $valsArray['slug'],
97
- 'fromVersion' => $data['Version'],
98
- 'toVersion' => $valsArray['new_version'],
99
- ));
100
- $data['vulnerabilityPatched'] = isset($result['vulnerable']) && $result['vulnerable'];
 
 
 
 
 
101
  }
102
  else {
103
  $data['vulnerabilityPatched'] = false;
92
 
93
  //Check the vulnerability database
94
  if (isset($valsArray['slug']) && isset($valsArray['new_version'])) {
95
+ try {
96
+ $result = $this->api->call('plugin_vulnerability_check', array(), array(
97
+ 'slug' => $valsArray['slug'],
98
+ 'fromVersion' => $data['Version'],
99
+ 'toVersion' => $valsArray['new_version'],
100
+ ));
101
+ $data['vulnerabilityPatched'] = isset($result['vulnerable']) && $result['vulnerable'];
102
+ }
103
+ catch(Exception $e){
104
+ $data['vulnerabilityPatched'] = false;
105
+ }
106
  }
107
  else {
108
  $data['vulnerabilityPatched'] = false;
lib/wfUtils.php CHANGED
@@ -38,6 +38,26 @@ class wfUtils {
38
  }
39
  }
40
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  public static function pluralize($m1, $t1, $m2 = false, $t2 = false) {
42
  if($m1 != 1) {
43
  $t1 = $t1 . 's';
38
  }
39
  }
40
  }
41
+ public static function makeDuration($secs) {
42
+ $months = floor($secs / (86400 * 30));
43
+ $days = floor($secs / 86400);
44
+ $hours = floor($secs / 3600);
45
+ $minutes = floor($secs / 60);
46
+ if($months) {
47
+ $days -= $months * 30;
48
+ return self::pluralize($months, 'month', $days, 'day');
49
+ } else if($days) {
50
+ $hours -= $days * 24;
51
+ return self::pluralize($days, 'day', $hours, 'hour');
52
+ } else if($hours) {
53
+ $minutes -= $hours * 60;
54
+ return self::pluralize($hours, 'hour', $minutes, 'minute');
55
+ } else if($minutes) {
56
+ return self::pluralize($minutes, 'minute');
57
+ } else {
58
+ return self::pluralize($secs, 'second');
59
+ }
60
+ }
61
  public static function pluralize($m1, $t1, $m2 = false, $t2 = false) {
62
  if($m1 != 1) {
63
  $t1 = $t1 . 's';
lib/wordfenceClass.php CHANGED
@@ -170,45 +170,50 @@ class wordfence {
170
  }
171
  public static function dailyCron(){
172
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
173
- $keyData = $api->call('ping_api_key');
174
- if(isset($keyData['_isPaidKey']) && $keyData['_isPaidKey']){
175
- $keyExpDays = $keyData['_keyExpDays'];
176
- $keyIsExpired = $keyData['_expired'];
177
- if (!empty($keyData['_autoRenew'])) {
178
- if ($keyExpDays > 12) {
179
- wfConfig::set('keyAutoRenew10Sent', '');
180
- } else if ($keyExpDays <= 12 && $keyExpDays > 0 && !wfConfig::get('keyAutoRenew10Sent')) {
181
- wfConfig::set('keyAutoRenew10Sent', 1);
182
- $email = "Your Premium Wordfence API Key is set to auto-renew in 10 days.";
183
- self::alert($email, "$email To update your API key settings please visit http://www.wordfence.com/zz9/dashboard", false);
184
- }
185
- } else {
186
- if($keyExpDays > 15){
187
- wfConfig::set('keyExp15Sent', '');
188
- wfConfig::set('keyExp7Sent', '');
189
- wfConfig::set('keyExp2Sent', '');
190
- wfConfig::set('keyExp1Sent', '');
191
- wfConfig::set('keyExpFinalSent', '');
192
- } else if($keyExpDays <= 15 && $keyExpDays > 0){
193
- if($keyExpDays <= 15 && $keyExpDays >= 11 && (! wfConfig::get('keyExp15Sent'))){
194
- wfConfig::set('keyExp15Sent', 1);
195
- self::keyAlert("Your Premium Wordfence API Key expires in less than 2 weeks.");
196
- } else if($keyExpDays <= 7 && $keyExpDays >= 4 && (! wfConfig::get('keyExp7Sent'))){
197
- wfConfig::set('keyExp7Sent', 1);
198
- self::keyAlert("Your Premium Wordfence API Key expires in less than a week.");
199
- } else if($keyExpDays == 2 && (! wfConfig::get('keyExp2Sent'))){
200
- wfConfig::set('keyExp2Sent', 1);
201
- self::keyAlert("Your Premium Wordfence API Key expires in 2 days.");
202
- } else if($keyExpDays == 1 && (! wfConfig::get('keyExp1Sent'))){
203
- wfConfig::set('keyExp1Sent', 1);
204
- self::keyAlert("Your Premium Wordfence API Key expires in 1 day.");
 
 
 
 
 
205
  }
206
- } else if($keyIsExpired && (! wfConfig::get('keyExpFinalSent')) ){
207
- wfConfig::set('keyExpFinalSent', 1);
208
- self::keyAlert("Your Wordfence Premium API Key has Expired!");
209
  }
210
  }
211
  }
 
 
 
212
 
213
  $wfdb = new wfDB();
214
  global $wpdb; $p = $wpdb->base_prefix;
@@ -518,6 +523,23 @@ SQL
518
  if (!$hasAttackLogTimeIndex) {
519
  $wpdb->query("ALTER TABLE $hitsTable ADD INDEX `attackLogTime` (`attackLogTime`)");
520
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
 
522
  //Must be the final line
523
  }
@@ -1038,6 +1060,7 @@ SQL
1038
  'homeURL' => home_url(),
1039
  'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
1040
  'howGetIPs' => (string) wfConfig::get('howGetIPs'),
 
1041
  );
1042
  foreach ($configDefaults as $key => $value) {
1043
  $waf->getStorageEngine()->setConfig($key, $value);
@@ -5262,8 +5285,8 @@ HTML
5262
  wp_enqueue_style('wordfence-activity-report-widget', wfUtils::getBaseURL() . 'css/activity-report-widget.css', '', WORDFENCE_VERSION);
5263
  $report_date_range = 'week';
5264
  switch (wfConfig::get('email_summary_interval')) {
5265
- case 'biweekly':
5266
- $report_date_range = '2 weeks';
5267
  break;
5268
 
5269
  case 'monthly':
@@ -5887,7 +5910,94 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
5887
  if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
5888
  $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
5889
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5890
 
 
 
 
 
 
 
5891
  $limit = 500;
5892
  $lastSendTime = wfConfig::get('lastAttackDataSendTime');
5893
  $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
@@ -5896,7 +6006,7 @@ AND attackLogTime > %.6f
5896
  LIMIT %d", $lastSendTime, $limit));
5897
  $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
5898
 
5899
- if ($attackData) {
5900
  $response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey')));
5901
 
5902
  if (!is_wp_error($response)) {
@@ -5971,6 +6081,9 @@ LIMIT %d", $lastSendTime, $limit));
5971
  self::scheduleSendAttackData(time() + 7200);
5972
  }
5973
  }
 
 
 
5974
 
5975
  self::trimWfHits();
5976
  }
170
  }
171
  public static function dailyCron(){
172
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
173
+ try {
174
+ $keyData = $api->call('ping_api_key');
175
+ if(isset($keyData['_isPaidKey']) && $keyData['_isPaidKey']){
176
+ $keyExpDays = $keyData['_keyExpDays'];
177
+ $keyIsExpired = $keyData['_expired'];
178
+ if (!empty($keyData['_autoRenew'])) {
179
+ if ($keyExpDays > 12) {
180
+ wfConfig::set('keyAutoRenew10Sent', '');
181
+ } else if ($keyExpDays <= 12 && $keyExpDays > 0 && !wfConfig::get('keyAutoRenew10Sent')) {
182
+ wfConfig::set('keyAutoRenew10Sent', 1);
183
+ $email = "Your Premium Wordfence API Key is set to auto-renew in 10 days.";
184
+ self::alert($email, "$email To update your API key settings please visit http://www.wordfence.com/zz9/dashboard", false);
185
+ }
186
+ } else {
187
+ if($keyExpDays > 15){
188
+ wfConfig::set('keyExp15Sent', '');
189
+ wfConfig::set('keyExp7Sent', '');
190
+ wfConfig::set('keyExp2Sent', '');
191
+ wfConfig::set('keyExp1Sent', '');
192
+ wfConfig::set('keyExpFinalSent', '');
193
+ } else if($keyExpDays <= 15 && $keyExpDays > 0){
194
+ if($keyExpDays <= 15 && $keyExpDays >= 11 && (! wfConfig::get('keyExp15Sent'))){
195
+ wfConfig::set('keyExp15Sent', 1);
196
+ self::keyAlert("Your Premium Wordfence API Key expires in less than 2 weeks.");
197
+ } else if($keyExpDays <= 7 && $keyExpDays >= 4 && (! wfConfig::get('keyExp7Sent'))){
198
+ wfConfig::set('keyExp7Sent', 1);
199
+ self::keyAlert("Your Premium Wordfence API Key expires in less than a week.");
200
+ } else if($keyExpDays == 2 && (! wfConfig::get('keyExp2Sent'))){
201
+ wfConfig::set('keyExp2Sent', 1);
202
+ self::keyAlert("Your Premium Wordfence API Key expires in 2 days.");
203
+ } else if($keyExpDays == 1 && (! wfConfig::get('keyExp1Sent'))){
204
+ wfConfig::set('keyExp1Sent', 1);
205
+ self::keyAlert("Your Premium Wordfence API Key expires in 1 day.");
206
+ }
207
+ } else if($keyIsExpired && (! wfConfig::get('keyExpFinalSent')) ){
208
+ wfConfig::set('keyExpFinalSent', 1);
209
+ self::keyAlert("Your Wordfence Premium API Key has Expired!");
210
  }
 
 
 
211
  }
212
  }
213
  }
214
+ catch(Exception $e){
215
+ wordfence::status(4, 'error', "Could not verify Wordfence API Key: " . $e->getMessage());
216
+ }
217
 
218
  $wfdb = new wfDB();
219
  global $wpdb; $p = $wpdb->base_prefix;
523
  if (!$hasAttackLogTimeIndex) {
524
  $wpdb->query("ALTER TABLE $hitsTable ADD INDEX `attackLogTime` (`attackLogTime`)");
525
  }
526
+
527
+ //6.1.16
528
+ $allowed404s = wfConfig::get('allowed404s', '');
529
+ if (!wfConfig::get('allowed404s6116Migration', false)) {
530
+ if (!preg_match('/(?:^|\b)browserconfig\.xml(?:\b|$)/i', $allowed404s)) {
531
+ if (strlen($allowed404s) > 0) {
532
+ $allowed404s .= "\n";
533
+ }
534
+ $allowed404s .= "/browserconfig.xml";
535
+ wfConfig::set('allowed404s', $allowed404s);
536
+ }
537
+
538
+ wfConfig::set('allowed404s6116Migration', 1);
539
+ }
540
+ if (wfConfig::get('email_summary_interval') == 'biweekly') {
541
+ wfConfig::set('email_summary_interval', 'weekly');
542
+ }
543
 
544
  //Must be the final line
545
  }
1060
  'homeURL' => home_url(),
1061
  'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
1062
  'howGetIPs' => (string) wfConfig::get('howGetIPs'),
1063
+ 'other_WFNet' => wfConfig::get('other_WFNet', true),
1064
  );
1065
  foreach ($configDefaults as $key => $value) {
1066
  $waf->getStorageEngine()->setConfig($key, $value);
5285
  wp_enqueue_style('wordfence-activity-report-widget', wfUtils::getBaseURL() . 'css/activity-report-widget.css', '', WORDFENCE_VERSION);
5286
  $report_date_range = 'week';
5287
  switch (wfConfig::get('email_summary_interval')) {
5288
+ case 'daily':
5289
+ $report_date_range = 'day';
5290
  break;
5291
 
5292
  case 'monthly':
5910
  if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
5911
  $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
5912
  }
5913
+
5914
+ //Send alert email if needed
5915
+ if (wfConfig::get('wafAlertOnAttacks')) {
5916
+ $alertInterval = wfConfig::get('wafAlertInterval', 0);
5917
+ $cutoffTime = max(time() - $alertInterval, wfConfig::get('wafAlertLastSendTime'));
5918
+ $wafAlertWhitelist = wfConfig::get('wafAlertWhitelist', '');
5919
+ $wafAlertWhitelist = preg_split("/[,\r\n]+/", $wafAlertWhitelist);
5920
+ foreach ($wafAlertWhitelist as $index => &$entry) {
5921
+ $entry = trim($entry);
5922
+ if (!preg_match('/^(?:\d{1,3}(?:\.|$)){4}/', $entry) && !preg_match('/^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/i', $entry)) {
5923
+ unset($wafAlertWhitelist[$index]);
5924
+ continue;
5925
+ }
5926
+
5927
+ $packed = @wfUtils::inet_pton($entry);
5928
+ if ($packed === false) {
5929
+ unset($wafAlertWhitelist[$index]);
5930
+ continue;
5931
+ }
5932
+ $entry = bin2hex($packed);
5933
+ }
5934
+ $wafAlertWhitelist = array_filter($wafAlertWhitelist);
5935
+ $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
5936
+ WHERE action = 'blocked:waf' " .
5937
+ (count($wafAlertWhitelist) ? "AND HEX(IP) NOT IN (" . implode(", ", array_fill(0, count($wafAlertWhitelist), '%s')) . ")" : "")
5938
+ . "AND attackLogTime > %.6f
5939
+ ORDER BY attackLogTime DESC
5940
+ LIMIT 10", array_merge($wafAlertWhitelist, array($cutoffTime))));
5941
+ $attackCount = $wpdb->get_var('SELECT FOUND_ROWS()');
5942
+ if ($attackCount >= wfConfig::get('wafAlertThreshold')) {
5943
+ $durationMessage = wfUtils::makeDuration($alertInterval);
5944
+ $message = <<<ALERTMSG
5945
+ The Wordfence Web Application Firewall has blocked {$attackCount} attacks over the last {$durationMessage}. Below is a sample of these recent attacks:
5946
+
5947
+
5948
+ ALERTMSG;
5949
+ $attackTable = array();
5950
+ $dateMax = $ipMax = $countryMax = 0;
5951
+ foreach ($attackData as $row) {
5952
+ $row->longDescription = "Blocked for " . $row->actionDescription;
5953
+
5954
+ $actionData = json_decode($row->actionData, true);
5955
+ if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
5956
+ continue;
5957
+ }
5958
+
5959
+ $paramKey = base64_decode($actionData['paramKey']);
5960
+ $paramValue = base64_decode($actionData['paramValue']);
5961
+ if (strlen($paramValue) > 100) {
5962
+ $paramValue = substr($paramValue, 0, 100) . chr(2026);
5963
+ }
5964
+
5965
+ if (preg_match('/([a-z0-9_]+\.[a-z0-9_]+)(?:\[(.+?)\](.*))?/i', $paramKey, $matches)) {
5966
+ switch ($matches[1]) {
5967
+ case 'request.queryString':
5968
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in query string: ' . $matches[2] . '=' . $paramValue;
5969
+ break;
5970
+ case 'request.body':
5971
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in POST body: ' . $matches[2] . '=' . $paramValue;
5972
+ break;
5973
+ case 'request.cookie':
5974
+ $row->longDescription = "Blocked for " . $row->actionDescription . ' in cookie: ' . $matches[2] . '=' . $paramValue;
5975
+ break;
5976
+ case 'request.fileNames':
5977
+ $row->longDescription = "Blocked for a " . $row->actionDescription . ' in file: ' . $matches[2] . '=' . $paramValue;
5978
+ break;
5979
+ }
5980
+ }
5981
+
5982
+ $date = date_i18n('F j, Y g:ia', floor($row->attackLogTime)); $dateMax = max(strlen($date), $dateMax);
5983
+ $ip = wfUtils::inet_ntop($row->IP); $ipMax = max(strlen($ip), $ipMax);
5984
+ $country = wfUtils::countryCode2Name(wfUtils::IP2Country($ip)); $country = (empty($country) ? 'Unknown' : $country); $countryMax = max(strlen($country), $countryMax);
5985
+ $attackTable[] = array('date' => $date, 'IP' => $ip, 'country' => $country, 'message' => $row->longDescription);
5986
+ }
5987
+
5988
+ foreach ($attackTable as $row) {
5989
+ $date = str_pad($row['date'], $dateMax + 2);
5990
+ $ip = str_pad($row['IP'] . " ({$row['country']})", $ipMax + $countryMax + 8);
5991
+ $attackMessage = $row['message'];
5992
+ $message .= $date . $ip . $attackMessage . "\n";
5993
+ }
5994
 
5995
+ self::alert('Increased Attack Rate', $message, false);
5996
+ wfConfig::set('wafAlertLastSendTime', time());
5997
+ }
5998
+ }
5999
+
6000
+ //Send attack data
6001
  $limit = 500;
6002
  $lastSendTime = wfConfig::get('lastAttackDataSendTime');
6003
  $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
6006
  LIMIT %d", $lastSendTime, $limit));
6007
  $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
6008
 
6009
+ if ($attackData && wfConfig::get('other_WFNet', true)) {
6010
  $response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey')));
6011
 
6012
  if (!is_wp_error($response)) {
6081
  self::scheduleSendAttackData(time() + 7200);
6082
  }
6083
  }
6084
+ else if (!wfConfig::get('other_WFNet', true)) {
6085
+ wfConfig::set('lastAttackDataSendTime', time());
6086
+ }
6087
 
6088
  self::trimWfHits();
6089
  }
lib/wordfenceScanner.php CHANGED
@@ -74,15 +74,24 @@ class wordfenceScanner {
74
  if(! (is_array($sigData) && isset($sigData['rules'])) ){
75
  throw new Exception("Wordfence could not get the attack signature patterns from the scanning server.");
76
  }
 
 
77
 
78
  if (is_array($sigData['rules'])) {
 
79
  foreach ($sigData['rules'] as $key => $signatureRow) {
80
  list(, , $pattern) = $signatureRow;
 
81
  if (@preg_match('/' . $pattern . '/i', null) === false) {
82
  wordfence::status(1, 'error', "A regex Wordfence received from it's servers is invalid. The pattern is: " . esc_html($pattern));
83
  unset($sigData['rules'][$key]);
84
  }
 
 
 
85
  }
 
 
86
  }
87
 
88
  $extra = wfConfig::get('scan_include_extra');
74
  if(! (is_array($sigData) && isset($sigData['rules'])) ){
75
  throw new Exception("Wordfence could not get the attack signature patterns from the scanning server.");
76
  }
77
+
78
+ try { wfWAF::getInstance()->setMalwareSignatures(array()); } catch (Exception $e) { /* Ignore */ }
79
 
80
  if (is_array($sigData['rules'])) {
81
+ $wafPatterns = array();
82
  foreach ($sigData['rules'] as $key => $signatureRow) {
83
  list(, , $pattern) = $signatureRow;
84
+ $logOnly = (isset($signatureRow[5]) && !empty($signatureRow[5])) ? $signatureRow[5] : false;
85
  if (@preg_match('/' . $pattern . '/i', null) === false) {
86
  wordfence::status(1, 'error', "A regex Wordfence received from it's servers is invalid. The pattern is: " . esc_html($pattern));
87
  unset($sigData['rules'][$key]);
88
  }
89
+ else if (!$logOnly) {
90
+ $wafPatterns[] = $pattern;
91
+ }
92
  }
93
+
94
+ try { wfWAF::getInstance()->setMalwareSignatures($wafPatterns); } catch (Exception $e) { /* Ignore */ }
95
  }
96
 
97
  $extra = wfConfig::get('scan_include_extra');
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking, block hackers
4
  Requires at least: 3.9
5
- Tested up to: 4.6.0
6
- Stable tag: 6.1.15
7
 
8
  Secure your website with the Wordfence security plugin for WordPress. Wordfence provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
 
@@ -200,6 +200,16 @@ Secure your website with Wordfence.
200
 
201
  == Changelog ==
202
 
 
 
 
 
 
 
 
 
 
 
203
  = 6.1.15 =
204
  * Improvement: Removed file-based config caching, added support for caching via WordPress's object cache.
205
  * Improvement: Whitelisted Uptime Robot's IP range.
2
  Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking, block hackers
4
  Requires at least: 3.9
5
+ Tested up to: 4.6.1
6
+ Stable tag: 6.1.16
7
 
8
  Secure your website with the Wordfence security plugin for WordPress. Wordfence provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
 
200
 
201
  == Changelog ==
202
 
203
+ = 6.1.16 =
204
+ * Improvement: Now performing malware scanning on all uploaded files in real-time.
205
+ * Improvement: Added Web Application Firewall activity to Wordfence summary email.
206
+ * Fix: Now using 503 response code in the page displayed when an IP is locked out.
207
+ * Fix: `wflogs` directory is now correctly removed on uninstall.
208
+ * Fix: Fixed recently introduced bug which caused the Whitelisted 404 URLs feature to no longer work.
209
+ * Fix: Added try/catch to uncaught exception thrown when pinging the API key.
210
+ * Improvement: Improved performance of the Live Traffic page in Firefox.
211
+ * Improvement: Updated GeoIP database.
212
+
213
  = 6.1.15 =
214
  * Improvement: Removed file-based config caching, added support for caching via WordPress's object cache.
215
  * Improvement: Whitelisted Uptime Robot's IP range.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -1,10 +1,10 @@
1
  <?php
2
 
3
- define('WFWAF_VERSION', '1.0.1');
4
  define('WFWAF_PATH', dirname(__FILE__) . '/');
5
  define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
  define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
- define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.3/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
1
  <?php
2
 
3
+ define('WFWAF_VERSION', '1.0.2');
4
  define('WFWAF_PATH', dirname(__FILE__) . '/');
5
  define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
  define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
+ define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.4/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
vendor/wordfence/wf-waf/src/lib/rules.php CHANGED
@@ -456,6 +456,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
456
  'currentuseris',
457
  'currentuserisnot',
458
  'md5equals',
 
459
  );
460
 
461
  /**
@@ -697,6 +698,42 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
697
  public function md5Equals($subject) {
698
  return md5((string) $subject) === $this->getExpected();
699
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
 
701
  /**
702
  * @return mixed
456
  'currentuseris',
457
  'currentuserisnot',
458
  'md5equals',
459
+ 'filepatternsmatch',
460
  );
461
 
462
  /**
698
  public function md5Equals($subject) {
699
  return md5((string) $subject) === $this->getExpected();
700
  }
701
+
702
+ public function filePatternsMatch($subject) {
703
+ $request = $this->getWAF()->getRequest();
704
+ $files = $request->getFiles();
705
+ $patterns = $this->getWAF()->getMalwareSignatures();
706
+ if (!is_array($patterns)) {
707
+ return false;
708
+ }
709
+
710
+ foreach ($files as $file) {
711
+ if ($file['name'] == (string) $subject) {
712
+ $fh = @fopen($file['tmp_name'], 'r');
713
+ if (!$fh) {
714
+ return false;
715
+ }
716
+ $totalRead = 0;
717
+
718
+ $readsize = max(min(10 * 1024 * 1024, wfWAFUtils::iniSizeToBytes(ini_get('upload_max_filesize'))), 1 * 1024 * 1024);
719
+ while (!feof($fh)) {
720
+ $data = fread($fh, $readsize);
721
+ $totalRead += strlen($data);
722
+ if ($totalRead < 1) {
723
+ return false;
724
+ }
725
+
726
+ foreach ($patterns as $rule) {
727
+ if (preg_match('/(' . $rule . ')/i', $data, $matches)) {
728
+ return true;
729
+ }
730
+ }
731
+ }
732
+ }
733
+ }
734
+
735
+ return false;
736
+ }
737
 
738
  /**
739
  * @return mixed
vendor/wordfence/wf-waf/src/lib/utils.php CHANGED
@@ -525,4 +525,23 @@ class wfWAFUtils {
525
  $args = func_get_args();
526
  return self::callMBSafeStrFunction('strrpos', $args);
527
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  }
525
  $args = func_get_args();
526
  return self::callMBSafeStrFunction('strrpos', $args);
527
  }
528
+
529
+ /**
530
+ * @param string $val An ini byte size value (e.g., 20M)
531
+ * @return int
532
+ */
533
+ public static function iniSizeToBytes($val) {
534
+ $val = trim($val);
535
+ $last = strtolower(substr($val, -1));
536
+ switch ($last) {
537
+ case 'g':
538
+ $val *= 1024;
539
+ case 'm':
540
+ $val *= 1024;
541
+ case 'k':
542
+ $val *= 1024;
543
+ }
544
+
545
+ return intval($val);
546
+ }
547
  }
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -323,6 +323,12 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
323
  $this->getStorageEngine()->setConfig('cron', $cron);
324
  }
325
 
 
 
 
 
 
 
326
  $this->getStorageEngine()->setConfig('version', WFWAF_VERSION);
327
  }
328
  }
@@ -416,6 +422,57 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
416
  }
417
  return false;
418
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
  /**
421
  * @param $rules
@@ -879,6 +936,12 @@ HTML
879
  if ($this->getStorageEngine()->getConfig('attackDataKey', false) === false) {
880
  $this->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
881
  }
 
 
 
 
 
 
882
 
883
  $request = new wfWAFHTTP();
884
  try {
@@ -1309,6 +1372,42 @@ class wfWAFCronFetchRulesEvent extends wfWAFCronEvent {
1309
 
1310
  }
1311
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1312
  } catch (wfWAFHTTPTransportException $e) {
1313
  error_log($e->getMessage());
1314
  } catch (wfWAFBuildRulesException $e) {
323
  $this->getStorageEngine()->setConfig('cron', $cron);
324
  }
325
 
326
+ if (version_compare($currentVersion, '1.0.2') === -1) {
327
+ $event = new wfWAFCronFetchRulesEvent(time() - 2);
328
+ $event->setWaf($this);
329
+ $event->fire();
330
+ }
331
+
332
  $this->getStorageEngine()->setConfig('version', WFWAF_VERSION);
333
  }
334
  }
422
  }
423
  return false;
424
  }
425
+
426
+ /**
427
+ * @return array
428
+ */
429
+ public function getMalwareSignatures() {
430
+ try {
431
+ $encoded = $this->getStorageEngine()->getConfig('filePatterns');
432
+ if (empty($encoded)) {
433
+ return array();
434
+ }
435
+
436
+ $authKey = $this->getStorageEngine()->getConfig('authKey');
437
+ $encoded = base64_decode($encoded);
438
+ $paddedKey = substr(str_repeat($authKey, ceil(strlen($encoded) / strlen($authKey))), 0, strlen($encoded));
439
+ $json = $encoded ^ $paddedKey;
440
+ $signatures = json_decode($json, true);
441
+ if (!is_array($signatures)) {
442
+ return array();
443
+ }
444
+ return $signatures;
445
+ }
446
+ catch (Exception $e) {
447
+ //Ignore
448
+ }
449
+ return array();
450
+ }
451
+
452
+ /**
453
+ * @param array $signatures
454
+ * @param bool $updateLastUpdatedTimestamp
455
+ */
456
+ public function setMalwareSignatures($signatures, $updateLastUpdatedTimestamp = true) {
457
+ try {
458
+ if (!is_array($signatures)) {
459
+ $signatures = array();
460
+ }
461
+
462
+ $authKey = $this->getStorageEngine()->getConfig('authKey');
463
+ $json = json_encode($signatures);
464
+ $paddedKey = substr(str_repeat($authKey, ceil(strlen($json) / strlen($authKey))), 0, strlen($json));
465
+ $payload = $json ^ $paddedKey;
466
+ $this->getStorageEngine()->setConfig('filePatterns', base64_encode($payload));
467
+
468
+ if ($updateLastUpdatedTimestamp) {
469
+ $this->getStorageEngine()->setConfig('signaturesLastUpdated', is_int($updateLastUpdatedTimestamp) ? $updateLastUpdatedTimestamp : time());
470
+ }
471
+ }
472
+ catch (Exception $e) {
473
+ //Ignore
474
+ }
475
+ }
476
 
477
  /**
478
  * @param $rules
936
  if ($this->getStorageEngine()->getConfig('attackDataKey', false) === false) {
937
  $this->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
938
  }
939
+
940
+ if (!$this->getStorageEngine()->getConfig('other_WFNet', true)) {
941
+ $this->getStorageEngine()->truncateAttackData();
942
+ $this->getStorageEngine()->unsetConfig('attackDataNextInterval');
943
+ return;
944
+ }
945
 
946
  $request = new wfWAFHTTP();
947
  try {
1372
 
1373
  }
1374
  }
1375
+
1376
+ $this->response = wfWAFHTTP::get(WFWAF_API_URL_SEC . "?" . http_build_query(array(
1377
+ 'action' => 'get_malware_signatures',
1378
+ 'k' => $waf->getStorageEngine()->getConfig('apiKey'),
1379
+ 's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') : $guessSiteURL,
1380
+ 'h' => $waf->getStorageEngine()->getConfig('homeURL') ? $waf->getStorageEngine()->getConfig('homeURL') : $guessSiteURL,
1381
+ 'openssl' => $waf->hasOpenSSL() ? 1 : 0,
1382
+ 'betaFeed' => (int) $waf->getStorageEngine()->getConfig('betaThreatDefenseFeed'),
1383
+ ), null, '&'));
1384
+ if ($this->response) {
1385
+ $jsonData = wfWAFUtils::json_decode($this->response->getBody(), true);
1386
+ if (is_array($jsonData)) {
1387
+ if ($waf->hasOpenSSL() &&
1388
+ isset($jsonData['data']['signature']) &&
1389
+ isset($jsonData['data']['signatures']) &&
1390
+ $waf->verifySignedRequest(base64_decode($jsonData['data']['signature']), $jsonData['data']['signatures'])
1391
+ ) {
1392
+ $waf->setMalwareSignatures(json_decode(base64_decode($jsonData['data']['signatures'])),
1393
+ isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true);
1394
+ if (array_key_exists('premiumCount', $jsonData['data'])) {
1395
+ $waf->getStorageEngine()->setConfig('signaturePremiumCount', $jsonData['data']['premiumCount']);
1396
+ }
1397
+
1398
+ } else if (!$waf->hasOpenSSL() &&
1399
+ isset($jsonData['data']['hash']) &&
1400
+ isset($jsonData['data']['signatures']) &&
1401
+ $waf->verifyHashedRequest($jsonData['data']['hash'], $jsonData['data']['signatures'])
1402
+ ) {
1403
+ $waf->setMalwareSignatures(json_decode(base64_decode($jsonData['data']['signatures'])),
1404
+ isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true);
1405
+ if (array_key_exists('premiumCount', $jsonData['data'])) {
1406
+ $waf->getStorageEngine()->setConfig('signaturePremiumCount', $jsonData['data']['premiumCount']);
1407
+ }
1408
+ }
1409
+ }
1410
+ }
1411
  } catch (wfWAFHTTPTransportException $e) {
1412
  error_log($e->getMessage());
1413
  } catch (wfWAFBuildRulesException $e) {
views/reports/activity-report-email-inline.php CHANGED
@@ -256,6 +256,52 @@ h6 a:visited { color: purple !important; }
256
  <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
257
  <a class="button" href="<?php echo network_admin_url('admin.php?page=WordfenceSecOpt#wfMarkerLoginSecurity') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Login Security Options</a>
258
  </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  <?php wfHelperString::cycle(); ?>
261
 
@@ -303,7 +349,7 @@ h6 a:visited { color: purple !important; }
303
  $newVersion = ($plugin['newVersion'] == 'Unknown' ? $plugin['newVersion'] : "v{$plugin['newVersion']}");
304
  ?>
305
  <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
306
- A new version of the plugin "<?php echo esc_html("{$plugin['Name']} ({$newVersion})") ?>" is available.
307
  </li>
308
  <?php endforeach ?>
309
  </ul>
@@ -316,7 +362,7 @@ h6 a:visited { color: purple !important; }
316
  $newVersion = ($theme['newVersion'] == 'Unknown' ? $theme['newVersion'] : "v{$theme['newVersion']}");
317
  ?>
318
  <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
319
- A new version of the theme "<?php echo esc_html("{$theme['name']} ({$newVersion})") ?>" is available.
320
  </li>
321
  <?php endforeach ?>
322
  </ul>
256
  <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
257
  <a class="button" href="<?php echo network_admin_url('admin.php?page=WordfenceSecOpt#wfMarkerLoginSecurity') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Login Security Options</a>
258
  </p>
259
+
260
+ <?php wfHelperString::cycle(); ?>
261
+
262
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Recently Blocked Attacks</h2>
263
+
264
+ <table class="activity-table" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; max-width: 100%; margin: 0; padding: 0; border: 0;">
265
+ <thead style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
266
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
267
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Time</th>
268
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">IP / Action</th>
269
+ </tr>
270
+ </thead>
271
+ <tbody style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
272
+ <?php if (count($recent_firewall_activity) > 0): ?>
273
+ <?php foreach ($recent_firewall_activity as $attack_row):
274
+ ?>
275
+ <?php
276
+ $stripe = wfHelperString::cycle('odd', 'even');
277
+ ?>
278
+ <tr class="<?php echo $stripe ?>" style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
279
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;white-space: nowrap;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo $this->attackTime($attack_row->attackLogTime) ?></td>
280
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline">
281
+ <div style="font-weight: bold; font-size: 12px;"><?php echo $this->displayIP($attack_row->IP) ?></div>
282
+ <pre class="display-file" style="font-size: 12px; vertical-align: baseline; width: 420px; margin: 0; padding: 0; border: 0; white-space: normal;"><?php echo esc_html($attack_row->longDescription) ?></pre>
283
+ </td>
284
+ </tr>
285
+ <?php endforeach ?>
286
+ <?php else: ?>
287
+ <tr>
288
+ <td colspan="2">
289
+ No blocked attacks yet.
290
+ </td>
291
+ </tr>
292
+ <?php endif ?>
293
+ </tbody>
294
+ </table>
295
+
296
+ <?php
297
+ if ($omitted_firewall_activity > 10):
298
+ ?>
299
+ <div style="font-size: 14px; vertical-align: baseline; clear: both; color: #f00 !important; margin: 8px 0 4px; padding: 0; border: 0;">and <?php echo $omitted_firewall_activity ?> additional attacks</div>
300
+ <?php endif ?>
301
+
302
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
303
+ <a class="button" href="<?php echo network_admin_url('admin.php?page=WordfenceActivity') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">View Recent Traffic</a>
304
+ </p>
305
 
306
  <?php wfHelperString::cycle(); ?>
307
 
349
  $newVersion = ($plugin['newVersion'] == 'Unknown' ? $plugin['newVersion'] : "v{$plugin['newVersion']}");
350
  ?>
351
  <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
352
+ A new version of the plugin "<?php echo esc_html("{$plugin['Name']} ({$newVersion})") ?>" is available.<?php if (isset($plugin['vulnerabilityPatched']) && $plugin['vulnerabilityPatched']) { echo " <strong>This update includes security-related fixes.</strong>"; } ?>
353
  </li>
354
  <?php endforeach ?>
355
  </ul>
362
  $newVersion = ($theme['newVersion'] == 'Unknown' ? $theme['newVersion'] : "v{$theme['newVersion']}");
363
  ?>
364
  <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
365
+ A new version of the theme "<?php echo esc_html("{$theme['name']} ({$newVersion})") ?>" is available.<?php if (isset($theme['vulnerabilityPatched']) && $theme['vulnerabilityPatched']) { echo " <strong>This update includes security-related fixes.</strong>"; } ?>
366
  </li>
367
  <?php endforeach ?>
368
  </ul>
waf/bootstrap.php CHANGED
@@ -184,6 +184,12 @@ class wfWAFWordPress extends wfWAF {
184
  public function isIPBlocked($ip) {
185
  return false;
186
  }
 
 
 
 
 
 
187
 
188
  /**
189
  * @return wfWAFRunException
184
  public function isIPBlocked($ip) {
185
  return false;
186
  }
187
+
188
+ public function uninstall() {
189
+ parent::uninstall();
190
+ @unlink(rtrim(WFWAF_LOG_PATH . '/') . '/.htaccess');
191
+ @rmdir(WFWAF_LOG_PATH);
192
+ }
193
 
194
  /**
195
  * @return wfWAFRunException
wordfence.php CHANGED
@@ -4,14 +4,14 @@ 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: 6.1.15
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.1.15');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 6.1.16
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.1.16');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17