Wordfence Security – Firewall & Malware Scan - Version 6.1.14

Version Description

  • Improvement: Support downloading a file of 2FA recovery codes.
  • Fix: Fixed PHP Notice: Undefined index: coreUnknown during scans.
  • Improvement: Add note to options page that login security is necessary for 2FA to work.
  • Fix: Fixed WAF false positives introduced with WordPress 4.6.
  • Improvement: Update Geo IP database.
Download this release

Release Info

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

Code changes from version 6.1.12 to 6.1.14

css/main.css CHANGED
@@ -1031,6 +1031,10 @@ pre.wf-pre {
1031
  overflow: auto;
1032
  }
1033
 
 
 
 
 
1034
 
1035
  /*
1036
  Whitelist table
@@ -1068,4 +1072,8 @@ tr.wf-table-filters input {
1068
  #wfTwoFactorRecoveryCodes li {
1069
  font-family: monospace;
1070
  text-align: center;
 
 
 
 
1071
  }
1031
  overflow: auto;
1032
  }
1033
 
1034
+ .wf-center {
1035
+ text-align: center;
1036
+ }
1037
+
1038
 
1039
  /*
1040
  Whitelist table
1072
  #wfTwoFactorRecoveryCodes li {
1073
  font-family: monospace;
1074
  text-align: center;
1075
+ }
1076
+
1077
+ #wfTwoFactorDownload .dashicons {
1078
+ line-height: 26px;
1079
  }
js/admin.js CHANGED
@@ -2115,24 +2115,33 @@
2115
  var totpURL = "otpauth://totp/" + encodeURI(res.homeurl) + encodeURI(" (" + res.username + ")") + "?" + res.uriQueryString + "&issuer=Wordfence";
2116
  self.twoFacStatus('User added! Scan the QR code with your authenticator app to add it.');
2117
 
2118
- var message = "Scan the code below with your authenticator app to add this account. Some authenticator apps also allow you to type in the text version instead.<br><div id=\"wfTwoFactorQRCodeTable\"></div><br><strong>Code:</strong> <input type=\"text\" size=\"34\" value=\"" + res.base32Secret + "\" onclick=\"this.select();\" readonly>";
2119
  if (res.recoveryCodes.length > 0) {
2120
  message = message + "<br><br><strong>Recovery Codes</strong><br><p>Use these codes to log in if you lose access to your authenticator device. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2121
 
 
2122
  var splitter = /.{4}/g;
2123
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2124
  var code = res.recoveryCodes[i];
2125
  var chunks = code.match(splitter);
2126
  message = message + "<li>" + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "</li>";
 
2127
  }
2128
 
2129
  message = message + "</ul>";
 
 
2130
  }
2131
 
2132
  message = message + "<p><em>This will be shown only once. Keep these codes somewhere safe.</em></p>";
2133
 
2134
- self.colorbox('400px', "Authentication Code", message, {onComplete: function() {
2135
  jQuery('#wfTwoFactorQRCodeTable').qrcode({text: totpURL});
 
 
 
 
 
2136
  }});
2137
  }
2138
  else {
@@ -2141,17 +2150,26 @@
2141
  if (res.recoveryCodes.length > 0) {
2142
  var message = "<p>Use these codes to log in if you are unable to access your phone. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2143
 
 
2144
  var splitter = /.{4}/g;
2145
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2146
  var code = res.recoveryCodes[i];
2147
  var chunks = code.match(splitter);
2148
  message = message + "<li>" + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "</li>";
 
2149
  }
2150
 
 
 
2151
  message = message + "</ul><p><em>This will be shown only once. Keep these codes somewhere safe.</em></p>";
2152
 
2153
  self.colorbox('400px', "Recovery Codes", message, {onComplete: function() {
2154
  jQuery('#wfTwoFactorQRCodeTable').qrcode({text: totpURL});
 
 
 
 
 
2155
  }});
2156
  }
2157
  }
@@ -2642,3 +2660,8 @@
2642
  wordfenceAdmin.init();
2643
  });
2644
  })(jQuery);
 
 
 
 
 
2115
  var totpURL = "otpauth://totp/" + encodeURI(res.homeurl) + encodeURI(" (" + res.username + ")") + "?" + res.uriQueryString + "&issuer=Wordfence";
2116
  self.twoFacStatus('User added! Scan the QR code with your authenticator app to add it.');
2117
 
2118
+ var message = "Scan the code below with your authenticator app to add this account. Some authenticator apps also allow you to type in the text version instead.<br><div id=\"wfTwoFactorQRCodeTable\"></div><br><strong>Key:</strong> <input type=\"text\" size=\"45\" value=\"" + res.base32Secret + "\" onclick=\"this.select();\" readonly>";
2119
  if (res.recoveryCodes.length > 0) {
2120
  message = message + "<br><br><strong>Recovery Codes</strong><br><p>Use these codes to log in if you lose access to your authenticator device. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2121
 
2122
+ var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
2123
  var splitter = /.{4}/g;
2124
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2125
  var code = res.recoveryCodes[i];
2126
  var chunks = code.match(splitter);
2127
  message = message + "<li>" + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "</li>";
2128
+ recoveryCodeFileContents = recoveryCodeFileContents + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "\r\n";
2129
  }
2130
 
2131
  message = message + "</ul>";
2132
+
2133
+ message = message + "<p class=\"wf-center\"><a href=\"#\" class=\"button\" id=\"wfTwoFactorDownload\" target=\"_blank\"><i class=\"dashicons dashicons-download\"></i> Download</a></p>";
2134
  }
2135
 
2136
  message = message + "<p><em>This will be shown only once. Keep these codes somewhere safe.</em></p>";
2137
 
2138
+ self.colorbox('440px', "Authentication Code", message, {onComplete: function() {
2139
  jQuery('#wfTwoFactorQRCodeTable').qrcode({text: totpURL});
2140
+ jQuery('#wfTwoFactorDownload').on('click', function(e) {
2141
+ e.preventDefault();
2142
+ e.stopPropagation();
2143
+ saveAs(new Blob([recoveryCodeFileContents], {type: "text/plain;charset=" + document.characterSet}), self.htmlEscape(res.homeurl) + "_" + self.htmlEscape(res.username) + "_recoverycodes.txt");
2144
+ });
2145
  }});
2146
  }
2147
  else {
2150
  if (res.recoveryCodes.length > 0) {
2151
  var message = "<p>Use these codes to log in if you are unable to access your phone. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2152
 
2153
+ var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
2154
  var splitter = /.{4}/g;
2155
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2156
  var code = res.recoveryCodes[i];
2157
  var chunks = code.match(splitter);
2158
  message = message + "<li>" + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "</li>";
2159
+ recoveryCodeFileContents = recoveryCodeFileContents + chunks[0] + " " + chunks[1] + " " + chunks[2] + " " + chunks[3] + "\r\n";
2160
  }
2161
 
2162
+ message = message + "<p class=\"wf-center\"><a href=\"#\" class=\"button\" id=\"wfTwoFactorDownload\" target=\"_blank\"><i class=\"dashicons dashicons-download\"></i> Download</a></p>";
2163
+
2164
  message = message + "</ul><p><em>This will be shown only once. Keep these codes somewhere safe.</em></p>";
2165
 
2166
  self.colorbox('400px', "Recovery Codes", message, {onComplete: function() {
2167
  jQuery('#wfTwoFactorQRCodeTable').qrcode({text: totpURL});
2168
+ jQuery('#wfTwoFactorDownload').on('click', function(e) {
2169
+ e.preventDefault();
2170
+ e.stopPropagation();
2171
+ saveAs(new Blob([recoveryCodeFileContents], {type: "text/plain;charset=" + document.characterSet}), self.htmlEscape(res.homeurl) + "_" + self.htmlEscape(res.username) + "_recoverycodes.txt");
2172
+ });
2173
  }});
2174
  }
2175
  }
2660
  wordfenceAdmin.init();
2661
  });
2662
  })(jQuery);
2663
+
2664
+ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
2665
+ var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/constructor/i.test(e.HTMLElement),f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(i){u(i)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,s){if(!s){t=p(t)}var v=this,w=t.type,m=w===d,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&a)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;i(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define([],function(){return saveAs})}
2666
+
2667
+ !function(t){"use strict";if(t.URL=t.URL||t.webkitURL,t.Blob&&t.URL)try{return void new Blob}catch(e){}var n=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||function(t){var e=function(t){return Object.prototype.toString.call(t).match(/^\[object\s(.*)\]$/)[1]},n=function(){this.data=[]},o=function(t,e,n){this.data=t,this.size=t.length,this.type=e,this.encoding=n},i=n.prototype,a=o.prototype,r=t.FileReaderSync,c=function(t){this.code=this[this.name=t]},l="NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR".split(" "),s=l.length,u=t.URL||t.webkitURL||t,d=u.createObjectURL,f=u.revokeObjectURL,R=u,p=t.btoa,h=t.atob,b=t.ArrayBuffer,g=t.Uint8Array,w=/^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/;for(o.fake=a.fake=!0;s--;)c.prototype[l[s]]=s+1;return u.createObjectURL||(R=t.URL=function(t){var e,n=document.createElementNS("http://www.w3.org/1999/xhtml","a");return n.href=t,"origin"in n||("data:"===n.protocol.toLowerCase()?n.origin=null:(e=t.match(w),n.origin=e&&e[1])),n}),R.createObjectURL=function(t){var e,n=t.type;return null===n&&(n="application/octet-stream"),t instanceof o?(e="data:"+n,"base64"===t.encoding?e+";base64,"+t.data:"URI"===t.encoding?e+","+decodeURIComponent(t.data):p?e+";base64,"+p(t.data):e+","+encodeURIComponent(t.data)):d?d.call(u,t):void 0},R.revokeObjectURL=function(t){"data:"!==t.substring(0,5)&&f&&f.call(u,t)},i.append=function(t){var n=this.data;if(g&&(t instanceof b||t instanceof g)){for(var i="",a=new g(t),l=0,s=a.length;s>l;l++)i+=String.fromCharCode(a[l]);n.push(i)}else if("Blob"===e(t)||"File"===e(t)){if(!r)throw new c("NOT_READABLE_ERR");var u=new r;n.push(u.readAsBinaryString(t))}else t instanceof o?"base64"===t.encoding&&h?n.push(h(t.data)):"URI"===t.encoding?n.push(decodeURIComponent(t.data)):"raw"===t.encoding&&n.push(t.data):("string"!=typeof t&&(t+=""),n.push(unescape(encodeURIComponent(t))))},i.getBlob=function(t){return arguments.length||(t=null),new o(this.data.join(""),t,"raw")},i.toString=function(){return"[object BlobBuilder]"},a.slice=function(t,e,n){var i=arguments.length;return 3>i&&(n=null),new o(this.data.slice(t,i>1?e:this.data.length),n,this.encoding)},a.toString=function(){return"[object Blob]"},a.close=function(){this.size=0,delete this.data},n}(t);t.Blob=function(t,e){var o=e?e.type||"":"",i=new n;if(t)for(var a=0,r=t.length;r>a;a++)Uint8Array&&t[a]instanceof Uint8Array?i.append(t[a].buffer):i.append(t[a]);var c=i.getBlob(o);return!c.slice&&c.webkitSlice&&(c.slice=c.webkitSlice),c};var o=Object.getPrototypeOf||function(t){return t.__proto__};t.Blob.prototype=o(new t.Blob)}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content||this);
lib/GeoIP.dat CHANGED
Binary file
lib/GeoIPv6.dat CHANGED
Binary file
lib/menu_options.php CHANGED
@@ -94,7 +94,7 @@ $w = new wfConfig();
94
  class="wfhelp"></a></th>
95
  <td><input type="checkbox" id="loginSecurityEnabled" class="wfConfigElem" name="loginSecurityEnabled"
96
  value="1" <?php $w->cb( 'loginSecurityEnabled' ); ?> />&nbsp;This option enables all "Login
97
- Security" options. You can modify individual options further down this page.
98
  </td>
99
  </tr>
100
  <tr>
94
  class="wfhelp"></a></th>
95
  <td><input type="checkbox" id="loginSecurityEnabled" class="wfConfigElem" name="loginSecurityEnabled"
96
  value="1" <?php $w->cb( 'loginSecurityEnabled' ); ?> />&nbsp;This option enables all "Login
97
+ Security" options, including two-factor authentication, strong password enforcement, and invalid login throttling. You can modify individual options further down this page.
98
  </td>
99
  </tr>
100
  <tr>
lib/menu_twoFactor.php CHANGED
@@ -25,7 +25,15 @@
25
  </div>
26
 
27
  <?php } ?>
28
-
 
 
 
 
 
 
 
 
29
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
30
  <h2>Enable Cellphone Sign-in</h2>
31
  <p><em>Our Cellphone Sign-in uses a technique called "Two Factor Authentication" which is used by banks, government agencies and military world-wide as one of the most secure forms of remote system authentication. We recommend you enable Cellphone Sign-in for all Administrator level accounts.</em></p>
25
  </div>
26
 
27
  <?php } ?>
28
+
29
+ <?php
30
+ if (!wfConfig::get('loginSecurityEnabled')) {
31
+ $url = network_admin_url('admin.php?page=WordfenceSecOpt');
32
+ ?>
33
+ <div class="notice notice-error"><p>The login security option is currently disabled. This will prevent your website from enforcing two factor authentication for any users configured below. Visit the <a href="<?php echo esc_url($url); ?>">Options page</a> to enable login security.</p></div>
34
+ <?php
35
+ }
36
+ ?>
37
  <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
38
  <h2>Enable Cellphone Sign-in</h2>
39
  <p><em>Our Cellphone Sign-in uses a technique called "Two Factor Authentication" which is used by banks, government agencies and military world-wide as one of the most secure forms of remote system authentication. We recommend you enable Cellphone Sign-in for all Administrator level accounts.</em></p>
lib/wfLog.php CHANGED
@@ -1770,7 +1770,7 @@ class wfLiveTrafficQuery {
1770
  $limit = absint($this->getLimit());
1771
  $offset = absint($this->getOffset());
1772
 
1773
- $wheres = array();
1774
  if ($startDate) {
1775
  $wheres[] = $wpdb->prepare('h.ctime > %f', $startDate);
1776
  }
1770
  $limit = absint($this->getLimit());
1771
  $offset = absint($this->getOffset());
1772
 
1773
+ $wheres = array("h.action != 'logged:waf'");
1774
  if ($startDate) {
1775
  $wheres[] = $wpdb->prepare('h.ctime > %f', $startDate);
1776
  }
lib/wordfenceClass.php CHANGED
@@ -1845,6 +1845,7 @@ SQL
1845
  'ok' => 1,
1846
  'userID' => $user->ID,
1847
  'username' => $username,
 
1848
  'mode' => $mode,
1849
  'phone' => $phone,
1850
  'recoveryCodes' => $recoveryCodes,
@@ -5726,7 +5727,7 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
5726
  $limit = 500;
5727
  $lastSendTime = wfConfig::get('lastAttackDataSendTime');
5728
  $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
5729
- WHERE action in ('blocked:waf', 'learned:waf')
5730
  AND attackLogTime > %.6f
5731
  LIMIT %d", $lastSendTime, $limit));
5732
  $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
@@ -5791,6 +5792,10 @@ LIMIT %d", $lastSendTime, $limit));
5791
  if ($totalRows > $limit) {
5792
  self::scheduleSendAttackData();
5793
  }
 
 
 
 
5794
  }
5795
  }
5796
  } else if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) {
@@ -5870,7 +5875,12 @@ LIMIT %d", $lastSendTime, $limit));
5870
  }
5871
  }
5872
 
5873
- $hit->action = 'blocked:waf';
 
 
 
 
 
5874
 
5875
  /** @var wfWAFRule $rule */
