Wordfence Security – Firewall & Malware Scan - Version 6.2.1

Version Description

  • Improvement: Now performing scanning for PHP code in all uploaded files in real-time.
  • Improvement: Improved handling of bad characters and IPv6 ranges in Advanced Blocking.
  • Improvement: Live traffic and scanning activity now display a paused notice when real-time updates are suspended while in the background.
  • Improvement: The file system scan alerts for files flagged by antivirus software with a '.suspected' extension.
  • Improvement: New alert option to get notified only when logins are from a new location/device.
  • Change: First phase for removing the Falcon cache in place, which will add a notice of its pending removal.
  • Fix: Included country flags for Kosovo and Curaao.
  • Fix: Fixed the .htaccess directives used to hide files found by the scanner.
  • Fix: Dashboard widget shows correct status for failed logins by deleted users.
  • Fix: Removed duplicate issues for modified files in the scan results.
  • Fix: Suppressed warning from reverse lookup on IPv6 addresses without valid DNS records.
  • Fix: Fixed file inclusion error with themes lacking a 404 page.
  • Fix: CSS fixes for activity report email.
Download this release

Release Info

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

Code changes from version 6.2.0 to 6.2.1

css/main.css CHANGED
@@ -5,6 +5,7 @@
5
  margin: 20px 0 0 20px;
6
  }
7
  div.wordfenceLive {
 
8
  height: 29px;
9
  white-space: nowrap;
10
  overflow: hidden;
@@ -37,6 +38,118 @@ div.wordfenceLive p {
37
  font-weight: normal;
38
  display: inline;
39
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  .wordfence-icon32 {
41
  width: 32px;
42
  height: 32px;
5
  margin: 20px 0 0 20px;
6
  }
7
  div.wordfenceLive {
8
+ position: relative;
9
  height: 29px;
10
  white-space: nowrap;
11
  overflow: hidden;
38
  font-weight: normal;
39
  display: inline;
40
  }
41
+ div.wordfenceLive .wordfenceLiveActivity::after {
42
+ position: absolute;
43
+ z-index: 3000;
44
+ top: 0;
45
+ right: 0;
46
+ width: 0;
47
+ height: 0;
48
+ background: rgba(255, 252, 239, 0.9);
49
+ content: '';
50
+ opacity: 0;
51
+ -webkit-transition: opacity 0.5s, width 0.1s, height 0.1s;
52
+ -webkit-transition-delay: 0s, 0.5s, 0.5s;
53
+ -moz-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
54
+ -o-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
55
+ transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
56
+ }
57
+ .wordfenceLiveActivityPaused div.wordfenceLive .wordfenceLiveActivity::after {
58
+ width: 100%;
59
+ height: 100%;
60
+ opacity: 1;
61
+ -webkit-transition: opacity 0.5s;
62
+ transition: opacity 0.5s;
63
+ }
64
+ div.wordfenceLive .wordfenceLiveStateMessage {
65
+ display: none;
66
+ position: absolute;
67
+ z-index: 3001;
68
+ top: 0;
69
+ left: 0;
70
+ width: 100%;
71
+ height: 100%;
72
+ text-align: center;
73
+ padding: 3px;
74
+ font-weight: normal;
75
+ line-height: 29px;
76
+ color: #666666;
77
+ opacity: 0;
78
+ -webkit-transition: opacity 0.5s, width 0.1s, height 0.1s;
79
+ -webkit-transition-delay: 0s, 0.5s, 0.5s;
80
+ -moz-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
81
+ -o-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
82
+ transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
83
+ }
84
+ div.wordfenceLive .wordfenceLiveStateMessage td {
85
+ padding: 0px;
86
+ }
87
+ .wordfenceLiveActivityPaused div.wordfenceLive .wordfenceLiveStateMessage {
88
+ opacity: 1;
89
+ -webkit-transition: opacity 0.5s;
90
+ transition: opacity 0.5s;
91
+ display: table;
92
+ }
93
+ #wfLiveTrafficOverlayAnchor::after {
94
+ position: absolute;
95
+ z-index: 3002;
96
+ top: 0;
97
+ right: 0;
98
+ width: 0;
99
+ height: 0;
100
+ background: rgba(241, 241, 241, 0.6);
101
+ content: '';
102
+ opacity: 0;
103
+ -webkit-transition: opacity 0.5s, width 0.1s, height 0.1s;
104
+ -webkit-transition-delay: 0s, 0.5s, 0.5s;
105
+ -moz-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
106
+ -o-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
107
+ transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
108
+ }
109
+ .wordfenceLiveActivityPaused #wfLiveTrafficOverlayAnchor::after {
110
+ width: 100%;
111
+ height: 100%;
112
+ opacity: 1;
113
+ -webkit-transition: opacity 0.5s;
114
+ transition: opacity 0.5s;
115
+ }
116
+ #wfLiveTrafficDisabledMessage {
117
+ display: none;
118
+ position: fixed;
119
+ z-index: 3003;
120
+ left: 0;
121
+ width: 100%;
122
+ top: 50%;
123
+ transform: translateY(-50%);
124
+ text-align: center;
125
+ color: #666666;
126
+ opacity: 0;
127
+ -webkit-transition: opacity 0.5s, width 0.1s, height 0.1s;
128
+ -webkit-transition-delay: 0s, 0.5s, 0.5s;
129
+ -moz-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
130
+ -o-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
131
+ transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s;
132
+ }
133
+ #wfLiveTrafficDisabledMessage h2 {
134
+ background-color: #FFF;
135
+ overflow: hidden;
136
+ border: 1px solid #CCC;
137
+ max-width: 350px;
138
+ margin: 0 auto;
139
+ padding: 15px;
140
+ font-size: 2.0em;
141
+ }
142
+ #wfLiveTrafficDisabledMessage h2 small {
143
+ font-size: 0.5em;
144
+ font-weight: normal;
145
+ margin-top: 20px;
146
+ }
147
+ .wordfenceLiveActivityPaused #wfLiveTrafficDisabledMessage {
148
+ display: block;
149
+ opacity: 1;
150
+ -webkit-transition: opacity 0.5s;
151
+ transition: opacity 0.5s;
152
+ }
153
  .wordfence-icon32 {
154
  width: 32px;
155
  height: 32px;
images/flags/cw.png ADDED
Binary file
images/flags/xk.png ADDED
Binary file
js/admin.js CHANGED
@@ -399,8 +399,14 @@
399
  },
400
  updateActivityLog: function() {
401
  if (this.activityLogUpdatePending || !this.windowHasFocus()) {
 
 
 
402
  return;
403
  }
 
 
 
404
  this.activityLogUpdatePending = true;
405
  var self = this;
406
  this.ajax('wordfence_activityLogUpdate', {
@@ -578,8 +584,14 @@
578
  },
579
  updateTicker: function(forceUpdate) {
580
  if ((!forceUpdate) && (this.tickerUpdatePending || !this.windowHasFocus())) {
 
 
 
581
  return;
582
  }
 
 
 
583
  this.tickerUpdatePending = true;
584
  var self = this;
585
  var alsoGet = '';
@@ -875,7 +887,8 @@
875
  "sWidth": '400px',
876
  "sType": 'html',
877
  fnRender: function(obj) {
878
- var tmplName = 'issueTmpl_' + obj.aData.type;
 
879
  return jQuery('#' + tmplName).tmpl(obj.aData).html();
880
  }
881
  }
@@ -1543,15 +1556,62 @@
1543
  return;
1544
  }
1545
  range = range.replace(/ /g, '');
1546
- if (range && /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*\-\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(range)) {
 
 
 
 
 
 
1547
  var ips = range.split('-');
1548
- var total = this.inet_aton(ips[1]) - this.inet_aton(ips[0]) + 1;
1549
- if (total < 1) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1550
  jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid. Starting IP is greater than ending IP.</span>");
1551
  return;
1552
  }
1553
- jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: " + total + " addresses in range.</span>");
1554
- } else {
 
 
 
 
 
 
1555
  jQuery('#wfShowRangeTotal').empty();
1556
  }
1557
  },
@@ -1649,6 +1709,7 @@
1649
  return;
1650
  }
1651
  ipRange = ipRange.replace(/ /g, '').toLowerCase();
 
1652
  if (ipRange) {
1653
  var range = ipRange.split('-'),
1654
  validRange;
@@ -2255,33 +2316,42 @@
2255
  // Return if 4 bytes, otherwise false.
2256
  return m.length === 4 ? m : false;
2257
  }
2258
- r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
2259
  m = a.match(r); // IPv6
2260
  if (m) {
2261
- // Translate each hexadecimal value.
2262
- for (j = 1; j < 4; j++) {
2263
- // Indice 2 is :: and if no length, continue.
2264
- if (j === 2 || m[j].length === 0) {
2265
- continue;
2266
- }
2267
- m[j] = m[j].split(':');
2268
- for (i = 0; i < m[j].length; i++) {
2269
- m[j][i] = parseInt(m[j][i], 16);
2270
- // Would be NaN if it was blank, return false.
2271
- if (isNaN(m[j][i])) {
2272
- return false; // Invalid IP.
2273
- }
2274
- m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
2275
  }
2276
- m[j] = m[j].join('');
 
2277
  }
2278
- x = m[1].length + m[3].length;
2279
- if (x === 16) {
2280
- return m[1] + m[3];
2281
- } else if (x < 16 && m[2].length > 0) {
2282
- return m[1] + (new Array(16 - x + 1))
2283
- .join('\x00') + m[3];
 
 
 
 
 
 
 
 
 
2284
  }
 
 
2285
  }
2286
  return false; // Invalid IP.
2287
  },
@@ -2668,6 +2738,11 @@
2668
  }
2669
  jQuery(function() {
2670
  wordfenceAdmin.init();
 
 
 
 
 
2671
  });
2672
  })(jQuery);
2673
 
399
  },
400
  updateActivityLog: function() {
401
  if (this.activityLogUpdatePending || !this.windowHasFocus()) {
402
+ if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.activityLogUpdatePending) {
403
+ jQuery('body').addClass('wordfenceLiveActivityPaused');
404
+ }
405
  return;
406
  }
407
+ if (jQuery('body').hasClass('wordfenceLiveActivityPaused')) {
408
+ jQuery('body').removeClass('wordfenceLiveActivityPaused');
409
+ }
410
  this.activityLogUpdatePending = true;
411
  var self = this;
412
  this.ajax('wordfence_activityLogUpdate', {
584
  },
585
  updateTicker: function(forceUpdate) {
586
  if ((!forceUpdate) && (this.tickerUpdatePending || !this.windowHasFocus())) {
587
+ if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.tickerUpdatePending) {
588
+ jQuery('body').addClass('wordfenceLiveActivityPaused');
589
+ }
590
  return;
591
  }
592
+ if (jQuery('body').hasClass('wordfenceLiveActivityPaused')) {
593
+ jQuery('body').removeClass('wordfenceLiveActivityPaused');
594
+ }
595
  this.tickerUpdatePending = true;
596
  var self = this;
597
  var alsoGet = '';
887
  "sWidth": '400px',
888
  "sType": 'html',
889
  fnRender: function(obj) {
890
+ var issueType = (obj.aData.type == 'knownfile' ? 'file' : obj.aData.type);
891
+ var tmplName = 'issueTmpl_' + issueType;
892
  return jQuery('#' + tmplName).tmpl(obj.aData).html();
893
  }
894
  }
1556
  return;
1557
  }
1558
  range = range.replace(/ /g, '');
