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 | 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 +8 -0
- js/admin.js +25 -2
- lib/GeoIP.dat +0 -0
- lib/GeoIPv6.dat +0 -0
- lib/menu_options.php +1 -1
- lib/menu_twoFactor.php +9 -1
- lib/wfLog.php +1 -1
- lib/wordfenceClass.php +18 -2
- lib/wordfenceHash.php +1 -0
- lib/wordfenceScanner.php +12 -5
- readme.txt +8 -1
- vendor/wordfence/wf-waf/src/init.php +1 -1
- vendor/wordfence/wf-waf/src/lib/waf.php +13 -0
- waf/bootstrap.php +18 -1
- wordfence.php +2 -2
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>
|
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('
|
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' ); ?> /> 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' ); ?> /> 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 |
-
$
|
|
|
|
|
|
|
|
|
|
|
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($
|
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.
|
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.
|
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.
|
8 |
Author URI: http://www.wordfence.com/
|
9 |
Network: true
|
10 |
*/
|
11 |
if(defined('WP_INSTALLING') && WP_INSTALLING){
|
12 |
return;
|
13 |
}
|
14 |
-
define('WORDFENCE_VERSION', '6.1.
|
15 |
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 |
|