5876
  $ruleIDs = explode('|', $failedRules);
@@ -5889,6 +5899,12 @@ LIMIT %d", $lastSendTime, $limit));
5889
  $actionData['ssl'] = $ssl;
5890
  $actionData['fullRequest'] = base64_encode($requestString);
5891
  }
 
 
 
 
 
 
5892
  }
5893
 
5894
  $hit->actionData = wfRequestModel::serializeActionData($actionData);
1845
  'ok' => 1,
1846
  'userID' => $user->ID,
1847
  'username' => $username,
1848
+ 'homeurl' => preg_replace('#.*?//#', '', get_home_url()),
1849
  'mode' => $mode,
1850
  'phone' => $phone,
1851
  'recoveryCodes' => $recoveryCodes,
5727
  $limit = 500;
5728
  $lastSendTime = wfConfig::get('lastAttackDataSendTime');
5729
  $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits
5730
+ WHERE action in ('blocked:waf', 'learned:waf', 'logged:waf')
5731
  AND attackLogTime > %.6f
5732
  LIMIT %d", $lastSendTime, $limit));
5733
  $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
5792
  if ($totalRows > $limit) {
5793
  self::scheduleSendAttackData();
5794
  }
5795
+
5796
+ if (array_key_exists('data', $jsonData) && array_key_exists('watchedIPList', $jsonData['data'])) {
5797
+ $waf->getStorageEngine()->setConfig('watchedIPs', $jsonData['data']['watchedIPList']);
5798
+ }
5799
  }