1559
+ range = range.replace(/[\u2013-\u2015]/g, '-'); //Non-hyphen dashes to hyphen
1560
+ if (range && /^[^\-]+\-[^\-]+$/.test(range)) {
1561
+ var count = 1;
1562
+ var countOverflow = false;
1563
+ var badRange = false;
1564
+ var badIP = false;
1565
+
1566
  var ips = range.split('-');
1567
+ var ip1 = this.inet_pton(ips[0]);
1568
+ var ip2 = this.inet_pton(ips[1]);
1569
+
1570
+ if (ip1 === false || ip2 === false) {
1571
+ badIP = true;
1572
+ }
1573
+ else {
1574
+ //Both to 16-byte binary strings
1575
+ var binStart = ("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" + ip1).slice(-16);
1576
+ var binEnd = ("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" + ip2).slice(-16);
1577
+
1578
+ for (var i = 0; i < binStart.length; i++) {
1579
+ var n0 = binStart.charCodeAt(i);
1580
+ var n1 = binEnd.charCodeAt(i);
1581
+
1582
+ if (i < 11 && n1 - n0 > 0) { //Based on Number.MAX_SAFE_INTEGER, which equals 2 ^ 53 - 1. Any of the first 9 bytes and part of the 10th that add to the range will put us over that
1583
+ countOverflow = true;
1584
+ break;
1585
+ }
1586
+ else if (i < 11 && n1 - n0 < 0) {
1587
+ badRange = true;
1588
+ break;
1589
+ }
1590
+
1591
+ count += (n1 - n0) << (8 * (15 - i));
1592
+ if (count < 1) {
1593
+ badRange = true;
1594
+ break;
1595
+ }
1596
+ }
1597
+ }
1598
+
1599
+ if (badIP) {
1600
+ jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid IP entered.</span>");
1601
+ return;
1602
+ }
1603
+ else if (badRange) {
1604
  jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid. Starting IP is greater than ending IP.</span>");
1605
  return;
1606
  }
1607
+ else if (countOverflow) {
1608
+ jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: &gt;281474976710656 addresses in range.</span>");
1609
+ return;
1610
+ }
1611
+
1612
+ jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: " + count + " addresses in range.</span>");
1613
+ }
1614
+ else {
1615
  jQuery('#wfShowRangeTotal').empty();
1616
  }
1617
  },
1709
  return;
1710
  }
1711
  ipRange = ipRange.replace(/ /g, '').toLowerCase();
1712
+ ipRange = ipRange.replace(/[\u2013-\u2015]/g, '-'); //Non-hyphen dashes to hyphen
1713
  if (ipRange) {
1714
  var range = ipRange.split('-'),
1715
  validRange;
2316
  // Return if 4 bytes, otherwise false.
2317
  return m.length === 4 ? m : false;
2318
  }
2319
+ r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/i;
2320
  m = a.match(r); // IPv6
2321
  if (m) {
2322
+ if (a == '::') {
2323
+ return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
2324
+ }
2325
+
2326
+ var colonCount = a.split(':').length - 1;
2327
+ var doubleColonPos = a.indexOf('::');
2328
+ if (doubleColonPos > -1) {
2329
+ var expansionLength = ((doubleColonPos == 0 || doubleColonPos == a.length - 2) ? 9 : 8) - colonCount;
2330
+ var expansion = '';
2331
+ for (i = 0; i < expansionLength; i++) {
2332
+ expansion += ':0000';
 
 
 
2333
  }
2334
+ a = a.replace('::', expansion + ':');
2335
+ a = a.replace(/(?:^\:|\:$)/, '', a);
2336
  }
2337
+
2338
+ var ipGroups = a.split(':');
2339
+ var ipBin = '';
2340
+ for (i = 0; i < ipGroups.length; i++) {
2341
+ var group = ipGroups[i];
2342
+ if (group.length > 4) {
2343
+ return false;
2344
+ }
2345
+ group = ("0000" + group).slice(-4);
2346
+ var b1 = parseInt(group.slice(0, 2), 16);
2347
+ var b2 = parseInt(group.slice(-2), 16);
2348
+ if (isNaN(b1) || isNaN(b2)) {
2349
+ return false;
2350
+ }
2351
+ ipBin += f(b1) + f(b2);
2352
  }
2353
+
2354
+ return ipBin.length == 16 ? ipBin : false;
2355
  }
2356
  return false; // Invalid IP.
2357
  },
2738
  }
2739
  jQuery(function() {
2740
  wordfenceAdmin.init();
2741
+ jQuery(window).on('focus', function() {
2742
+ if (jQuery('body').hasClass('wordfenceLiveActivityPaused')) {
2743
+ jQuery('body').removeClass('wordfenceLiveActivityPaused');
2744
+ }
2745
+ });
2746
  });
2747
  })(jQuery);
2748
 
js/tourTip.js CHANGED
@@ -41,6 +41,14 @@ window['wordfenceExt'] = {
41
  function(){ jQuery('#wordfenceSuPHPUpdateWarning').fadeOut(); }
42
  );
43
  },
 
 
 
 
 
 
 
 
