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(.*)\]#x2F;)[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