5800
  }
5801
  } else if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) {
5875
  }
5876
  }
5877
 
5878
+ if ($failedRules == 'logged') {
5879
+ $hit->action = 'logged:waf';
5880
+ }
5881
+ else {
5882
+ $hit->action = 'blocked:waf';
5883
+ }
5884
 
5885
  /** @var wfWAFRule $rule */
5886
  $ruleIDs = explode('|', $failedRules);
5899
  $actionData['ssl'] = $ssl;
5900
  $actionData['fullRequest'] = base64_encode($requestString);
5901
  }
5902
+ else if ($ruleIDs[0] == 'logged') {
5903
+ $hit->actionDescription = 'Watched IP Traffic: ' . $ip;
5904
+ $actionData['category'] = 'logged';
5905
+ $actionData['ssl'] = $ssl;
5906
+ $actionData['fullRequest'] = base64_encode($requestString);
5907
+ }
5908
  }
5909
 
5910
  $hit->actionData = wfRequestModel::serializeActionData($actionData);
lib/wordfenceHash.php CHANGED
@@ -101,6 +101,7 @@ class wordfenceHash {
101
  }
102
  $this->haveIssues = array(
103
  'core' => false,
 
104
  'themes' => false,
105
  'plugins' => false,
106
  'malware' => false
101
  }