44
  removeFromCache: function(postID){
45
  this.ajax('wordfence_removeFromCache', {
46
  id: postID
41
  function(){ jQuery('#wordfenceSuPHPUpdateWarning').fadeOut(); }
42
  );
43
  },
44
+ falconDeprecationChoice: function(choice) {
45
+ this.ajax('wordfence_falconDeprecationChoice', {
46
+ choice: choice
47
+ },
48
+ function(res){ jQuery('#wordfenceFalconDeprecationWarning').fadeOut(); },
49
+ function(){ jQuery('#wordfenceFalconDeprecationWarning').fadeOut(); }
50
+ );
51
+ },
52
  removeFromCache: function(postID){
53
  this.ajax('wordfence_removeFromCache', {
54
  id: postID
lib/menu_activity.php CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  <div class="wrap wordfence">
2
  <?php require('menuHeader.php'); ?>
3
 
@@ -23,12 +29,17 @@
23
 
24
  <div class="wordfenceModeElem" id="wordfenceMode_activity"></div>
25
  <div class="wordfenceLive">
26
- <table border="0" cellpadding="0" cellspacing="0">
27
  <tr>
28
  <td><h2>Wordfence Live Activity:</h2></td>
29
  <td id="wfLiveStatus"></td>
30
  </tr>
31
  </table>
 
 
 
 
 
32
  </div>
33
  <div class="wordfenceWrap<?php if (!wfConfig::get('isPaid')) { echo " wordfence-community"; }?>">
34
  <?php
1
+ <?php if (wfConfig::liveTrafficEnabled()): ?>
2
+ <div id="wfLiveTrafficOverlayAnchor"></div>
3
+ <div id="wfLiveTrafficDisabledMessage">
4
+ <h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
5
+ </div>
6
+ <?php endif ?>
7
  <div class="wrap wordfence">
8
  <?php require('menuHeader.php'); ?>
9
 
29
 
30
  <div class="wordfenceModeElem" id="wordfenceMode_activity"></div>
31
  <div class="wordfenceLive">
32
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveActivity">
33
  <tr>
34
  <td><h2>Wordfence Live Activity:</h2></td>
35
  <td id="wfLiveStatus"></td>
36
  </tr>
37
  </table>
38
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveStateMessage">
39
+ <tr>
40
+ <td>Live Updates Paused &mdash; Click inside window to resume</td>
41
+ </tr>
42
+ </table>
43
  </div>
44
  <div class="wordfenceWrap<?php if (!wfConfig::get('isPaid')) { echo " wordfence-community"; }?>">
45
  <?php
lib/menu_blockedIPs.php CHANGED
@@ -3,8 +3,16 @@
3
  <?php require('menuHeader.php'); ?>
4
  <?php $helpLink="http://docs.wordfence.com/en/Blocked_IPs"; $helpLabel="Learn more about Blocked IPs"; $pageTitle = "Wordfence Blocked IPs"; include('pageTitle.php'); ?>
5
  <div class="wordfenceLive">
6
- <table border="0" cellpadding="0" cellspacing="0">
7
- <tr><td><h2>Wordfence Live Activity:</h2></td><td id="wfLiveStatus"></td></tr>
 
 
 
 
 
 
 
 
8
  </table>
9
  </div>
10
  <?php if(! wfConfig::get('firewallEnabled')){ ?><div style="color: #F00; font-weight: bold;">Rate limiting rules and advanced blocking are disabled. You can enable it on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options page</a> at the top.</div><?php } ?>
3
  <?php require('menuHeader.php'); ?>
4
  <?php $helpLink="http://docs.wordfence.com/en/Blocked_IPs"; $helpLabel="Learn more about Blocked IPs"; $pageTitle = "Wordfence Blocked IPs"; include('pageTitle.php'); ?>
5
  <div class="wordfenceLive">
6
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveActivity">
7
+ <tr>
8
+ <td><h2>Wordfence Live Activity:</h2></td>
9
+ <td id="wfLiveStatus"></td>
10
+ </tr>
11
+ </table>
12
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveStateMessage">
13
+ <tr>
14
+ <td>Live Updates Paused &mdash; Click inside window to resume</td>
15
+ </tr>
16
  </table>
17
  </div>
18
  <?php if(! wfConfig::get('firewallEnabled')){ ?><div style="color: #F00; font-weight: bold;">Rate limiting rules and advanced blocking are disabled. You can enable it on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options page</a> at the top.</div><?php } ?>
lib/menu_options.php CHANGED
@@ -9,12 +9,17 @@ $w = new wfConfig();
9
  $pageTitle = "Wordfence Options";
10
  include( 'pageTitle.php' ); ?>
11
  <div class="wordfenceLive">
12
- <table border="0" cellpadding="0" cellspacing="0">
13
  <tr>
14
  <td><h2>Wordfence Live Activity:</h2></td>
15
  <td id="wfLiveStatus"></td>
16
  </tr>
17
  </table>
 
 
 
 
 
18
  </div>
19
  <?php
20
  $rightRail = new wfView('marketing/rightrail', array('additionalClasses' => 'wordfenceRightRailOptions'));
@@ -297,11 +302,21 @@ $w = new wfConfig();
297
  <td><input type="checkbox" id="alertOn_adminLogin" class="wfConfigElem" name="alertOn_adminLogin"
298
  value="1" <?php $w->cb( 'alertOn_adminLogin' ); ?>/></td>
299
  </tr>
 
 
 
 
 
300
  <tr>
301
  <th>Alert me when a non-admin user signs in</th>
302
  <td><input type="checkbox" id="alertOn_nonAdminLogin" class="wfConfigElem"
303
  name="alertOn_nonAdminLogin" value="1" <?php $w->cb( 'alertOn_nonAdminLogin' ); ?>/></td>
304
  </tr>
 
 
 
 
 
305
  <tr>
306
  <th>Alert me when there's a large increase in attacks detected on my site</th>
307
  <td><input type="checkbox" id="wafAlertOnAttacks" class="wfConfigElem"
@@ -434,6 +449,15 @@ $w = new wfConfig();
434
  name="scansEnabled_checkReadableConfig" value="1" <?php $w->cb( 'scansEnabled_checkReadableConfig' ); ?> />
435
  </td>
436
  </tr>
 
 
 
 
 
 
 
 
 
437
  <!-- <tr>-->
438
  <!-- <th>Scan for Full Path Disclosure?<a-->
439
  <!-- href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_Full_Path_Disclosure"-->
9
  $pageTitle = "Wordfence Options";
10
  include( 'pageTitle.php' ); ?>
11
  <div class="wordfenceLive">
12
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveActivity">
13
  <tr>
14
  <td><h2>Wordfence Live Activity:</h2></td>
15
  <td id="wfLiveStatus"></td>
16
  </tr>
17
  </table>
18
+ <table border="0" cellpadding="0" cellspacing="0" class="wordfenceLiveStateMessage">
19
+ <tr>
20
+ <td>Live Updates Paused &mdash; Click inside window to resume</td>
21
+ </tr>
22
+ </table>
23
  </div>
24
  <?php
25
  $rightRail = new wfView('marketing/rightrail', array('additionalClasses' => 'wordfenceRightRailOptions'));
302
  <td><input type="checkbox" id="alertOn_adminLogin" class="wfConfigElem" name="alertOn_adminLogin"
303
  value="1" <?php $w->cb( 'alertOn_adminLogin' ); ?>/></td>
304
  </tr>
305
+ <tr>
306
+ <th style="color: #666666;padding-left: 20px;">Only alert me when that administrator signs in from a new device or location</th>
307
+ <td><input type="checkbox" id="alertOn_firstAdminLoginOnly" class="wfConfigElem" name="alertOn_firstAdminLoginOnly"
308
+ value="1" <?php $w->cb( 'alertOn_firstAdminLoginOnly' ); ?>/></td>
309
+ </tr>
310
  <tr>
311
  <th>Alert me when a non-admin user signs in</th>
312
  <td><input type="checkbox" id="alertOn_nonAdminLogin" class="wfConfigElem"
313
  name="alertOn_nonAdminLogin" value="1" <?php $w->cb( 'alertOn_nonAdminLogin' ); ?>/></td>
314
  </tr>
315
+ <tr>
316
+ <th style="color: #666666;padding-left: 20px;">Only alert me when that user signs in from a new device or location</th>
317
+ <td><input type="checkbox" id="alertOn_firstNonAdminLoginOnly" class="wfConfigElem" name="alertOn_firstNonAdminLoginOnly"
318
+ value="1" <?php $w->cb( 'alertOn_firstNonAdminLoginOnly' ); ?>/></td>
319
+ </tr>
320
  <tr>
321
  <th>Alert me when there's a large increase in attacks detected on my site</th>
322
  <td><input type="checkbox" id="wafAlertOnAttacks" class="wfConfigElem"
449
  name="scansEnabled_checkReadableConfig" value="1" <?php $w->cb( 'scansEnabled_checkReadableConfig' ); ?> />
450
  </td>
451
  </tr>
452
+ <tr>
453
+ <th>Scan for publicly accessible quarantined files<a
454
+ href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_publicly_accessible_quarantined_files"
455
+ target="_blank" class="wfhelp"></a></th>
456
+ <td><input type="checkbox" id="scansEnabled_suspectedFiles" class="wfConfigElem"
457
+ name="scansEnabled_suspectedFiles"
458
+ value="1" <?php $w->cb( 'scansEnabled_suspectedFiles' ); ?>/>
459
+ </td>
460
+ </tr>
461
  <!-- <tr>-->
462
  <!-- <th>Scan for Full Path Disclosure?<a-->
463
  <!-- href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_Full_Path_Disclosure"-->
lib/menu_scan.php CHANGED
@@ -2,6 +2,10 @@
2
  $sigUpdateTime = wfConfig::get('signatureUpdateTime');
3
  ?>
4
  <div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
 
 
 
 
5
  <div class="wrap wordfence">
6
 
7
  <?php
@@ -256,6 +260,55 @@ $sigUpdateTime = wfConfig::get('signatureUpdateTime');
256
  </div>
257
  </div>
258
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  <script type="text/x-jquery-template" id="issueTmpl_wpscan_fullPathDiscl">
260
  <div>
261
  <div class="wfIssue">
2
  $sigUpdateTime = wfConfig::get('signatureUpdateTime');
3
  ?>
4
  <div class="wordfenceModeElem" id="wordfenceMode_scan"></div>
5
+ <div id="wfLiveTrafficOverlayAnchor"></div>
6
+ <div id="wfLiveTrafficDisabledMessage">
7
+ <h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
8
+ </div>
9
  <div class="wrap wordfence">
10
 
11
  <?php
260
  </div>
261
  </div>
262
  </script>
263
+ <script type="text/x-jquery-template" id="issueTmpl_publiclyAccessible">
264
+ <div>
265
+ <div class="wfIssue">
266
+ <h2>${shortMsg}</h2>
267
+ <table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
268
+ <tr>
269
+ <th>URL:</th>
270
+ <td><a href="${data.url}" target="_blank">${data.url}</a></td>
271
+ <tr>
272
+ <th>Severity:</th>
273
+ <td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td>
274
+ </tr>
275
+ <tr>
276
+ <th>Status</th>
277
+ <td>
278
+ {{if status == 'new' }}New{{/if}}
279
+ {{if status == 'ignoreP' || status == 'ignoreC' }}Ignored{{/if}}
280
+ </td>
281
+ </tr>
282
+ </table>
283
+ <p>
284
+ {{html longMsg}}
285
+ </p>
286
+ <div class="wfIssueOptions">
287
+ <strong>Tools:</strong>
288
+ {{if data.fileExists}}
289
+ <a target="_blank" href="${WFAD.makeViewFileLink(data.file)}">View the file</a>
290
+ {{/if}}
291
+ <a href="#" onclick="WFAD.hideFile('${id}', 'delete'); return false;">Hide this file in <em>.htaccess</em></a>
292
+ {{if data.canDelete}}
293
+ <a href="#" onclick="WFAD.deleteFile('${id}'); return false;">Delete this file (can't be undone).</a>
294
+ <p>
295
+ <label><input type="checkbox" class="wfdelCheckbox" value="${id}" />&nbsp;Select for bulk delete</label>
296
+ </p>
297
+ {{/if}}
298
+ </div>
299
+ <div class="wfIssueOptions">
300
+ {{if status == 'new'}}
301
+ <strong>Resolve:</strong>
302
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
303
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore this issue</a>
304
+ {{/if}}
305
+ {{if status == 'ignoreC' || status == 'ignoreP'}}
306
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
307
+ {{/if}}
308
+ </div>
309
+ </div>
310
+ </div>
311
+ </script>
312
  <script type="text/x-jquery-template" id="issueTmpl_wpscan_fullPathDiscl">
313
  <div>
314
  <div class="wfIssue">
lib/menu_sitePerf.php CHANGED
@@ -10,6 +10,11 @@ $w = new wfConfig();
10
  echo $rightRail;
11
  ?>
12
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px; max-width: 800px;">
 
 
 
 
 
13
  <h2>Caching</h2>
14
  <table border="0">
15
  <tr><td>Disable all performance enhancements:</td><td><input type="radio" name="cacheType" id="cacheType_disable" value="disable" <?php if(! wfConfig::get('cacheType')){ echo 'checked="checked"'; } ?> /></td><td>No performance improvement</td></tr>
10
  echo $rightRail;
11
  ?>
12
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px; max-width: 800px;">
13
+ <?php if (wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') { ?>
14
+ <div id="wordfenceFalconDeprecationWarning" class="wf-notice"><p><strong>Support for the Falcon and Basic cache will be removed.</strong> This site currently has the <?php echo (wfConfig::get('cacheType') == 'php' ? 'Basic' : 'Falcon'); ?> cache enabled, and it is scheduled to be removed in an upcoming release. Please investigate other caching options and then manually disable it below. It will be disabled automatically when support is removed. <a href="http://docs.wordfence.com/en/Falcon_Cache" target="_blank">More information.</a></p></div>
15
+ <?php } else { ?>
16
+ <div id="wordfenceFalconDeprecationWarning" class="wf-notice"><p><strong>Support for the Falcon and Basic cache will be removed.</strong> It is scheduled to be removed in an upcoming release and should not be enabled. If enabled, it will be disabled automatically when support is removed. <a href="http://docs.wordfence.com/en/Falcon_Cache" target="_blank">More information.</a></p></div>
17
+ <?php } ?>
18
  <h2>Caching</h2>
19
  <table border="0">
20
  <tr><td>Disable all performance enhancements:</td><td><input type="radio" name="cacheType" id="cacheType_disable" value="disable" <?php if(! wfConfig::get('cacheType')){ echo 'checked="checked"'; } ?> /></td><td>No performance improvement</td></tr>
lib/menu_waf.php CHANGED
@@ -51,6 +51,7 @@ $wafRemoveURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeA
51
  <?php echo wp_kses($storageExceptionMessage, 'post') ?>
52
  </div>
53
  <?php elseif (!empty($wafActionContent)): ?>
 
54
  <?php echo $wafActionContent ?>
55
 
56
  <?php if (!empty($_REQUEST['wafAction']) && $_REQUEST['wafAction'] == 'removeAutoPrepend') { ?>
@@ -66,11 +67,11 @@ $wafRemoveURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeA
66
  <a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for
67
  help</a>.</em></p>
68
  <?php } ?>
69
-
70
  <?php else: ?>
71
 
72
  <?php if (!empty($configExceptionMessage)): ?>
73
- <div style="font-weight: bold; margin: 20px 0px;;">
74
  <?php echo wp_kses($configExceptionMessage, 'post') ?>
75
  </div>
76
  <?php endif ?>
51
  <?php echo wp_kses($storageExceptionMessage, 'post') ?>
52
  </div>
53
  <?php elseif (!empty($wafActionContent)): ?>
54
+ <div style="max-width: 900px;">
55
  <?php echo $wafActionContent ?>
56
 
57
  <?php if (!empty($_REQUEST['wafAction']) && $_REQUEST['wafAction'] == 'removeAutoPrepend') { ?>
67
  <a target="_blank" href="https://docs.wordfence.com/en/Web_Application_Firewall_Setup">click here for
68
  help</a>.</em></p>
69
  <?php } ?>
70
+ </div>
71
  <?php else: ?>
72
 
73
  <?php if (!empty($configExceptionMessage)): ?>
74
+ <div style="font-weight: bold; margin: 20px 0px; max-width: 700px;">
75
  <?php echo wp_kses($configExceptionMessage, 'post') ?>
76
  </div>
77
  <?php endif ?>
lib/wfActivityReport.php CHANGED
@@ -256,13 +256,14 @@ SQL
256
  }
257
 
258
  $results = $this->db->get_results($this->db->prepare(<<<SQL
259
- SELECT *,
260
- sum(fail) as fail_count,
261
- max(userID) as is_valid_user
262
- FROM {$this->db->base_prefix}wfLogins
263
- WHERE fail = 1
264
- AND ctime > $interval
265
- GROUP BY username
 
266
  ORDER BY fail_count DESC
267
  LIMIT %d
268
  SQL
256
  }
257
 
258
  $results = $this->db->get_results($this->db->prepare(<<<SQL
259
+ SELECT wfl.*,
260
+ sum(wfl.fail) as fail_count,
261
+ !ISNULL(wpu.ID) as is_valid_user
262
+ FROM {$this->db->base_prefix}wfLogins wfl
263
+ LEFT JOIN {$this->db->base_prefix}users wpu ON wfl.username = wpu.user_login OR wfl.username = wpu.user_email
264
+ WHERE wfl.fail = 1
265
+ AND wfl.ctime > $interval
266
+ GROUP BY wfl.username
267
  ORDER BY fail_count DESC
268
  LIMIT %d
269
  SQL
lib/wfCache.php CHANGED
@@ -719,10 +719,7 @@ EOT;
719
 
720
  $homePath = get_home_path();
721
  $htaccessFile = $homePath.'.htaccess';
722
- if (file_exists($htaccessFile)) {
723
- return $htaccessFile;
724
- }
725
- return false;
726
  }
727
  public static function doNotCache(){
728
  if(! defined('WFDONOTCACHE')){
719
 
720
  $homePath = get_home_path();
721
  $htaccessFile = $homePath.'.htaccess';
722
+ return $htaccessFile;
 
 
 
723
  }
724
  public static function doNotCache(){
725
  if(! defined('WFDONOTCACHE')){
lib/wfConfig.php CHANGED
@@ -22,7 +22,9 @@ class wfConfig {
22
  "alertOn_loginLockout" => array('value' => true, 'autoload' => self::AUTOLOAD),
23
  "alertOn_lostPasswdForm" => array('value' => true, 'autoload' => self::AUTOLOAD),
24
  "alertOn_adminLogin" => array('value' => true, 'autoload' => self::AUTOLOAD),
 
25
  "alertOn_nonAdminLogin" => array('value' => false, 'autoload' => self::AUTOLOAD),
 
26
  "liveTrafficEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
27
  "scansEnabled_checkReadableConfig" => array('value' => true, 'autoload' => self::AUTOLOAD),
28
  "advancedCommentScanning" => array('value' => false, 'autoload' => self::AUTOLOAD),
@@ -40,6 +42,7 @@ class wfConfig {
40
  "scansEnabled_coreUnknown" => array('value' => true, 'autoload' => self::AUTOLOAD),
41
  "scansEnabled_malware" => array('value' => true, 'autoload' => self::AUTOLOAD),
42
  "scansEnabled_fileContents" => array('value' => true, 'autoload' => self::AUTOLOAD),
 
43
  "scansEnabled_posts" => array('value' => true, 'autoload' => self::AUTOLOAD),
44
  "scansEnabled_comments" => array('value' => true, 'autoload' => self::AUTOLOAD),
45
  "scansEnabled_passwds" => array('value' => true, 'autoload' => self::AUTOLOAD),
@@ -61,7 +64,7 @@ class wfConfig {
61
  "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD),
62
  "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD),
63
  "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD),
64
- "other_hideWPVersion" => array('value' => true, 'autoload' => self::AUTOLOAD),
65
  "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
66
  "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD),
67
  "other_scanComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
22
  "alertOn_loginLockout" => array('value' => true, 'autoload' => self::AUTOLOAD),
23
  "alertOn_lostPasswdForm" => array('value' => true, 'autoload' => self::AUTOLOAD),
24
  "alertOn_adminLogin" => array('value' => true, 'autoload' => self::AUTOLOAD),
25
+ "alertOn_firstAdminLoginOnly" => array('value' => false, 'autoload' => self::AUTOLOAD),
26
  "alertOn_nonAdminLogin" => array('value' => false, 'autoload' => self::AUTOLOAD),
27
+ "alertOn_firstNonAdminLoginOnly" => array('value' => false, 'autoload' => self::AUTOLOAD),
28
  "liveTrafficEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
29
  "scansEnabled_checkReadableConfig" => array('value' => true, 'autoload' => self::AUTOLOAD),
30
  "advancedCommentScanning" => array('value' => false, 'autoload' => self::AUTOLOAD),
42
  "scansEnabled_coreUnknown" => array('value' => true, 'autoload' => self::AUTOLOAD),
43
  "scansEnabled_malware" => array('value' => true, 'autoload' => self::AUTOLOAD),
44
  "scansEnabled_fileContents" => array('value' => true, 'autoload' => self::AUTOLOAD),
45
+ "scansEnabled_suspectedFiles" => array('value' => true, 'autoload' => self::AUTOLOAD),
46
  "scansEnabled_posts" => array('value' => true, 'autoload' => self::AUTOLOAD),
47
  "scansEnabled_comments" => array('value' => true, 'autoload' => self::AUTOLOAD),
48
  "scansEnabled_passwds" => array('value' => true, 'autoload' => self::AUTOLOAD),
64
  "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD),
65
  "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD),
66
  "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD),
67
+ "other_hideWPVersion" => array('value' => false, 'autoload' => self::AUTOLOAD),
68
  "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
69
  "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD),
70
  "other_scanComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
lib/wfLog.php CHANGED
@@ -153,6 +153,15 @@ class wfLog {
153
  return;
154
  }
155
  }
 
 
 
 
 
 
 
 
 
156
  // change the action flag here if the user does not exist.
157
  if ($action == 'loginFailValidUsername' && $userID == 0) {
158
  $action = 'loginFailInvalidUsername';
@@ -1246,9 +1255,11 @@ class wfUserIPRange {
1246
 
1247
  // IPv6 range
1248
  } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
1249
- if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
1250
- $IPparts = explode(':', strtolower(wfUtils::expandIPv6Address($ip)));
1251
- $whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
 
 
1252
  $mismatch = false;
1253
  for ($i = 0; $i <= 7; $i++) {
1254
  if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
@@ -1416,7 +1427,7 @@ class wfUserIPRange {
1416
  * @param string|null $ip_string
1417
  */
1418
  public function setIPString($ip_string) {
1419
- $this->ip_string = $ip_string;
1420
  }
1421
  }
1422
 
153
  return;
154
  }
155
  }
156
+ else {
157
+ $user = get_user_by('email', $username);
158
+ if ($user) {
159
+ $userID = $user->ID;
160
+ if (!$userID) {
161
+ return;
162
+ }
163
+ }
164
+ }
165
  // change the action flag here if the user does not exist.
166
  if ($action == 'loginFailValidUsername' && $userID == 0) {
167
  $action = 'loginFailInvalidUsername';
1255
 
1256
  // IPv6 range
1257
  } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
1258
+ $ip = strtolower(wfUtils::expandIPv6Address($ip));
1259
+ $ip_string = strtolower(self::expandIPv6Range($ip_string));
1260
+ if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) {
1261
+ $IPparts = explode(':', $ip);
1262
+ $whiteParts = explode(':', $ip_string);
1263
  $mismatch = false;
1264
  for ($i = 0; $i <= 7; $i++) {
1265
  if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
1427
  * @param string|null $ip_string
1428
  */
1429
  public function setIPString($ip_string) {
1430
+ $this->ip_string = preg_replace('/[\x{2013}-\x{2015}]/u', '-', $ip_string); //Replace em-dash, en-dash, and horizontal bar with a regular dash
1431
  }
1432
  }
1433
 
lib/wfMD5BloomFilter.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright (c) 2012, Da Xue
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+ 3. The name of the author nor the names of its contributors may be used
14
+ to endorse or promote products derived from this software without
15
+ specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY DA XUE ''AS IS'' AND ANY
18
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL DA XUE BE LIABLE FOR ANY
21
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ /* https://github.com/dsx724/php-bloom-filter */
30
+
31
+ // Modified for PHP 5.2 compatibility and to support serialization.
32
+
33
+ class wfMD5BloomFilter {
34
+ private static function merge($bf1,$bf2,$bfout,$union = false){
35
+ if ($bf1->m != $bf2->m) throw new Exception('Unable to merge due to vector difference.');
36
+ if ($bf1->k != $bf2->k) throw new Exception('Unable to merge due to hash count difference.');
37
+ $length = strlen($bfout->bit_array);
38
+ if ($union){
39
+ $bfout->bit_array = $bf1->bit_array | $bf2->bit_array;
40
+ $bfout->n = $bf1->n + $bf2->n;
41
+ } else {
42
+ $bfout->bit_array = $bf1->bit_array & $bf2->bit_array;
43
+ $bfout->n = abs($bf1->n - $bf2->n);
44
+ }
45
+ }
46
+ public static function createFromProbability($n, $p){
47
+ if ($p <= 0 || $p >= 1) throw new Exception('Invalid false positive rate requested.');
48
+ if ($n <= 0) throw new Exception('Invalid capacity requested.');
49
+ $k = floor(log(1/$p,2));
50
+ $m = pow(2,ceil(log(-$n*log($p)/pow(log(2),2),2))); //approximate estimator method
51
+ return new self($m,$k);
52
+ }
53
+ public static function getUnion($bf1,$bf2){
54
+ $bf = new self($bf1->m,$bf1->k,$bf1->hash);
55
+ self::merge($bf1,$bf2,$bf,true);
56
+ return $bf;
57
+ }
58
+ public static function getIntersection($bf1,$bf2){
59
+ $bf = new self($bf1->m,$bf1->k,$bf1->hash);
60
+ self::merge($bf1,$bf2,$bf,false);
61
+ return $bf;
62
+ }
63
+ private $n = 0; // # of entries
64
+ private $m; // # of bits in array
65
+ private $k; // # of hash functions
66
+ private $k2;
67
+ private $mask;
68
+ private $bit_array; // data structure
69
+ public function __construct($m, $k){
70
+ if ($m < 8) throw new Exception('The bit array length must be at least 8 bits.');
71
+ if (($m & ($m - 1)) !== 0) throw new Exception('The bit array length must be power of 2.');
72
+ if ($m > 65536) throw new Exception('The maximum data structure size is 8KB.');
73
+ if ($k > 8) throw new Exception('The maximum bits to set is 8.');
74
+ $this->m = $m;
75
+ $this->k = $k;
76
+ $this->k2 = $k * 2;
77
+ $address_bits = (int)log($m,2);
78
+ $this->mask = (1 << $address_bits) - 8;
79
+ $this->bit_array = (binary)(str_repeat("\0",$this->getArraySize(true)));
80
+ }
81
+ public function __sleep() {
82
+ return array('n', 'm', 'k', 'k2', 'mask', 'bit_array');
83
+ }
84
+ public function calculateProbability($n = 0){
85
+ return pow(1-pow(1-1/$this->m,$this->k*($n ? $n : $this->n)),$this->k);
86
+ }
87
+ public function calculateCapacity($p){
88
+ return floor($this->m*log(2)/log($p,1-pow(1-1/$this->m,$this->m*log(2))));
89
+ }
90
+ public function getElementCount(){
91
+ return $this->n;
92
+ }
93
+ public function getArraySize($bytes = false){
94
+ return $this->m >> ($bytes ? 3 : 0);
95
+ }
96
+ public function getHashCount(){
97
+ return $this->k;
98
+ }
99
+ public function getInfo($p = null){
100
+ $units = array('','K','M','G','T','P','E','Z','Y');
101
+ $M = $this->getArraySize(true);
102
+ $magnitude = intval(floor(log($M,1024)));
103
+ $unit = $units[$magnitude];
104
+ $M /= pow(1024,$magnitude);
105
+ return 'Allocated '.$this->getArraySize().' bits ('.$M.' '.$unit.'Bytes)'.PHP_EOL.
106
+ 'Using '.$this->getHashCount(). ' (16b) hashes'.PHP_EOL.
107
+ 'Contains '.$this->getElementCount().' elements'.PHP_EOL.
108
+ (isset($p) ? 'Capacity of '.number_format($this->calculateCapacity($p)).' (p='.$p.')'.PHP_EOL : '');
109
+ }
110
+ public function add($key){
111
+ $hash = md5($key,true);
112
+ for ($index = 0; $index < $this->k2; $index++){
113
+ $hash_sub = (ord($hash[$index++]) << 8) | ord($hash[$index]);
114
+ $word = ($hash_sub & $this->mask) >> 3;
115
+ $this->bit_array[$word] = $this->bit_array[$word] | chr(1 << ($hash_sub & 7));
116
+ }
117
+ $this->n++;
118
+ }
119
+ public function contains($key){
120
+ $hash = md5($key,true);
121
+ for ($index = 0; $index < $this->k2; $index++){
122
+ $hash_sub = (ord($hash[$index++]) << 8) | ord($hash[$index]);
123
+ if ((ord($this->bit_array[($hash_sub & $this->mask) >> 3]) & (1 << ($hash_sub & 7))) === 0) return false;
124
+ }
125
+ return true;
126
+ }
127
+ public function unionWith($bf){
128
+ self::merge($this,$bf,$this,true);
129
+ }
130
+ public function intersectWith($bf){
131
+ self::merge($this,$bf,$this,false);
132
+ }
133
+ }
134
+ ?>
lib/wfScanEngine.php CHANGED
@@ -36,7 +36,7 @@ class wfScanEngine {
36
  );
37
  private $userPasswdQueue = "";
38
  private $passwdHasIssues = false;
39
-
40
 
41
  /**
42
  * @var wordfenceDBScanner
@@ -73,7 +73,7 @@ class wfScanEngine {
73
  }
74
 
75
  public function __sleep(){ //Same order here as above for properties that are included in serialization
76
- return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', 'dbScanner', 'knownFilesLoader', 'metrics');
77
  }
78
  public function __construct(){
79
  $this->startTime = time();
@@ -94,7 +94,7 @@ class wfScanEngine {
94
  $this->jobList[] = 'knownFiles_init';
95
  $this->jobList[] = 'knownFiles_main';
96
  $this->jobList[] = 'knownFiles_finish';
97
- foreach (array('knownFiles', 'checkReadableConfig', 'fileContents',
98
  // 'wpscan_fullPathDisclosure', 'wpscan_directoryListingEnabled',
99
  'posts', 'comments', 'passwds', 'dns', 'diskSpace', 'oldVersions', 'suspiciousAdminUsers') as $scanType) {
100
  if (wfConfig::get('scansEnabled_' . $scanType)) {
@@ -493,6 +493,7 @@ class wfScanEngine {
493
  $this->i->updateSummaryItem('totalData', wfUtils::formatBytes($this->hasher->totalData));
494
  $this->i->updateSummaryItem('totalFiles', $this->hasher->totalFiles);
495
  $this->i->updateSummaryItem('totalDirs', $this->hasher->totalDirs);
 
496
  $this->hasher = false;
497
  }
498
  private function scan_knownFiles_finish(){
@@ -529,6 +530,39 @@ class wfScanEngine {
529
  wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
530
  }
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
  private function scan_posts_init(){
534
  $this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URLs in Google\'s Safe Browsing List');
@@ -1488,7 +1522,7 @@ class wfCommonBackupFileTest {
1488
  * @return wfCommonBackupFileTest
1489
  */
1490
  public static function createFromRootPath($path) {
1491
- return new self(home_url($path), ABSPATH . $path);
1492
  }
1493
 
1494
  private $url;
@@ -1584,3 +1618,7 @@ class wfCommonBackupFileTest {
1584
  return $this->response;
1585
  }
1586
  }
 
 
 
 
36
  );
37
  private $userPasswdQueue = "";
38
  private $passwdHasIssues = false;
39
+ private $suspectedFiles = false; //Files found with the ".suspected" extension
40
 
41
  /**
42
  * @var wordfenceDBScanner
73
  }
74
 
75
  public function __sleep(){ //Same order here as above for properties that are included in serialization
76
+ return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', 'suspectedFiles', 'dbScanner', 'knownFilesLoader', 'metrics');
77
  }
78
  public function __construct(){
79
  $this->startTime = time();
94
  $this->jobList[] = 'knownFiles_init';
95
  $this->jobList[] = 'knownFiles_main';
96
  $this->jobList[] = 'knownFiles_finish';
97
+ foreach (array('knownFiles', 'checkReadableConfig', 'fileContents', 'suspectedFiles',
98
  // 'wpscan_fullPathDisclosure', 'wpscan_directoryListingEnabled',
99
  'posts', 'comments', 'passwds', 'dns', 'diskSpace', 'oldVersions', 'suspiciousAdminUsers') as $scanType) {
100
  if (wfConfig::get('scansEnabled_' . $scanType)) {
493
  $this->i->updateSummaryItem('totalData', wfUtils::formatBytes($this->hasher->totalData));
494
  $this->i->updateSummaryItem('totalFiles', $this->hasher->totalFiles);
495
  $this->i->updateSummaryItem('totalDirs', $this->hasher->totalDirs);
496
+ $this->suspectedFiles = $this->hasher->getSuspectedFiles();
497
  $this->hasher = false;
498
  }
499
  private function scan_knownFiles_finish(){
530
  wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
531
  }
532
 
533
+ private function scan_suspectedFiles() {
534
+ $haveIssues = false;
535
+ $status = wordfence::statusStart("Scanning for publicly accessible quarantined files");
536
+
537
+ if (is_array($this->suspectedFiles) && count($this->suspectedFiles) > 0) {
538
+ foreach ($this->suspectedFiles as $file) {
539
+ wordfence::status(4, 'info', "Testing accessibility of: $file");
540
+ $test = wfPubliclyAccessibleFileTest::createFromRootPath($file);
541
+ if ($test->fileExists() && $test->isPubliclyAccessible()) {
542
+ $key = "publiclyAccessible" . bin2hex($test->getUrl());
543
+ if ($this->addIssue(
544
+ 'publiclyAccessible',
545
+ 2,
546
+ $key,
547
+ $key,
548
+ 'Publicly accessible quarantined file found: ' . esc_html($file),
549
+ '<a href="' . $test->getUrl() . '" target="_blank">' . $test->getUrl() . '</a> is publicly
550
+ accessible and may expose source code or sensitive information about your site. Files such as this one are commonly
551
+ checked for by scanners and should be removed or made inaccessible.',
552
+ array(
553
+ 'url' => $test->getUrl(),
554
+ 'file' => $file,
555
+ 'canDelete' => true,
556
+ )
557
+ )) {
558
+ $haveIssues = true;
559
+ }
560
+ }
561
+ }
562
+ }
563
+
564
+ wordfence::statusEnd($status, $haveIssues);
565
+ }
566
 
567
  private function scan_posts_init(){
568
  $this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URLs in Google\'s Safe Browsing List');
1522
  * @return wfCommonBackupFileTest
1523
  */
1524
  public static function createFromRootPath($path) {
1525
+ return new self(site_url($path), ABSPATH . $path);
1526
  }
1527
 
1528
  private $url;
1618
  return $this->response;
1619
  }
1620
  }
1621
+
1622
+ class wfPubliclyAccessibleFileTest extends wfCommonBackupFileTest {
1623
+
1624
+ }
lib/wfUtils.php CHANGED
@@ -960,7 +960,7 @@ class wfUtils {
960
  if (!$host) {
961
  // This function works for IPv4 or IPv6
962
  if (function_exists('gethostbyaddr')) {
963
- $host = gethostbyaddr($IP);
964
  }
965
  if (!$host) {
966
  $ptr = false;
@@ -1277,7 +1277,7 @@ class wfUtils {
1277
 
1278
  public static function htaccessAppend($code)
1279
  {
1280
- $htaccess = ABSPATH . '/.htaccess';
1281
  $content = self::htaccess();
1282
  if (wfUtils::isNginx() || !is_writable($htaccess)) {
1283
  return false;
@@ -1290,10 +1290,27 @@ class wfUtils {
1290
 
1291
  return true;
1292
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1293
 
1294
  public static function htaccess() {
1295
- if (is_readable(ABSPATH . '/.htaccess') && !wfUtils::isNginx()) {
1296
- return file_get_contents(ABSPATH . '/.htaccess');
 
1297
  }
1298
  return "";
1299
  }
960
  if (!$host) {
961
  // This function works for IPv4 or IPv6
962
  if (function_exists('gethostbyaddr')) {
963
+ $host = @gethostbyaddr($IP);
964
  }
965
  if (!$host) {
966
  $ptr = false;
1277
 
1278
  public static function htaccessAppend($code)
1279
  {
1280
+ $htaccess = wfCache::getHtaccessPath();
1281
  $content = self::htaccess();
1282
  if (wfUtils::isNginx() || !is_writable($htaccess)) {
1283
  return false;
1290
 
1291
  return true;
1292
  }
1293
+
1294
+ public static function htaccessPrepend($code)
1295
+ {
1296
+ $htaccess = wfCache::getHtaccessPath();
1297
+ $content = self::htaccess();
1298
+ if (wfUtils::isNginx() || !is_writable($htaccess)) {
1299
+ return false;
1300
+ }
1301
+
1302
+ if (strpos($content, $code) === false) {
1303
+ // make sure we write this once
1304
+ file_put_contents($htaccess, trim($code) . "\n" . $content, LOCK_EX);
1305
+ }
1306
+
1307
+ return true;
1308
+ }
1309
 
1310
  public static function htaccess() {
1311
+ $htaccess = wfCache::getHtaccessPath();
1312
+ if (is_readable($htaccess) && !wfUtils::isNginx()) {
1313
+ return file_get_contents($htaccess);
1314
  }
1315
  return "";
1316
  }
lib/wordfenceClass.php CHANGED
@@ -13,6 +13,7 @@ require_once('wfConfig.php');
13
  require_once('wfSchema.php');
14
  require_once('wfCache.php');
15
  require_once('wfCrypt.php');
 
16
  require_once 'wfView.php';
17
  require_once 'wfHelperString.php';
18
  require_once 'wfDirectoryIterator.php';
@@ -544,6 +545,13 @@ SQL
544
 
545
  //6.2.0
546
  wfConfig::migrateCodeExecutionForUploadsPHP7();
 
 
 
 
 
 
 
547
 
548
  //Must be the final line
549
  }
@@ -585,6 +593,9 @@ SQL
585
  }
586
 
587
  self::initProtection();
 
 
 
588
 
589
  //These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed.
590
  wfCache::setupCaching();
@@ -737,6 +748,8 @@ SQL
737
 
738
  add_action('wordfence_batchReportBlockedAttempts', 'wordfence::wfsnBatchReportBlockedAttempts');
739
  add_action('wordfence_batchReportFailedAttempts', 'wordfence::wfsnBatchReportFailedAttempts');
 
 
740
 
741
  if (wfConfig::get('other_hideWPVersion')) {
742
  add_filter('update_feedback', 'wordfence::restoreReadmeForUpgrade');
@@ -745,10 +758,37 @@ SQL
745
  }
746
  public static function _pluginPageActionLinks($links) {
747
  if (!wfConfig::get('isPaid')) {
748
- $links = array_merge(array('aWordfencePluginCallout' => '<a href="https://www.wordfence.com/zz12/wordfence-signup/" target="_blank"><strong style="color: #FCB214; display: inline;">Upgrade To Premium</strong></a>'), $links);
749
- }
750
  return $links;
751
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  /*
753
  public static function cronAddSchedules($schedules){
754
  $schedules['wfEachMinute'] = array(
@@ -1201,16 +1241,37 @@ SQL
1201
  'IP' => wfUtils::getIP()
1202
  ));
1203
  }
1204
-
 
 
 
1205
  if(user_can($userID, 'update_core')){
1206
  if(wfConfig::get('alertOn_adminLogin')){
1207
- wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
 
 
 
 
 
 
 
1208
  }
1209
  } else {
1210
  if(wfConfig::get('alertOn_nonAdminLogin')){
1211
- wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.", wfUtils::getIP());
 
 
 
 
 
 
 
1212
  }
1213
  }
 
 
 
 
1214
  }
1215
  public static function registrationFilter($errors, $santizedLogin, $userEmail){
1216
  if(wfConfig::get('loginSec_blockAdminReg') && $santizedLogin == 'admin'){
@@ -2429,6 +2490,10 @@ SQL
2429
  wfConfig::set('suPHPWAFUpdateChoice', '1');
2430
  return array('ok' => 1);
2431
  }
 
 
 
 
2432
  public static function ajax_removeFromCache_callback(){
2433
  $id = $_POST['id'];
2434
  $link = get_permalink($id);
@@ -2916,24 +2981,54 @@ SQL
2916
  if (!$issue) {
2917
  return array('cerrorMsg' => "We could not find that issue in our database.");
2918
  }
2919
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2920
  $file = $issue['data']['file'];
2921
- $localFile = ABSPATH . '/' . $file;
2922
  $localFile = realpath($localFile);
2923
- if (strpos($localFile, ABSPATH) !== 0) {
2924
- return array('cerrorMsg' => "An invalid file was requested for deletion.");
2925
  }
2926
- $localFile = substr($localFile, strlen(ABSPATH));
2927
-
2928
- if (!wfUtils::htaccessAppend("<Files \"{$localFile}\">
2929
- <IfModule mod_authz_core.c>
2930
- Require all denied
 
 
 
 
 
2931
  </IfModule>
2932
- <IfModule !mod_authz_core.c>
2933
- Order deny,allow
2934
- Deny from all
 
 
 
 
 
 
 
2935
  </IfModule>
2936
- </Files>")) {
 
 
2937
  return array('cerrorMsg' => "You don't have permission to repair .htaccess. 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.");
2938
  }
2939
  $issues->updateIssue($_POST['issueID'], 'delete');
@@ -4159,7 +4254,7 @@ HTML;
4159
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4160
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4161
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess',
4162
- 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'saveCacheOptions', 'clearPageCache',
4163
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4164
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
4165
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
@@ -4318,6 +4413,12 @@ HTML;
4318
  echo '<div id="wordfenceAdminEmailWarning" class="fade error"><p><strong>You have not set an administrator email address to receive alerts for Wordfence.</strong> Please <a href="' . self::getMyOptionsURL() . '">click here to go to the Wordfence Options Page</a> and set an email address where you will receive security alerts from this site.</p><p><a class="button button-small" href="#" onclick="wordfenceExt.adminEmailChoice(\'mine\'); return false;"">Use My Email Address</a>
4319
  <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.adminEmailChoice(\'no\'); return false;">Dismiss</a></p></div>';
4320
  }
 
 
 
 
 
 
4321
  public static function autoUpdateNotice(){
4322
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
4323
  }
@@ -4340,6 +4441,16 @@ HTML;
4340
  }
4341
  $warningAdded = true;
4342
  }
 
 
 
 
 
 
 
 
 
 
4343
  if(! $warningAdded){
4344
  if(wfConfig::get('tourClosed') == '1' && (! wfConfig::get('autoUpdate')) && (! wfConfig::get('autoUpdateChoice'))){
4345
  $warningAdded = true;
@@ -4384,7 +4495,9 @@ HTML;
4384
  add_submenu_page("Wordfence", "Firewall", "Firewall", "activate_plugins", "WordfenceWAF", 'wordfence::menu_waf');
4385
  add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
4386
  /* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
4387
- add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
 
 
4388
  add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
4389
  add_submenu_page('Wordfence', 'Password Audit', 'Password Audit', 'activate_plugins', 'WordfencePasswdAudit', 'wordfence::menu_passwd');
4390
 
@@ -5661,7 +5774,12 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
5661
  ) {
5662
  status_header(404);
5663
  nocache_headers();
5664
- include(get_404_template());
 
 
 
 
 
5665
  exit;
5666
  }
5667
  return $query_vars;
13
  require_once('wfSchema.php');
14
  require_once('wfCache.php');
15
  require_once('wfCrypt.php');
16
+ require_once('wfMD5BloomFilter.php');
17
  require_once 'wfView.php';
18
  require_once 'wfHelperString.php';
19
  require_once 'wfDirectoryIterator.php';
545
 
546
  //6.2.0
547
  wfConfig::migrateCodeExecutionForUploadsPHP7();
548
+
549
+ //6.2.1
550
+ if ((wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') && !wfConfig::get('wf621HadFalconEnabled')) {
551
+ wfConfig::set('wf621HadFalconEnabled', true);
552
+
553
+ wp_schedule_single_event(time(), 'wordfence_sendFalconDeprecationNotice');
554
+ }
555
 
556
  //Must be the final line
557
  }
593
  }
594
 
595
  self::initProtection();
596
+
597
+ //Fix wp_mail bug when $_SERVER['SERVER_NAME'] is undefined
598
+ add_filter('wp_mail_from', 'wordfence::fixWPMailFromAddress');
599
 
600
  //These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed.
601
  wfCache::setupCaching();
748
 
749
  add_action('wordfence_batchReportBlockedAttempts', 'wordfence::wfsnBatchReportBlockedAttempts');
750
  add_action('wordfence_batchReportFailedAttempts', 'wordfence::wfsnBatchReportFailedAttempts');
751
+
752
+ add_action('wordfence_sendFalconDeprecationNotice', 'wordfence::sendFalconDeprecationNotice');
753
 
754
  if (wfConfig::get('other_hideWPVersion')) {
755
  add_filter('update_feedback', 'wordfence::restoreReadmeForUpgrade');
758
  }
759
  public static function _pluginPageActionLinks($links) {
760
  if (!wfConfig::get('isPaid')) {
761
+ $links = array_merge(array('aWordfencePluginCallout' => '<a href="https://www.wordfence.com/zz12/wordfence-signup/" target="_blank"><strong style="color: #11967A; display: inline;">Upgrade To Premium</strong></a>'), $links);
762
+ }
763
  return $links;
764
  }
765
+ public static function sendFalconDeprecationNotice() {
766
+ $url = network_admin_url('admin.php?page=WordfenceSitePerf');
767
+ $cacheName = (wfConfig::get('cacheType') == 'php' ? 'Basic' : 'Falcon');
768
+ wordfence::alert("Support for the Falcon and Basic cache will be removed", "This site currently has the {$cacheName} cache enabled, and it is scheduled to be removed in an upcoming release. Please investigate other caching options and then visit the cache settings page to manually disable the {$cacheName} cache. It will be disabled automatically when support is removed.\n\nCache Settings Page: {$url}\n", wfUtils::getIP());
769
+ }
770
+ public static function fixWPMailFromAddress($from_email) {
771
+ if ($from_email == 'wordpress@') { //$_SERVER['SERVER_NAME'] is undefined so we get an incomplete email address
772
+ wordfence::status(4, 'info', "wp_mail from address is incomplete, attempting to fix");
773
+ $urls = array(get_site_url(), get_home_url());
774
+ foreach ($urls as $u) {
775
+ if (!empty($u)) {
776
+ $u = preg_replace('#^[^/]*//+([^/]+).*$#', '\1', $u);
777
+ if (substr($u, 0, 4) == 'www.') {
778
+ $u = substr($u, 4);
779
+ }
780
+
781
+ if (!empty($u)) {
782
+ wordfence::status(4, 'info', "Fixing wp_mail from address: " . $from_email . $u);
783
+ return $from_email . $u;
784
+ }
785
+ }
786
+ }
787
+
788
+ //Can't fix it, return it as it was
789
+ }
790
+ return $from_email;
791
+ }
792
  /*
793
  public static function cronAddSchedules($schedules){
794
  $schedules['wfEachMinute'] = array(
1241
  'IP' => wfUtils::getIP()
1242
  ));
1243
  }
1244
+
1245
+ $salt = wp_salt('logged_in');
1246
+ $cookiename = 'wf_loginalerted_' . hash_hmac('sha256', wfUtils::getIP() . '|' . $user->ID, $salt);
1247
+ $cookievalue = hash_hmac('sha256', $user->user_login, $salt);
1248
  if(user_can($userID, 'update_core')){
1249
  if(wfConfig::get('alertOn_adminLogin')){
1250
+ $shouldAlert = true;
1251
+ if (wfConfig::get('alertOn_firstAdminLoginOnly') && isset($_COOKIE[$cookiename])) {
1252
+ $shouldAlert = !hash_equals($cookievalue, $_COOKIE[$cookiename]);
1253
+ }
1254
+
1255
+ if ($shouldAlert) {
1256
+ wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
1257
+ }
1258
  }
1259
  } else {
1260
  if(wfConfig::get('alertOn_nonAdminLogin')){
1261
+ $shouldAlert = true;
1262
+ if (wfConfig::get('alertOn_firstNonAdminLoginOnly') && isset($_COOKIE[$cookiename])) {
1263
+ $shouldAlert = !hash_equals($cookievalue, $_COOKIE[$cookiename]);
1264
+ }
1265
+
1266
+ if ($shouldAlert) {
1267
+ wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.", wfUtils::getIP());
1268
+ }
1269
  }
1270
  }
1271
+
1272
+ if (wfConfig::get('alertOn_firstAdminLoginOnly') || wfConfig::get('alertOn_firstNonAdminLoginOnly')) {
1273
+ wfUtils::setcookie($cookiename, $cookievalue, time() + (86400 * 365), '/', null, null, true);
1274
+ }
1275
  }
1276
  public static function registrationFilter($errors, $santizedLogin, $userEmail){
1277
  if(wfConfig::get('loginSec_blockAdminReg') && $santizedLogin == 'admin'){
2490
  wfConfig::set('suPHPWAFUpdateChoice', '1');
2491
  return array('ok' => 1);
2492
  }
2493
+ public static function ajax_falconDeprecationChoice_callback() {
2494
+ wfConfig::set('falconDeprecationChoice', '1');
2495
+ return array('ok' => 1);
2496
+ }
2497
  public static function ajax_removeFromCache_callback(){
2498
  $id = $_POST['id'];
2499
  $link = get_permalink($id);
2981
  if (!$issue) {
2982
  return array('cerrorMsg' => "We could not find that issue in our database.");
2983
  }
2984
+
2985
+ if (!function_exists('get_home_path')) {
2986
+ include_once ABSPATH . 'wp-admin/includes/file.php';
2987
+ }
2988
+
2989
+ $homeURL = get_home_url();
2990
+ $components = parse_url($homeURL);
2991
+ if ($components === false) {
2992
+ return array('cerrorMsg' => "An error occurred while trying to hide the file.");
2993
+ }
2994
+
2995
+ $sitePath = '';
2996
+ if (isset($components['path'])) {
2997
+ $sitePath = trim($components['path'], '/');
2998
+ }
2999
+
3000
+ $homePath = get_home_path();
3001
  $file = $issue['data']['file'];
3002
+ $localFile = ABSPATH . '/' . $file; //The scanner uses ABSPATH as its base rather than get_home_path()
3003
  $localFile = realpath($localFile);
3004
+ if (strpos($localFile, $homePath) !== 0) {
3005
+ return array('cerrorMsg' => "An invalid file was requested for hiding.");
3006
  }
3007
+ $localFile = substr($localFile, strlen($homePath));
3008
+ $absoluteURIPath = trim($sitePath . '/' . $localFile, '/');
3009
+ $regexLocalFile = preg_replace('#/#', '/+', preg_quote($absoluteURIPath));
3010
+ $filename = basename($localFile);
3011
+
3012
+ $htaccessContent = <<<HTACCESS
3013
+ <IfModule mod_rewrite.c>
3014
+ RewriteEngine On
3015
+ RewriteCond %{REQUEST_URI} ^/?{$regexLocalFile}$
3016
+ RewriteRule .* - [F,L,NC]
3017
  </IfModule>
3018
+ <IfModule !mod_rewrite.c>
3019
+ <Files "{$filename}">
3020
+ <IfModule mod_authz_core.c>
3021
+ Require all denied
3022
+ </IfModule>
3023
+ <IfModule !mod_authz_core.c>
3024
+ Order deny,allow
3025
+ Deny from all
3026
+ </IfModule>
3027
+ </Files>
3028
  </IfModule>
3029
+ HTACCESS;
3030
+
3031
+ if (!wfUtils::htaccessPrepend($htaccessContent)) {
3032
  return array('cerrorMsg' => "You don't have permission to repair .htaccess. 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.");
3033
  }
3034
  $issues->updateIssue($_POST['issueID'], 'delete');
4254
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4255
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4256
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess',
4257
+ 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'falconDeprecationChoice', 'saveCacheOptions', 'clearPageCache',
4258
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4259
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
4260
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
4413
  echo '<div id="wordfenceAdminEmailWarning" class="fade error"><p><strong>You have not set an administrator email address to receive alerts for Wordfence.</strong> Please <a href="' . self::getMyOptionsURL() . '">click here to go to the Wordfence Options Page</a> and set an email address where you will receive security alerts from this site.</p><p><a class="button button-small" href="#" onclick="wordfenceExt.adminEmailChoice(\'mine\'); return false;"">Use My Email Address</a>
4414
  <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.adminEmailChoice(\'no\'); return false;">Dismiss</a></p></div>';
4415
  }
4416
+ public static function falconDeprecationWarning() {
4417
+ $url = network_admin_url('admin.php?page=WordfenceSitePerf');
4418
+ $cacheName = (wfConfig::get('cacheType') == 'php' ? 'Basic' : 'Falcon');
4419
+ echo '<div id="wordfenceFalconDeprecationWarning" class="fade error"><p><strong>Support for the Falcon and Basic cache will be removed.</strong> This site currently has the ' . $cacheName . ' cache enabled, and it is scheduled to be removed in an upcoming release. Please investigate other caching options and then <a href="' . $url . '">click here to visit the cache settings page</a> to manually disable the cache. It will be disabled automatically when support is removed.</p><p>
4420
+ <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.falconDeprecationChoice(\'no\'); return false;">Dismiss</a></p></div>';
4421
+ }
4422
  public static function autoUpdateNotice(){
4423
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
4424
  }
4441
  }
4442
  $warningAdded = true;
4443
  }
4444
+
4445
+ $page = (isset($_GET['page']) ? $_GET['page'] : '');
4446
+ if ((wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') && !wfConfig::get('falconDeprecationChoice') && $page != 'WordfenceSitePerf') {
4447
+ $warningAdded = true;
4448
+ if(wfUtils::isAdminPageMU()){
4449
+ add_action('network_admin_notices', 'wordfence::falconDeprecationWarning');
4450
+ } else {
4451
+ add_action('admin_notices', 'wordfence::falconDeprecationWarning');
4452
+ }
4453
+ }
4454
  if(! $warningAdded){
4455
  if(wfConfig::get('tourClosed') == '1' && (! wfConfig::get('autoUpdate')) && (! wfConfig::get('autoUpdateChoice'))){
4456
  $warningAdded = true;
4495
  add_submenu_page("Wordfence", "Firewall", "Firewall", "activate_plugins", "WordfenceWAF", 'wordfence::menu_waf');
4496
  add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
4497
  /* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
4498
+ if (wfConfig::get('wf621HadFalconEnabled') || (defined('WF_ENABLE_FALCON') && WF_ENABLE_FALCON)) {
4499
+ add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
4500
+ }
4501
  add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
4502
  add_submenu_page('Wordfence', 'Password Audit', 'Password Audit', 'activate_plugins', 'WordfencePasswdAudit', 'wordfence::menu_passwd');
4503
 
5774
  ) {
5775
  status_header(404);
5776
  nocache_headers();
5777
+
5778
+ $template = get_404_template();
5779
+ if ($template && file_exists($template)) {
5780
+ include($template);
5781
+ }
5782
+
5783
  exit;
5784
  }
5785
  return $query_vars;
lib/wordfenceHash.php CHANGED
@@ -26,6 +26,8 @@ class wordfenceHash {
26
  private $totalForks = 0;
27
  private $alertedOnUnknownWordPressVersion = false;
28
  private $foldersProcessed = array();
 
 
29
 
30
  /**
31
  * @param string $striplen
@@ -59,6 +61,8 @@ class wordfenceHash {
59
  if(wfConfig::get('scansEnabled_coreUnknown')){
60
  $this->coreUnknownEnabled = true;
61
  }
 
 
62
 
63
  $this->db = new wfDB();
64
 
@@ -112,13 +116,16 @@ class wordfenceHash {
112
  if($this->coreUnknownEnabled){ $this->status['coreUnknown'] = wordfence::statusStart("Scanning for unknown files in wp-admin and wp-includes"); } else { wordfence::statusDisabled("Skipping unknown core file scan"); }
113
  }
114
  public function __sleep(){
115
- return array('striplen', 'totalFiles', 'totalDirs', 'totalData', 'stoppedOnFile', 'coreEnabled', 'pluginsEnabled', 'themesEnabled', 'malwareEnabled', 'coreUnknownEnabled', 'knownFiles', 'malwareData', 'haveIssues', 'status', 'possibleMalware', 'path', 'only', 'totalForks', 'alertedOnUnknownWordPressVersion', 'foldersProcessed');
116
  }
117
  public function __wakeup(){
118
  $this->db = new wfDB();
119
  $this->startTime = microtime(true);
120
  $this->totalForks++;
121
  }
 
 
 
122
  public function run($engine){ //base path and 'only' is a list of files and dirs in the bast that are the only ones that should be processed. Everything else in base is ignored. If only is empty then everything is processed.
123
  if($this->totalForks > 1000){
124
  throw new Exception("Wordfence file scanner detected a possible infinite loop. Exiting on file: " . $this->stoppedOnFile);
@@ -237,6 +244,12 @@ class wordfenceHash {
237
  }
238
  private function processFile($realFile){
239
  $file = substr($realFile, $this->striplen);
 
 
 
 
 
 
240
  if (!$this->_shouldHashFile($realFile)) {
241
  wordfence::status(4, 'info', "Skipping unneeded hash: {$realFile}");
242
  return;
@@ -303,7 +316,7 @@ class wordfenceHash {
303
 
304
  $this->haveIssues['core'] = true;
305
  $this->engine->addIssue(
306
- 'file',
307
  1,
308
  'coreModified' . $file . $md5,
309
  'coreModified' . $file,
@@ -337,7 +350,7 @@ class wordfenceHash {
337
  $cKey = $this->knownFiles['plugins'][$file][2];
338
  $this->haveIssues['plugins'] = true;
339
  $this->engine->addIssue(
340
- 'file',
341
  2,
342
  'modifiedplugin' . $file . $md5,
343
  'modifiedplugin' . $file,
@@ -374,7 +387,7 @@ class wordfenceHash {
374
  $cKey = $this->knownFiles['themes'][$file][2];
375
  $this->haveIssues['themes'] = true;
376
  $this->engine->addIssue(
377
- 'file',
378
  2,
379
  'modifiedtheme' . $file . $md5,
380
  'modifiedtheme' . $file,
@@ -402,7 +415,7 @@ class wordfenceHash {
402
  if (strpos($realFile, $path) === 0) {
403
  $this->haveIssues['coreUnknown'] = true;
404
  $this->engine->addIssue(
405
- 'file',
406
  2,
407
  'coreUnknown' . $file . $md5,
408
  'coreUnknown' . $file,
@@ -424,6 +437,8 @@ class wordfenceHash {
424
  // we could split this into files who's path we recognize and file's who's path we recognize AND who have a valid sig.
425
  // But because we want to scan files who's sig we don't recognize, regardless of known path or not, we only need one "knownFile" field.
426
  $this->db->queryWrite("insert into " . $this->db->prefix() . "wfFileMods (filename, filenameMD5, knownFile, oldMD5, newMD5) values ('%s', unhex(md5('%s')), %d, '', unhex('%s')) ON DUPLICATE KEY UPDATE newMD5=unhex('%s'), knownFile=%d", $file, $file, $knownFile, $md5, $md5, $knownFile);
 
 
427
 
428
  $this->totalFiles++;
429
  $this->totalData += filesize($realFile); //We already checked if file overflows int in the fileTooBig routine above
@@ -455,7 +470,16 @@ class wordfenceHash {
455
  return array($md5, $shac);
456
  }
457
  private function _shouldHashFile($fullPath) {
458
- $file = substr($fullPath, $this->striplen);
 
 
 
 
 
 
 
 
 
459
 
460
  //Core File, return true
461
  if ((isset($this->knownFiles['core']) && isset($this->knownFiles['core'][$file])) ||
26
  private $totalForks = 0;
27
  private $alertedOnUnknownWordPressVersion = false;
28
  private $foldersProcessed = array();
29
+ private $filesProcessedBloomFilter = false;
30
+ private $suspectedFiles = array();
31
 
32
  /**
33
  * @param string $striplen
61
  if(wfConfig::get('scansEnabled_coreUnknown')){
62
  $this->coreUnknownEnabled = true;
63
  }
64
+
65
+ $this->filesProcessedBloomFilter = new wfMD5BloomFilter(65536, 8); //65536,8 produces the lowest miss probability. Uses approximately 8 KB of memory + PHP object overhead
66
 
67
  $this->db = new wfDB();
68
 
116
  if($this->coreUnknownEnabled){ $this->status['coreUnknown'] = wordfence::statusStart("Scanning for unknown files in wp-admin and wp-includes"); } else { wordfence::statusDisabled("Skipping unknown core file scan"); }
117
  }
118
  public function __sleep(){
119
+ return array('striplen', 'totalFiles', 'totalDirs', 'totalData', 'stoppedOnFile', 'coreEnabled', 'pluginsEnabled', 'themesEnabled', 'malwareEnabled', 'coreUnknownEnabled', 'knownFiles', 'malwareData', 'haveIssues', 'status', 'possibleMalware', 'path', 'only', 'totalForks', 'alertedOnUnknownWordPressVersion', 'foldersProcessed', 'filesProcessedBloomFilter', 'suspectedFiles');
120
  }
121
  public function __wakeup(){
122
  $this->db = new wfDB();
123
  $this->startTime = microtime(true);
124
  $this->totalForks++;
125
  }
126
+ public function getSuspectedFiles() {
127
+ return array_keys($this->suspectedFiles);
128
+ }
129
  public function run($engine){ //base path and 'only' is a list of files and dirs in the bast that are the only ones that should be processed. Everything else in base is ignored. If only is empty then everything is processed.
130
  if($this->totalForks > 1000){
131
  throw new Exception("Wordfence file scanner detected a possible infinite loop. Exiting on file: " . $this->stoppedOnFile);
244
  }
245
  private function processFile($realFile){
246
  $file = substr($realFile, $this->striplen);
247
+
248
+ if (preg_match('/\.suspected$/i', $file)) { //Already iterating over all files in the search areas so generate this list here
249
+ wordfence::status(4, 'info', "Found .suspected file: $file");
250
+ $this->suspectedFiles[$file] = 1;
251
+ }
252
+
253
  if (!$this->_shouldHashFile($realFile)) {
254
  wordfence::status(4, 'info', "Skipping unneeded hash: {$realFile}");
255
  return;
316
 
317
  $this->haveIssues['core'] = true;
318
  $this->engine->addIssue(
319
+ 'knownfile',
320
  1,
321
  'coreModified' . $file . $md5,
322
  'coreModified' . $file,
350
  $cKey = $this->knownFiles['plugins'][$file][2];
351
  $this->haveIssues['plugins'] = true;
352
  $this->engine->addIssue(
353
+ 'knownfile',
354
  2,
355
  'modifiedplugin' . $file . $md5,
356
  'modifiedplugin' . $file,
387
  $cKey = $this->knownFiles['themes'][$file][2];
388
  $this->haveIssues['themes'] = true;
389
  $this->engine->addIssue(
390
+ 'knownfile',
391
  2,
392
  'modifiedtheme' . $file . $md5,
393
  'modifiedtheme' . $file,
415
  if (strpos($realFile, $path) === 0) {
416
  $this->haveIssues['coreUnknown'] = true;
417
  $this->engine->addIssue(
418
+ 'knownfile',
419
  2,
420
  'coreUnknown' . $file . $md5,
421
  'coreUnknown' . $file,
437
  // we could split this into files who's path we recognize and file's who's path we recognize AND who have a valid sig.
438
  // But because we want to scan files who's sig we don't recognize, regardless of known path or not, we only need one "knownFile" field.
439
  $this->db->queryWrite("insert into " . $this->db->prefix() . "wfFileMods (filename, filenameMD5, knownFile, oldMD5, newMD5) values ('%s', unhex(md5('%s')), %d, '', unhex('%s')) ON DUPLICATE KEY UPDATE newMD5=unhex('%s'), knownFile=%d", $file, $file, $knownFile, $md5, $md5, $knownFile);
440
+
441
+ $this->filesProcessedBloomFilter->add($file);
442
 
443
  $this->totalFiles++;
444
  $this->totalData += filesize($realFile); //We already checked if file overflows int in the fileTooBig routine above
470
  return array($md5, $shac);
471
  }
472
  private function _shouldHashFile($fullPath) {
473
+ $file = substr($fullPath, $this->striplen);
474
+
475
+ //Already hashed and processed, return false
476
+ if ($this->filesProcessedBloomFilter->contains($file)) { //Might have hashed it, verify
477
+ $rec = $this->db->querySingleRec("SELECT * FROM " . $this->db->prefix() . "wfFileMods WHERE filename = '%s'", $file);
478
+ if ($rec !== null) {
479
+ wordfence::status(4, 'info', "Already hashed: {$file}");
480
+ return false;
481
+ }
482
+ }
483
 
484
  //Core File, return true
485
  if ((isset($this->knownFiles['core']) && isset($this->knownFiles['core'][$file])) ||
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
  === Wordfence Security ===
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.2.0
7
 
8
- Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scanner, blocking, live traffic, login security & more.
9
 
10
  == Description ==
11
  = THE MOST DOWNLOADED WORDPRESS SECURITY PLUGIN =
@@ -61,11 +61,7 @@ Wordfence Security is now Multi-Site compatible and includes Cellphone Sign-in w
61
 
62
  = Multi-Site Security =
63
  * Wordfence Security for multi-site also scans all posts and comments across all blogs from one admin panel.
64
- * WordPress Multi-Site (or WordPress MU in the older parlance) compatible.
65
-
66
- = Caching Features =
67
- * Includes Falcon Engine, the fastest WordPress caching engine available today. Falcon is faster because it reduces your web server disk and database activity to a minimum.
68
- * Wordfence includes two caching modes for compatability and has cache management features like the ability to clear the cache and monitor cache usage.
69
 
70
  = IPv6 Compatible =
71
  * Fully IPv6 compatible including all whois lookup, location, blocking and security functions.
@@ -107,8 +103,7 @@ Secure your website with Wordfence.
107
  [Visit our support website which contains a FAQ and knowledgebase which is more comprehensive and updated frequently.](http://support.wordfence.com/?utm_source=repo&utm_medium=web&utm_campaign=pluginDesc)
108
 
109
  = What does Wordfence Security do that other WordPress security plugins don't do? =
110
-
111
- * Wordfence Security is the only WordPress security plugin that is fully integrated with it's own high speed caching engine to avoid security and caching conflicts.
112
  * Wordfence Security actually verifies your website source code integrity against the official WordPress repository and shows you the changes. We are the only plugin to do this.
113
  * Wordfence Security provides two-factor authentication (Cellphone Sign-in) for paid members. We're the first plugin to offer this.
114
  * Wordfence Security fully supports IPv6 including giving you the ability to look up the location of IPv6 addresses, block IPv6 ranges, detect IPv6 country and do a whois lookup on IPv6 addresses and more.
@@ -124,10 +119,8 @@ Yes. WordPress MU or Multi-Site as it's called now is fully supported. Using Wor
124
 
125
  = Will Wordfence Security slow my site down? =
126
 
127
- No. Actually it will make your site up to 50X faster when Falcon Engine is enabled, up to 30 times faster with our PHP caching engine and even
128
- without caching Wordfence is extremely fast and uses techniques like caching it's own configuration data to avoid database lookups. Older
129
- versions of Wordfence did incur a slight performance penalty, but we have not only fixed this issue but knocked it out of the park. Wordfence
130
- now makes your site faster than any other caching plugin available!!
131
 
132
  = How often is Wordfence Security updated? =
133
 
@@ -197,6 +190,21 @@ Secure your website with Wordfence.
197
 
198
  == Changelog ==
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  = 6.2.0 =
201
  * Improvement: Massive performance boost in file system scan.
202
  * Improvement: Added low resource usage scan option for shared hosts.
1
  === Wordfence Security ===
2
  Contributors: mmaunder
3
+ Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.6.1
6
+ Stable tag: 6.2.1
7
 
8
+ Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
10
  == Description ==
11
  = THE MOST DOWNLOADED WORDPRESS SECURITY PLUGIN =
61
 
62
  = Multi-Site Security =
63
  * Wordfence Security for multi-site also scans all posts and comments across all blogs from one admin panel.
64
+ * WordPress Multi-Site (or WordPress MU in the older parlance) compatible.
 
 
 
 
65
 
66
  = IPv6 Compatible =
67
  * Fully IPv6 compatible including all whois lookup, location, blocking and security functions.
103
  [Visit our support website which contains a FAQ and knowledgebase which is more comprehensive and updated frequently.](http://support.wordfence.com/?utm_source=repo&utm_medium=web&utm_campaign=pluginDesc)
104
 
105
  = What does Wordfence Security do that other WordPress security plugins don't do? =
106
+
 
107
  * Wordfence Security actually verifies your website source code integrity against the official WordPress repository and shows you the changes. We are the only plugin to do this.
108
  * Wordfence Security provides two-factor authentication (Cellphone Sign-in) for paid members. We're the first plugin to offer this.
109
  * Wordfence Security fully supports IPv6 including giving you the ability to look up the location of IPv6 addresses, block IPv6 ranges, detect IPv6 country and do a whois lookup on IPv6 addresses and more.
119
 
120
  = Will Wordfence Security slow my site down? =
121
 
122
+ No. Wordfence is extremely fast and uses techniques like caching its own configuration data to avoid database lookups and blocking malicious attacks that would slow down your site. Older
123
+ versions of Wordfence did incur a slight performance penalty, but we have not only fixed this issue but knocked it out of the park.
 
 
124
 
125
  = How often is Wordfence Security updated? =
126
 
190
 
191
  == Changelog ==
192
 
193
+ = 6.2.1 =
194
+ * Improvement: Now performing scanning for PHP code in all uploaded files in real-time.
195
+ * Improvement: Improved handling of bad characters and IPv6 ranges in Advanced Blocking.
196
+ * Improvement: Live traffic and scanning activity now display a paused notice when real-time updates are suspended while in the background.
197
+ * Improvement: The file system scan alerts for files flagged by antivirus software with a '.suspected' extension.
198
+ * Improvement: New alert option to get notified only when logins are from a new location/device.
199
+ * Change: First phase for removing the Falcon cache in place, which will add a notice of its pending removal.
200
+ * Fix: Included country flags for Kosovo and Curaçao.
201
+ * Fix: Fixed the .htaccess directives used to hide files found by the scanner.
202
+ * Fix: Dashboard widget shows correct status for failed logins by deleted users.
203
+ * Fix: Removed duplicate issues for modified files in the scan results.
204
+ * Fix: Suppressed warning from reverse lookup on IPv6 addresses without valid DNS records.
205
+ * Fix: Fixed file inclusion error with themes lacking a 404 page.
206
+ * Fix: CSS fixes for activity report email.
207
+
208
  = 6.2.0 =
209
  * Improvement: Massive performance boost in file system scan.
210
  * Improvement: Added low resource usage scan option for shared hosts.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -4,7 +4,7 @@ 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
  }
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.5/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
vendor/wordfence/wf-waf/src/lib/rules.php CHANGED
@@ -457,6 +457,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
457
  'currentuserisnot',
458
  'md5equals',
459
  'filepatternsmatch',
 
460
  );
461
 
462
  /**
@@ -711,7 +712,7 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
711
  if ($file['name'] == (string) $subject) {
712
  $fh = @fopen($file['tmp_name'], 'r');
713
  if (!$fh) {
714
- return false;
715
  }
716
  $totalRead = 0;
717
 
@@ -734,6 +735,122 @@ class wfWAFRuleComparison implements wfWAFRuleInterface {
734
 
735
  return false;
736
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737
 
738
  /**
739
  * @return mixed
457
  'currentuserisnot',
458
  'md5equals',
459
  'filepatternsmatch',
460
+ 'filehasphp',
461
  );
462
 
463
  /**
712
  if ($file['name'] == (string) $subject) {
713
  $fh = @fopen($file['tmp_name'], 'r');
714
  if (!$fh) {
715
+ continue;
716
  }
717
  $totalRead = 0;
718
 
735
 
736
  return false;
737
  }
738
+
739
+ public function fileHasPHP($subject) {
740
+ $request = $this->getWAF()->getRequest();
741
+ $files = $request->getFiles();
742
+ if (!is_array($files)) {
743
+ return false;
744
+ }
745
+
746
+ foreach ($files as $file) {
747
+ if ($file['name'] == (string) $subject) {
748
+ $fh = @fopen($file['tmp_name'], 'r');
749
+ if (!$fh) {
750
+ continue;
751
+ }
752
+
753
+ $totalRead = 0;
754
+ $hasExecutablePHP = false;
755
+ $possiblyHasExecutablePHP = false;
756
+ $hasOpenParen = false;
757
+ $hasCloseParen = false;
758
+ $backtickCount = 0;
759
+ $wrappedTokenCheckBytes = '';
760
+ $maxTokenSize = 15; //__halt_compiler
761
+ $possibleWrappedTokens = array('<?php', '<?=', '<?', '?>', 'exit', 'new', 'clone', 'echo', 'print', 'require', 'include', 'require_once', 'include_once', '__halt_compiler');
762
+
763
+ $readsize = 512 * 1024; //512k at a time
764
+ while (!feof($fh)) {
765
+ $data = fread($fh, $readsize);
766
+ $actualReadsize = strlen($data);
767
+ $totalRead += $actualReadsize;
768
+ if ($totalRead < 1) {
769
+ break;
770
+ }
771
+
772
+ //Make sure we didn't miss PHP split over a chunking boundary
773
+ $wrappedCheckLength = strlen($wrappedTokenCheckBytes);
774
+ if ($wrappedCheckLength > 0) {
775
+ $testBytes = $wrappedTokenCheckBytes . substr($data, 0, min($maxTokenSize, $actualReadsize));
776
+ foreach ($possibleWrappedTokens as $t) {
777
+ $position = strpos($testBytes, $t);
778
+ if ($position !== false && $position < $wrappedCheckLength && $position + strlen($t) >= $wrappedCheckLength) { //Found a token that starts before this segment of data and ends within it
779
+ $data = substr($wrappedTokenCheckBytes, $position) . $data;
780
+ break;
781
+ }
782
+ }
783
+ }
784
+
785
+ //Tokenize the data and check for PHP
786
+ $tokens = @token_get_all($data);
787
+ foreach ($tokens as $token) {
788
+ if (is_array($token)) {
789
+ switch ($token[0]) {
790
+ case T_OPEN_TAG:
791
+ $hasOpenParen = false;
792
+ $hasCloseParen = false;
793
+ $backtickCount = 0;
794
+ $possiblyHasExecutablePHP = false;
795
+ break;
796
+
797
+ case T_OPEN_TAG_WITH_ECHO:
798
+ $hasOpenParen = false;
799
+ $hasCloseParen = false;
800
+ $backtickCount = 0;
801
+ $possiblyHasExecutablePHP = true;
802
+ break;
803
+
804
+ case T_CLOSE_TAG:
805
+ if ($possiblyHasExecutablePHP) {
806
+ $hasExecutablePHP = true; //Assume the echo short tag outputted something useful
807
+ }
808
+ break 2;
809
+
810
+ case T_NEW:
811
+ case T_CLONE:
812
+ case T_ECHO:
813
+ case T_PRINT:
814
+ case T_REQUIRE:
815
+ case T_INCLUDE:
816
+ case T_REQUIRE_ONCE:
817
+ case T_INCLUDE_ONCE:
818
+ case T_HALT_COMPILER:
819
+ case T_EXIT:
820
+ $hasExecutablePHP = true;
821
+ break 2;
822
+ }
823
+ }
824
+ else {
825
+ switch ($token) {
826
+ case '(':
827
+ $hasOpenParen = true;
828
+ break;
829
+ case ')':
830
+ $hasCloseParen = true;
831
+ break;
832
+ case '`':
833
+ $backtickCount++;
834
+ break;
835
+ }
836
+ }
837
+ if (!$hasExecutablePHP && (($hasOpenParen && $hasCloseParen) || ($backtickCount > 1 && $backtickCount % 2 === 0))) {
838
+ $hasExecutablePHP = true;
839
+ break;
840
+ }
841
+ }
842
+
843
+ if ($hasExecutablePHP) {
844
+ return true;
845
+ }
846
+
847
+ $wrappedTokenCheckBytes = substr($data, - min($maxTokenSize, $actualReadsize));
848
+ }
849
+ }
850
+ }
851
+
852
+ return false;
853
+ }
854
 
855
  /**
856
  * @return mixed
views/reports/activity-report-email-inline.php CHANGED
@@ -89,8 +89,8 @@ a.comment-edit-link:hover { color: #21759b !important; }
89
  @viewport { width: device-width !important; }
90
  .main-navigation li a:hover { color: #000 !important; }
91
  .main-navigation li a:focus { color: #000 !important; }
92
- .main-navigation ul li:hover &gt; ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
93
- .main-navigation ul li:focus &gt; ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
94
  .main-navigation li ul li a:hover { background: #e3e3e3 !important; color: #444 !important; }
95
  .main-navigation li ul li a:focus { background: #e3e3e3 !important; color: #444 !important; }
96
  footer a[rel=bookmark]:after { content: " [" attr(href) "] " !important; }
89
  @viewport { width: device-width !important; }
90
  .main-navigation li a:hover { color: #000 !important; }
91
  .main-navigation li a:focus { color: #000 !important; }
92
+ .main-navigation ul li:hover > ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
93
+ .main-navigation ul li:focus > ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
94
  .main-navigation li ul li a:hover { background: #e3e3e3 !important; color: #444 !important; }
95
  .main-navigation li ul li a:focus { background: #e3e3e3 !important; color: #444 !important; }
96
  footer a[rel=bookmark]:after { content: " [" attr(href) "] " !important; }
views/waf/debug.php CHANGED
@@ -111,7 +111,7 @@ try {
111
  margin: 20px 0px 8px;
112
  }
113
  pre, p {
114
- 8px 0px 20px;
115
  }
116
  pre.request-debug {
117
  padding: 12px;
111
  margin: 20px 0px 8px;
112
  }
113
  pre, p {
114
+ margin: 8px 0px 20px;
115
  }
116
  pre.request-debug {
117
  padding: 12px;
waf/wfWAFUserIPRange.php CHANGED
@@ -50,9 +50,11 @@ class wfWAFUserIPRange {
50
 
51
  // IPv6 range
52
  } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
53
- if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
54
- $IPparts = explode(':', strtolower(wfWAFUtils::expandIPv6Address($ip)));
55
- $whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
 
 
56
  $mismatch = false;
57
  for ($i = 0; $i <= 7; $i++) {
58
  if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
@@ -219,6 +221,6 @@ class wfWAFUserIPRange {
219
  * @param string|null $ip_string
220
  */
221
  public function setIPString($ip_string) {
222
- $this->ip_string = $ip_string;
223
  }
224
  }
50
 
51
  // IPv6 range
52
  } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
53
+ $ip = strtolower(wfWAFUtils::expandIPv6Address($ip));
54
+ $ip_string = strtolower(self::expandIPv6Range($ip_string));
55
+ if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) {
56
+ $IPparts = explode(':', $ip);
57
+ $whiteParts = explode(':', $ip_string);
58
  $mismatch = false;
59
  for ($i = 0; $i <= 7; $i++) {
60
  if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
221
  * @param string|null $ip_string
222
  */
223
  public function setIPString($ip_string) {
224
+ $this->ip_string = preg_replace('/[\x{2013}-\x{2015}]/u', '-', $ip_string); //Replace em-dash, en-dash, and horizontal bar with a regular dash
225
  }
226
  }
wordfence.php CHANGED
@@ -2,16 +2,16 @@
2
  /*
3
  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.2.0
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.2.0');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
2
  /*
3
  Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
+ Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 6.2.1
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.2.1');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17