102
  $this->haveIssues = array(
103
  'core' => false,
104
+ 'coreUnknown' => false,
105
  'themes' => false,
106
  'plugins' => false,
107
  'malware' => false
lib/wordfenceScanner.php CHANGED
@@ -291,29 +291,36 @@ class wordfenceScanner {
291
  $regexMatched = false;
292
  foreach ($this->patterns['rules'] as $rule) {
293
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
 
294
  if ($type == 'server' && !$treatAsBinary) { continue; }
295
  else if (($type == 'both' || $type == 'browser') && $fileExt == 'js') { $extraMsg = ''; }
296
  else if (($type == 'both' || $type == 'browser') && !$treatAsBinary) { continue; }
297
 
298
- if (preg_match('/(' . $rule[2] . ')/i', $data, $matches)) {
299
  if (!$this->isSafeFile($this->path . $file)) {
 
 
 
 
 
300
  $this->addResult(array(
301
  'type' => 'file',
302
  'severity' => 1,
303
  'ignoreP' => $this->path . $file,
304
  'ignoreC' => $fileSum,
305
  'shortMsg' => "File appears to be malicious: " . esc_html($file),
306
- 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . esc_html((strlen($matches[1]) > 200 ? substr($matches[1], 0, 200) . '...' : $matches[1])) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>' . $extraMsg,
307
  'data' => array_merge(array(
308
  'file' => $file,
309
  ), $dataForFile),
310
  ));
311
- $regexMatched = true;
312
- $this->scanEngine->recordMetric('malwareSignature', $rule[0], array('file' => $file, 'match' => $matches[1]), false);
313
- break;
314
  }
 
 
 
315
  }
316
  }
 
317
  if ($regexMatched) { break; }
318
  }
319
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
291
  $regexMatched = false;
292
  foreach ($this->patterns['rules'] as $rule) {
293
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
294
+ $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
295
  if ($type == 'server' && !$treatAsBinary) { continue; }
296
  else if (($type == 'both' || $type == 'browser') && $fileExt == 'js') { $extraMsg = ''; }
297
  else if (($type == 'both' || $type == 'browser') && !$treatAsBinary) { continue; }
298
 
299
+ if (preg_match('/(' . $rule[2] . ')/i', $data, $matches, PREG_OFFSET_CAPTURE)) {
300
  if (!$this->isSafeFile($this->path . $file)) {
301
+ $matchString = $matches[1][0];
302
+ $matchOffset = $matches[1][1];
303
+ $beforeString = substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
304
+ $afterString = substr($data, $matchOffset + strlen($matchString), 100);
305
+ if (!$logOnly) {
306
  $this->addResult(array(
307
  'type' => 'file',
308
  'severity' => 1,
309
  'ignoreP' => $this->path . $file,
310
  'ignoreC' => $fileSum,
311
  'shortMsg' => "File appears to be malicious: " . esc_html($file),
312
+ 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . esc_html((strlen($matchString) > 200 ? substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>' . $extraMsg,
313
  'data' => array_merge(array(
314
  'file' => $file,
315
  ), $dataForFile),
316
  ));
 
 
 
317
  }
318
+ $regexMatched = true;
319
+ $this->scanEngine->recordMetric('malwareSignature', $rule[0], array('file' => $file, 'match' => $matchString, 'before' => $beforeString, 'after' => $afterString), false);
320
+ break;
321
  }
322
  }
323
+ }
324
  if ($regexMatched) { break; }
325
  }
326
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking, block hackers
4
  Requires at least: 3.9
5
  Tested up to: 4.6.0
6
- Stable tag: 6.1.12
7
 
8
  Secure your website with the Wordfence security plugin for WordPress. Wordfence provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
 
@@ -200,6 +200,13 @@ Secure your website with Wordfence.
200
 
201
  == Changelog ==
202
 
 
 
 
 
 
 
 
203
  = 6.1.12 =
204
  * Fix: Fixed fatal error on sites running Wordfence 6.1.11 in subdirectory and 6.1.10 or lower in parent directory.
205
  * Fix: Added a few common files to be excluded from unknown WordPress core file scan.
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking, block hackers
4
  Requires at least: 3.9
5
  Tested up to: 4.6.0
6
+ Stable tag: 6.1.14
7
 
8
  Secure your website with the Wordfence security plugin for WordPress. Wordfence provides free enterprise-class WordPress security, protecting your website from hacks and malware.
9
 
200
 
201
  == Changelog ==
202
 
203
+ = 6.1.14 =
204
+ * Improvement: Support downloading a file of 2FA recovery codes.
205
+ * Fix: Fixed PHP Notice: Undefined index: coreUnknown during scans.
206
+ * Improvement: Add note to options page that login security is necessary for 2FA to work.
207
+ * Fix: Fixed WAF false positives introduced with WordPress 4.6.
208
+ * Improvement: Update Geo IP database.
209
+
210
  = 6.1.12 =
211
  * Fix: Fixed fatal error on sites running Wordfence 6.1.11 in subdirectory and 6.1.10 or lower in parent directory.
212
  * Fix: Added a few common files to be excluded from unknown WordPress core file scan.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -4,7 +4,7 @@ define('WFWAF_VERSION', '1.0.0');
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.2/');
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.3/');
8
  if (!defined('WFWAF_DEBUG')) {
9
  define('WFWAF_DEBUG', false);
10
  }
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -188,6 +188,9 @@ auEa+7b+FGTKs7dUo2BNGR7OVifK4GZ8w/ajS0TelhrSRi3BBQCGXLzUO/UURUAh
188
  } catch (wfWAFBlockSQLiException $e) {
189
  $this->eventBus->blockSQLi($ip, $e);
190
  $this->blockAction($e);
 
 
 
191
  }
192
 
193
  $this->runCron();
@@ -736,6 +739,10 @@ HTML
736
  header('HTTP/1.0 403 Forbidden');
737
  exit($this->getBlockedMessage());
738
  }
 
 
 
 
739
 
740
  /**
741
  * @return string
@@ -889,6 +896,9 @@ HTML
889
  $this->getStorageEngine()->truncateAttackData();
890
  $this->getStorageEngine()->unsetConfig('attackDataNextInterval');
891
  }
 
 
 
892
  }
893
  } else if (is_string($response->getBody()) && preg_match('/next check in: ([0-9]+)/', $response->getBody(), $matches)) {
894
  $this->getStorageEngine()->setConfig('attackDataNextInterval', time() + $matches[1]);
@@ -1540,6 +1550,9 @@ class wfWAFBlockXSSException extends wfWAFRunException {
1540
  class wfWAFBlockSQLiException extends wfWAFRunException {
1541
  }
1542
 
 
 
 
1543
  class wfWAFBuildRulesException extends wfWAFException {
1544
  }
1545
 
188
  } catch (wfWAFBlockSQLiException $e) {
189
  $this->eventBus->blockSQLi($ip, $e);
190
  $this->blockAction($e);
191
+
192
+ } catch (wfWAFLogException $e) {
193
+ $this->logAction($e);
194
  }
195
 
196
  $this->runCron();
739
  header('HTTP/1.0 403 Forbidden');
740
  exit($this->getBlockedMessage());
741
  }
742
+
743
+ public function logAction($e) {
744
+ $this->getStorageEngine()->logAttack(array('logged'), $e->getParamKey(), $e->getParamValue(), $this->getRequest());
745
+ }
746
 
747
  /**
748
  * @return string
896
  $this->getStorageEngine()->truncateAttackData();
897
  $this->getStorageEngine()->unsetConfig('attackDataNextInterval');
898
  }
899
+ if (array_key_exists('data', $jsonData) && array_key_exists('watchedIPList', $jsonData['data'])) {
900
+ $this->getStorageEngine()->setConfig('watchedIPs', $jsonData['data']['watchedIPList']);
901
+ }
902
  }
903
  } else if (is_string($response->getBody()) && preg_match('/next check in: ([0-9]+)/', $response->getBody(), $matches)) {
904
  $this->getStorageEngine()->setConfig('attackDataNextInterval', time() + $matches[1]);
1550
  class wfWAFBlockSQLiException extends wfWAFRunException {
1551
  }
1552
 
1553
+ class wfWAFLogException extends wfWAFRunException {
1554
+ }
1555
+
1556
  class wfWAFBuildRulesException extends wfWAFException {
1557
  }
1558
 
waf/bootstrap.php CHANGED
@@ -8,6 +8,7 @@ if (!defined('WFWAF_AUTO_PREPEND')) {
8
  define('WFWAF_AUTO_PREPEND', true);
9
  }
10
 
 
11
  require_once dirname(__FILE__) . '/../vendor/wordfence/wf-waf/src/init.php';
12
 
13
  class wfWAFWordPressRequest extends wfWAFRequest {
@@ -65,7 +66,6 @@ class wfWAFWordPressObserver extends wfWAFBaseObserver {
65
  // Whitelisted IPs (Wordfence config)
66
  $whitelistedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedIPs');
67
  if ($whitelistedIPs) {
68
- require_once dirname(__FILE__) . '/wfWAFUserIPRange.php';
69
  if (!is_array($whitelistedIPs)) {
70
  $whitelistedIPs = explode(',', $whitelistedIPs);
71
  }
@@ -77,6 +77,23 @@ class wfWAFWordPressObserver extends wfWAFBaseObserver {
77
  }
78
  }
79
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
 
82
  /**
8
  define('WFWAF_AUTO_PREPEND', true);
9
  }
10
 
11
+ require_once dirname(__FILE__) . '/wfWAFUserIPRange.php';
12
  require_once dirname(__FILE__) . '/../vendor/wordfence/wf-waf/src/init.php';
13
 
14
  class wfWAFWordPressRequest extends wfWAFRequest {
66
  // Whitelisted IPs (Wordfence config)
67
  $whitelistedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('whitelistedIPs');
68
  if ($whitelistedIPs) {
 
69
  if (!is_array($whitelistedIPs)) {
70
  $whitelistedIPs = explode(',', $whitelistedIPs);
71
  }
77
  }
78
  }
79
  }
80
+
81
+ public function afterRunRules()
82
+ {
83
+ //wfWAFLogException
84
+ $watchedIPs = wfWAF::getInstance()->getStorageEngine()->getConfig('watchedIPs');
85
+ if ($watchedIPs) {
86
+ if (!is_array($watchedIPs)) {
87
+ $watchedIPs = explode(',', $watchedIPs);
88
+ }
89
+ foreach ($watchedIPs as $watchedIP) {
90
+ $ipRange = new wfWAFUserIPRange($watchedIP);
91
+ if ($ipRange->isIPInRange(wfWAF::getInstance()->getRequest()->getIP())) {
92
+ throw new wfWAFLogException('Wordfence watched IP.');
93
+ }
94
+ }
95
+ }
96
+ }
97
  }
98
 
99
  /**
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
- Version: 6.1.12
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.1.12');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 6.1.14
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.1.14');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17