Wordfence Security – Firewall & Malware Scan - Version 5.0.6

Version Description

  • Feature: Prevent discovery of usernames through '?/author=N' scans. New option under login security which you can enable.
  • Fix: Introduced new global hash whitelist on our servers that drastically reduces false positives in all scans especially theme and plugin scans.
  • Fix: Fixed issue that corrupted .htaccess because stat cache would store file size and cause filesize() to report incorrect size when reading/writing .htaccess.
  • Fix: Fixed LiteSpeed issue where Falcon Engine would not serve cached pages under LiteSpeed and LiteSpeed warned about unknown server variable in .htaccess.
  • Fix: Fixed issue where Wordfence Security Network won't block known bad IP after first login attempt if "Don't let WordPress reveal valid users in login errors" option is not enabled.
  • Fix: Sites installed under a directory would sometimes see Falcon not serving cached docs.
  • Fix: If you are a premium customer and you have 2FA enabled and your key expires, fixed issue that may have caused you to get locked out.
  • Improvement: If your Premium API key now expires, we simply downgrade you to free scanning and continue rather than disabling Wordfence.
  • Improvement: Email warnings a few days before your Premium key expires so you have a chance to upgrade for uninterrupted service.
Download this release

Release Info

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

Code changes from version 5.0.5 to 5.0.6

js/admin.js CHANGED
@@ -1224,6 +1224,8 @@ window['wordfenceAdmin'] = {
1224
}, function(res){
1225
if(res.ok){
1226
self.colorbox('400px', "Enabling Falcon Engine", 'First read this <a href="http://www.wordfence.com/introduction-to-wordfence-falcon-engine/" target="_blank">Introduction to Falcon Engine</a>. Falcon modifies your website configuration file which is called your .htaccess file. To enable Falcon we ask that you make a backup of this file. This is a safety precaution in case for some reason Falcon is not compatible with your site.<br /><br /><a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wfNextBut\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a><br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" disabled="disabled" onclick="WFAD.confirmSwitchToFalcon(0);" />');
1227
} else if(res.err){
1228
self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err + "<br /><br />Advanced users: If you would like to manually enable Falcon yourself by editing your .htaccess, you can add the rules below to the beginning of your .htaccess file. Then click the button below to enable Falcon. Don't do this unless you understand website configuration.<br /><textarea style='width: 300px; height:100px;' readonly>" + jQuery('<div/>').text(res.code).html() + "</textarea><br /><input type='button' value='Enable Falcon after manually editing .htaccess' onclick='WFAD.confirmSwitchToFalcon(1);' />");
1229
}
1224
}, function(res){
1225
if(res.ok){
1226
self.colorbox('400px', "Enabling Falcon Engine", 'First read this <a href="http://www.wordfence.com/introduction-to-wordfence-falcon-engine/" target="_blank">Introduction to Falcon Engine</a>. Falcon modifies your website configuration file which is called your .htaccess file. To enable Falcon we ask that you make a backup of this file. This is a safety precaution in case for some reason Falcon is not compatible with your site.<br /><br /><a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wfNextBut\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a><br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" disabled="disabled" onclick="WFAD.confirmSwitchToFalcon(0);" />');
1227
+ } else if(res.nginx){
1228
+ self.colorbox('400px', "Enabling Falcon Engine", 'You are using an Nginx web server and using a FastCGI processor like PHP5-FPM. To use Falcon you will need to manually modify your nginx.conf configuration file and reload your Nginx server for the changes to take effect. You can find the <a href="http://www.wordfence.com/blog/2014/05/nginx-wordfence-falcon-engine-php-fpm-fastcgi-fast-cgi/" target="_blank">rules you need to make these changes to nginx.conf on this page on wordfence.com</a>. Once you have made these changes, compressed cached files will be served to your visitors directly from Nginx making your site extremely fast. When you have made the changes and reloaded your Nginx server, you can click the button below to enable Falcon.<br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" onclick="WFAD.confirmSwitchToFalcon(0);" />');
1229
} else if(res.err){
1230
self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err + "<br /><br />Advanced users: If you would like to manually enable Falcon yourself by editing your .htaccess, you can add the rules below to the beginning of your .htaccess file. Then click the button below to enable Falcon. Don't do this unless you understand website configuration.<br /><textarea style='width: 300px; height:100px;' readonly>" + jQuery('<div/>').text(res.code).html() + "</textarea><br /><input type='button' value='Enable Falcon after manually editing .htaccess' onclick='WFAD.confirmSwitchToFalcon(1);' />");
1231
}
lib/menu_options.php CHANGED
@@ -243,6 +243,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
243
<tr><th>Immediately lock out invalid usernames</th><td><input type="checkbox" id="loginSec_lockInvalidUsers" class="wfConfigElem" name="loginSec_lockInvalidUsers" <?php $w->cb('loginSec_lockInvalidUsers'); ?> /></td></tr>
244
<tr><th>Don't let WordPress reveal valid users in login errors</th><td><input type="checkbox" id="loginSec_maskLoginErrors" class="wfConfigElem" name="loginSec_maskLoginErrors" <?php $w->cb('loginSec_maskLoginErrors'); ?> /></td></tr>
245
<tr><th>Prevent users registering 'admin' username if it doesn't exist</th><td><input type="checkbox" id="loginSec_blockAdminReg" class="wfConfigElem" name="loginSec_blockAdminReg" <?php $w->cb('loginSec_blockAdminReg'); ?> /></td></tr>
246
<tr><td colspan="2">
247
<div class="wfMarker" id="wfMarkerOtherOptions"></div>
248
<h3 class="wfConfigHeading">Other Options</h3>
243
<tr><th>Immediately lock out invalid usernames</th><td><input type="checkbox" id="loginSec_lockInvalidUsers" class="wfConfigElem" name="loginSec_lockInvalidUsers" <?php $w->cb('loginSec_lockInvalidUsers'); ?> /></td></tr>
244
<tr><th>Don't let WordPress reveal valid users in login errors</th><td><input type="checkbox" id="loginSec_maskLoginErrors" class="wfConfigElem" name="loginSec_maskLoginErrors" <?php $w->cb('loginSec_maskLoginErrors'); ?> /></td></tr>
245
<tr><th>Prevent users registering 'admin' username if it doesn't exist</th><td><input type="checkbox" id="loginSec_blockAdminReg" class="wfConfigElem" name="loginSec_blockAdminReg" <?php $w->cb('loginSec_blockAdminReg'); ?> /></td></tr>
246
+ <tr><th>Prevent discovery of usernames through '?/author=N' scans</th><td><input type="checkbox" id="loginSec_disableAuthorScan" class="wfConfigElem" name="loginSec_disableAuthorScan" <?php $w->cb('loginSec_disableAuthorScan'); ?> /></td></tr>
247
<tr><td colspan="2">
248
<div class="wfMarker" id="wfMarkerOtherOptions"></div>
249
<h3 class="wfConfigHeading">Other Options</h3>
lib/wfAPI.php CHANGED
@@ -26,6 +26,15 @@ class wfAPI {
26
}
27
28
$dat = json_decode($json, true);
29
if(! is_array($dat)){
30
throw new Exception("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
31
}
26
}
27
28
$dat = json_decode($json, true);
29
+ if(isset($dat['_isPaidKey'])){
30
+ wfConfig::set('keyExpDays', $dat['_keyExpDays']);
31
+ if($dat['_keyExpDays'] > -1){
32
+ wfConfig::set('isPaid', 1);
33
+ } else if($dat['_keyExpDays'] < 0){
34
+ wfConfig::set('isPaid', '');
35
+ }
36
+ }
37
+
38
if(! is_array($dat)){
39
throw new Exception("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
40
}
lib/wfCache.php CHANGED
@@ -369,6 +369,7 @@ class wfCache {
369
}
370
flock($fh, LOCK_EX);
371
fseek($fh, 0, SEEK_SET); //start of file
372
$contents = fread($fh, filesize($htaccessPath));
373
if(! $contents){
374
fclose($fh);
@@ -388,17 +389,23 @@ class wfCache {
388
}
389
public static function getHtaccessCode(){
390
$siteURL = site_url();
391
$pathPrefix = "";
392
- $matchCaps = '$1/$2~$3~$4~$5~$6';
393
if(preg_match('/^https?:\/\/[^\/]+\/(.+)#x2F;i', $siteURL, $matches)){
394
$path = $matches[1];
395
$path = preg_replace('/^\//', '', $path);
396
$path = preg_replace('/\/#x2F;', '', $path);
397
- $pieces = explode('/', $path);
398
$pathPrefix = '/' . $path; // Which is: /my/path
399
if(count($pieces) == 1){
400
- # No path: "/wp-content/wfcache/%{HTTP_HOST}_$1/$2~$3~$4~$5~$6_wfcache%{WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
401
- # One path: "/mdm/wp-content/wfcache/%{HTTP_HOST}_mdm/$1~$2~$3~$4~$5_wfcache%{WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
402
$matchCaps = $pieces[0] . '/$1~$2~$3~$4~$5';
403
} else if(count($pieces) == 2){
404
$matchCaps = $pieces[0] . '/' . $pieces[1] . '/$1~$2~$3~$4';
@@ -448,8 +455,8 @@ class wfCache {
448
RewriteCond %{HTTP_COOKIE} !(comment_author|wp\-postpass|wf_logout|wordpress_logged_in|wptouch_switch_toggle|wpmp_switcher) [NC]
449
450
RewriteCond %{REQUEST_URI} \/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)(.*)$
451
- RewriteCond "%{DOCUMENT_ROOT}{$pathPrefix}/wp-content/wfcache/%{HTTP_HOST}_%1/%2~%3~%4~%5~%6_wfcache%{WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" -f
452
- RewriteRule \/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)(.*)$ "{$pathPrefix}/wp-content/wfcache/%{HTTP_HOST}_{$matchCaps}_wfcache%{WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
453
</IfModule>
454
#Do not remove this line. Disable Web caching in Wordfence to remove this data - WFCACHECODE
455
EOT;
@@ -479,6 +486,7 @@ EOT;
479
}
480
flock($fh, LOCK_EX);
481
fseek($fh, 0, SEEK_SET); //start of file
482
$contents = @fread($fh, filesize($htaccessPath));
483
if(! $contents){
484
fclose($fh);
@@ -556,6 +564,7 @@ EOT;
556
//Minimize time between lock/unlock
557
flock($fh, LOCK_EX);
558
fseek($fh, 0, SEEK_SET); //start of file
559
$contents = @fread($fh, filesize($htaccessPath));
560
if(! $contents){
561
fclose($fh);
369
}
370
flock($fh, LOCK_EX);
371
fseek($fh, 0, SEEK_SET); //start of file
372
+ clearstatcache();
373
$contents = fread($fh, filesize($htaccessPath));
374
if(! $contents){
375
fclose($fh);
389
}
390
public static function getHtaccessCode(){
391
$siteURL = site_url();
392
+ $homeURL = home_url();
393
$pathPrefix = "";
394
if(preg_match('/^https?:\/\/[^\/]+\/(.+)#x2F;i', $siteURL, $matches)){
395
$path = $matches[1];
396
$path = preg_replace('/^\//', '', $path);
397
$path = preg_replace('/\/#x2F;', '', $path);
398
$pathPrefix = '/' . $path; // Which is: /my/path
399
+ }
400
+ $matchCaps = '$1/$2~$3~$4~$5~$6';
401
+ if(preg_match('/^https?:\/\/[^\/]+\/(.+)#x2F;i', $homeURL, $matches)){
402
+ $path = $matches[1];
403
+ $path = preg_replace('/^\//', '', $path);
404
+ $path = preg_replace('/\/#x2F;', '', $path);
405
+ $pieces = explode('/', $path);
406
if(count($pieces) == 1){
407
+ # No path: "/wp-content/wfcache/%{HTTP_HOST}_$1/$2~$3~$4~$5~$6_wfcache%{ENV:WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
408
+ # One path: "/mdm/wp-content/wfcache/%{HTTP_HOST}_mdm/$1~$2~$3~$4~$5_wfcache%{ENV:WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
409
$matchCaps = $pieces[0] . '/$1~$2~$3~$4~$5';
410
} else if(count($pieces) == 2){
411
$matchCaps = $pieces[0] . '/' . $pieces[1] . '/$1~$2~$3~$4';
455
RewriteCond %{HTTP_COOKIE} !(comment_author|wp\-postpass|wf_logout|wordpress_logged_in|wptouch_switch_toggle|wpmp_switcher) [NC]
456
457
RewriteCond %{REQUEST_URI} \/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)(.*)$
458
+ RewriteCond "%{DOCUMENT_ROOT}{$pathPrefix}/wp-content/wfcache/%{HTTP_HOST}_%1/%2~%3~%4~%5~%6_wfcache%{ENV:WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" -f
459
+ RewriteRule \/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)\/*([^\/]*)(.*)$ "{$pathPrefix}/wp-content/wfcache/%{HTTP_HOST}_{$matchCaps}_wfcache%{ENV:WRDFNC_HTTPS}.html%{ENV:WRDFNC_ENC}" [L]
460
</IfModule>
461
#Do not remove this line. Disable Web caching in Wordfence to remove this data - WFCACHECODE
462
EOT;
486
}
487
flock($fh, LOCK_EX);
488
fseek($fh, 0, SEEK_SET); //start of file
489
+ clearstatcache();
490
$contents = @fread($fh, filesize($htaccessPath));
491
if(! $contents){
492
fclose($fh);
564
//Minimize time between lock/unlock
565
flock($fh, LOCK_EX);
566
fseek($fh, 0, SEEK_SET); //start of file
567
+ clearstatcache(); //Or we get the wrong size from a cached entry and corrupt the file
568
$contents = @fread($fh, filesize($htaccessPath));
569
if(! $contents){
570
fclose($fh);
lib/wfConfig.php CHANGED
@@ -45,6 +45,7 @@ class wfConfig {
45
"loginSec_lockInvalidUsers" => false,
46
"loginSec_maskLoginErrors" => false,
47
"loginSec_blockAdminReg" => false,
48
"other_hideWPVersion" => false,
49
"other_noAnonMemberComments" => false,
50
"other_scanComments" => false,
@@ -119,6 +120,7 @@ class wfConfig {
119
"loginSec_lockInvalidUsers" => false,
120
"loginSec_maskLoginErrors" => true,
121
"loginSec_blockAdminReg" => true,
122
"other_hideWPVersion" => true,
123
"other_noAnonMemberComments" => true,
124
"other_scanComments" => true,
@@ -193,6 +195,7 @@ class wfConfig {
193
"loginSec_lockInvalidUsers" => false,
194
"loginSec_maskLoginErrors" => true,
195
"loginSec_blockAdminReg" => true,
196
"other_hideWPVersion" => true,
197
"other_noAnonMemberComments" => true,
198
"other_scanComments" => true,
@@ -267,6 +270,7 @@ class wfConfig {
267
"loginSec_lockInvalidUsers" => false,
268
"loginSec_maskLoginErrors" => true,
269
"loginSec_blockAdminReg" => true,
270
"other_hideWPVersion" => true,
271
"other_noAnonMemberComments" => true,
272
"other_scanComments" => true,
@@ -341,6 +345,7 @@ class wfConfig {
341
"loginSec_lockInvalidUsers" => true,
342
"loginSec_maskLoginErrors" => true,
343
"loginSec_blockAdminReg" => true,
344
"other_hideWPVersion" => true,
345
"other_noAnonMemberComments" => true,
346
"other_scanComments" => true,
45
"loginSec_lockInvalidUsers" => false,
46
"loginSec_maskLoginErrors" => false,
47
"loginSec_blockAdminReg" => false,
48
+ "loginSec_disableAuthorScan" => false,
49
"other_hideWPVersion" => false,
50
"other_noAnonMemberComments" => false,
51
"other_scanComments" => false,
120
"loginSec_lockInvalidUsers" => false,
121
"loginSec_maskLoginErrors" => true,
122
"loginSec_blockAdminReg" => true,
123
+ "loginSec_disableAuthorScan" => true,
124
"other_hideWPVersion" => true,
125
"other_noAnonMemberComments" => true,
126
"other_scanComments" => true,
195
"loginSec_lockInvalidUsers" => false,
196
"loginSec_maskLoginErrors" => true,
197
"loginSec_blockAdminReg" => true,
198
+ "loginSec_disableAuthorScan" => true,
199
"other_hideWPVersion" => true,
200
"other_noAnonMemberComments" => true,
201
"other_scanComments" => true,
270
"loginSec_lockInvalidUsers" => false,
271
"loginSec_maskLoginErrors" => true,
272
"loginSec_blockAdminReg" => true,
273
+ "loginSec_disableAuthorScan" => true,
274
"other_hideWPVersion" => true,
275
"other_noAnonMemberComments" => true,
276
"other_scanComments" => true,
345
"loginSec_lockInvalidUsers" => true,
346
"loginSec_maskLoginErrors" => true,
347
"loginSec_blockAdminReg" => true,
348
+ "loginSec_disableAuthorScan" => true,
349
"other_hideWPVersion" => true,
350
"other_noAnonMemberComments" => true,
351
"other_scanComments" => true,
lib/wfUtils.php CHANGED
@@ -593,6 +593,13 @@ class wfUtils {
593
@setcookie($name, $value, $expire, $path);
594
}
595
}
596
}
597
598
593
@setcookie($name, $value, $expire, $path);
594
}
595
}
596
+ public static function isNginx(){
597
+ $sapi = php_sapi_name();
598
+ $serverSoft = $_SERVER['SERVER_SOFTWARE'];
599
+ if($sapi == 'fpm-fcgi' || stripos($serverSoft, 'nginx') !== false){
600
+ return true;
601
+ }
602
+ }
603
}
604
605
lib/wordfenceClass.php CHANGED
@@ -72,8 +72,7 @@ class wordfence {
72
public static function hourlyCron(){
73
global $wpdb; $p = $wpdb->base_prefix;
74
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
75
-
76
-
77
if(wfConfig::get('other_WFNet')){
78
$wfdb = new wfDB();
79
$q1 = $wfdb->querySelect("select URI from $p"."wfNet404s where ctime > unix_timestamp() - 3600 limit 1000");
@@ -126,10 +125,43 @@ class wordfence {
126
}
127
}
128
}
129
public static function dailyCron(){
130
$wfdb = new wfDB();
131
global $wpdb; $p = $wpdb->base_prefix;
132
- $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
133
try {
134
$patData = $api->call('get_known_vuln_pattern');
135
if(is_array($patData) && $patData['pat']){
@@ -225,7 +257,7 @@ class wordfence {
225
}
226
wp_clear_scheduled_hook('wordfence_daily_cron');
227
wp_clear_scheduled_hook('wordfence_hourly_cron');
228
- wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
229
wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron');
230
$db = new wfDB();
231
@@ -342,6 +374,10 @@ class wordfence {
342
add_action('validate_password_reset', 'wordfence::validatePassword', 10, 2 );
343
}
344
345
add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2);
346
add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3);
347
//html|xhtml|atom|rss2|rdf|comment|export
@@ -370,6 +406,21 @@ class wordfence {
370
}
371
}
372
}
373
public static function ajax_testAjax_callback(){
374
die("WFSCANTESTOK");
375
}
@@ -665,7 +716,7 @@ class wordfence {
665
public static function authenticateFilter($authResult){
666
$IP = wfUtils::getIP();
667
$secEnabled = wfConfig::get('loginSecurityEnabled');
668
- if($secEnabled && (! self::getLog()->isWhitelisted($IP)) ){
669
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
670
if(isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0){
671
$userDat = $_POST['wordfence_userDat'];
@@ -678,6 +729,9 @@ class wordfence {
678
} else if($_POST['wordfence_authFactor'] == $t[2]){
679
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
680
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
681
if(isset($codeResult['ok']) && $codeResult['ok']){
682
$t[2] = $codeResult['code'];
683
$t[4] = time() + 1800; //30 minutes until code expires
@@ -696,6 +750,10 @@ class wordfence {
696
if($t[0] == $userDat->ID && $t[3] == 'activated'){ //Yup, enabled, so lets send the code
697
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
698
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
699
if(isset($codeResult['ok']) && $codeResult['ok']){
700
$t[2] = $codeResult['code'];
701
$t[4] = time() + 1800; //30 minutes until code expires
@@ -714,7 +772,7 @@ class wordfence {
714
if(self::getLog()->isWhitelisted($IP)){
715
return $authResult;
716
}
717
- if(wfConfig::get('other_WFNet') && is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') && wfConfig::get('loginSec_maskLoginErrors')){
718
if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
719
self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
720
}
@@ -1374,6 +1432,9 @@ class wordfence {
1374
return array('ok' => 1);
1375
}
1376
public static function ajax_checkFalconHtaccess_callback(){
1377
$file = wfCache::getHtaccessPath();
1378
if(! $file){
1379
return array('err' => "We could not find your .htaccess file to modify it.", 'code' => wfCache::getHtaccessCode() );
@@ -1581,6 +1642,9 @@ class wordfence {
1581
} catch (Exception $e){
1582
return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . htmlentities($e->getMessage()) );
1583
}
1584
}
1585
return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
1586
}
72
public static function hourlyCron(){
73
global $wpdb; $p = $wpdb->base_prefix;
74
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
75
+
76
if(wfConfig::get('other_WFNet')){
77
$wfdb = new wfDB();
78
$q1 = $wfdb->querySelect("select URI from $p"."wfNet404s where ctime > unix_timestamp() - 3600 limit 1000");
125
}
126
}
127
}
128
+ private function keyAlert($msg){
129
+ self::alert($msg, $msg . " To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your API key by visiting http://www.wordfence.com/ Sign in, go to your dashboard,\nselect the key about to expire and click the button to renew that API key.", false);
130
+ }
131
public static function dailyCron(){
132
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
133
+ $keyData = $api->call('ping_api_key');
134
+ if(isset($keyData['_isPaidKey']) && $keyData['_isPaidKey']){
135
+ $keyExpDays = $keyData['_keyExpDays'];
136
+ $keyIsExpired = $keyData['_expired'];
137
+ if($keyExpDays > 15){
138
+ wfConfig::set('keyExp15Sent', '');
139
+ wfConfig::set('keyExp7Sent', '');
140
+ wfConfig::set('keyExp2Sent', '');
141
+ wfConfig::set('keyExp1Sent', '');
142
+ wfConfig::set('keyExpFinalSent', '');
143
+ } else if($keyExpDays <= 15 && $keyExpDays > 0){
144
+ if($keyExpDays <= 15 && $keyExpDays >= 11 && (! wfConfig::get('keyExp15Sent'))){
145
+ wfConfig::set('keyExp15Sent', 1);
146
+ self::keyAlert("Your Premium Wordfence API Key expires in less than 2 weeks.");
147
+ } else if($keyExpDays <= 7 && $keyExpDays >= 4 && (! wfConfig::get('keyExp7Sent'))){
148
+ wfConfig::set('keyExp7Sent', 1);
149
+ self::keyAlert("Your Premium Wordfence API Key expires in less than a week.");
150
+ } else if($keyExpDays == 2 && (! wfConfig::get('keyExp2Sent'))){
151
+ wfConfig::set('keyExp2Sent', 1);
152
+ self::keyAlert("Your Premium Wordfence API Key expires in 2 days.");
153
+ } else if($keyExpDays == 1 && (! wfConfig::get('keyExp1Sent'))){
154
+ wfConfig::set('keyExp1Sent', 1);
155
+ self::keyAlert("Your Premium Wordfence API Key expires in 1 day.");
156
+ }
157
+ } else if($keyIsExpired && (! wfConfig::get('keyExpFinalSent')) ){
158
+ wfConfig::set('keyExpFinalSent', 1);
159
+ self::keyAlert("Your Wordfence Premium API Key has Expired!");
160
+ }
161
+ }
162
+
163
$wfdb = new wfDB();
164
global $wpdb; $p = $wpdb->base_prefix;
165
try {
166
$patData = $api->call('get_known_vuln_pattern');
167
if(is_array($patData) && $patData['pat']){
257
}
258
wp_clear_scheduled_hook('wordfence_daily_cron');
259
wp_clear_scheduled_hook('wordfence_hourly_cron');
260
+ wp_schedule_event(time(), 'daily', 'wordfence_daily_cron'); //'daily'
261
wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron');
262
$db = new wfDB();
263
374
add_action('validate_password_reset', 'wordfence::validatePassword', 10, 2 );
375
}
376
377
+ //For debugging
378
+ //add_filter( 'cron_schedules', 'wordfence::cronAddSchedules' );
379
+
380
+ add_filter('wp_redirect', 'wordfence::wpRedirectFilter', 99, 2);
381
add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2);
382
add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3);
383
//html|xhtml|atom|rss2|rdf|comment|export
406
}
407
}
408
}
409
+ /* For debugging:
410
+ public static function cronAddSchedules($schedules){
411
+ $schedules['wfEachMinute'] = array(
412
+ 'interval' => 60,
413
+ 'display' => __( 'Once a Minute' )
414
+ );
415
+ return $schedules;
416
+ }
417
+ */
418
+ public static function wpRedirectFilter($URL, $status){
419
+ if(isset($_GET['author']) && preg_match('/^https?:\/\/[^\/]+\/author\/.+/i', $URL) && wfConfig::get('loginSec_disableAuthorScan') ){ //author query variable is present and we're about to redirect to a URL that starts with http://blah/author/...
420
+ return home_url(); //Send the user to the home URL (as opposed to site_url() which is not the home page on some sites)
421
+ }
422
+ return $URL;
423
+ }
424
public static function ajax_testAjax_callback(){
425
die("WFSCANTESTOK");
426
}
716
public static function authenticateFilter($authResult){
717
$IP = wfUtils::getIP();
718
$secEnabled = wfConfig::get('loginSecurityEnabled');
719
+ if($secEnabled && (! self::getLog()->isWhitelisted($IP)) && wfConfig::get('isPaid') ){
720
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
721
if(isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0){
722
$userDat = $_POST['wordfence_userDat'];
729
} else if($_POST['wordfence_authFactor'] == $t[2]){
730
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
731
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
732
+ if(isset($codeResult['notPaid']) && $codeResult['notPaid']){
733
+ break; //Let them sign in without two factor
734
+ }
735
if(isset($codeResult['ok']) && $codeResult['ok']){
736
$t[2] = $codeResult['code'];
737
$t[4] = time() + 1800; //30 minutes until code expires
750
if($t[0] == $userDat->ID && $t[3] == 'activated'){ //Yup, enabled, so lets send the code
751
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
752
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
753
+ if(isset($codeResult['notPaid']) && $codeResult['notPaid']){
754
+ break; //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
755
+ }
756
+
757
if(isset($codeResult['ok']) && $codeResult['ok']){
758
$t[2] = $codeResult['code'];
759
$t[4] = time() + 1800; //30 minutes until code expires
772
if(self::getLog()->isWhitelisted($IP)){
773
return $authResult;
774
}
775
+ if(wfConfig::get('other_WFNet') && is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') ){
776
if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
777
self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
778
}
1432
return array('ok' => 1);
1433
}
1434
public static function ajax_checkFalconHtaccess_callback(){
1435
+ if(wfUtils::isNginx()){
1436
+ return array('nginx' => 1);
1437
+ }
1438
$file = wfCache::getHtaccessPath();
1439
if(! $file){
1440
return array('err' => "We could not find your .htaccess file to modify it.", 'code' => wfCache::getHtaccessCode() );
1642
} catch (Exception $e){
1643
return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . htmlentities($e->getMessage()) );
1644
}
1645
+ } else {
1646
+ $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
1647
+ $res = $api->call('ping_api_key', array(), array());
1648
}
1649
return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
1650
}
lib/wordfenceConstants.php CHANGED
@@ -1,5 +1,5 @@
1
<?php
2
- define('WORDFENCE_API_VERSION', '2.9');
3
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
1
<?php
2
+ define('WORDFENCE_API_VERSION', '2.10');
3
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
lib/wordfenceHash.php CHANGED
@@ -1,7 +1,6 @@
1
<?php
2
require_once('wordfenceClass.php');
3
class wordfenceHash {
4
- private $whitespace = array("\n","\r","\t"," ");
5
private $engine = false;
6
private $db = false;
7
private $startTime = false;
@@ -213,7 +212,7 @@ class wordfenceHash {
213
} else {
214
wordfence::status(4, 'info', "Scanning: $realFile");
215
}
216
- $wfHash = $this->wfHash($realFile);
217
if($wfHash){
218
$md5 = strtoupper($wfHash[0]);
219
$shac = strtoupper($wfHash[1]);
@@ -228,55 +227,59 @@ class wordfenceHash {
228
if($this->coreEnabled){
229
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
230
$fileContents = @file_get_contents($localFile);
231
- if($fileContents && (! preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*#x2F;s', $fileContents))){
232
-
233
- $this->haveIssues['core'] = true;
234
$this->engine->addIssue(
235
'file',
236
- 1,
237
- 'coreModified' . $file . $md5,
238
- 'coreModified' . $file,
239
- 'WordPress core file modified: ' . $file,
240
- "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
241
array(
242
'file' => $file,
243
- 'cType' => 'core',
244
'canDiff' => true,
245
'canFix' => true,
246
- 'canDelete' => false
247
)
248
);
249
}
250
}
251
- }
252
- } else if(isset($this->knownFiles['plugins'][$file])){
253
- if(in_array($shac, $this->knownFiles['plugins'][$file])){
254
- $knownFile = 1;
255
- } else {
256
- if($this->pluginsEnabled){
257
- $itemName = $this->knownFiles['plugins'][$file][0];
258
- $itemVersion = $this->knownFiles['plugins'][$file][1];
259
- $cKey = $this->knownFiles['plugins'][$file][2];
260
- $this->haveIssues['plugins'] = true;
261
- $this->engine->addIssue(
262
- 'file',
263
- 2,
264
- 'modifiedplugin' . $file . $md5,
265
- 'modifiedplugin' . $file,
266
- 'Modified plugin file: ' . $file,
267
- "This file belongs to plugin \"$itemName\" version \"$itemVersion\" and has been modified from the file that is distributed by WordPress.org for this version. Please use the link to see how the file has changed. If you have modified this file yourself, you can safely ignore this warning. If you see a lot of changed files in a plugin that have been made by the author, then try uninstalling and reinstalling the plugin to force an upgrade. Doing this is a workaround for plugin authors who don't manage their code correctly. [See our FAQ on www.wordfence.com for more info]",
268
- array(
269
- 'file' => $file,
270
- 'cType' => 'plugin',
271
- 'canDiff' => true,
272
- 'canFix' => true,
273
- 'canDelete' => false,
274
- 'cName' => $itemName,
275
- 'cVersion' => $itemVersion,
276
- 'cKey' => $cKey
277
- )
278
- );
279
- }
280
281
}
282
} else if(isset($this->knownFiles['themes'][$file])){
@@ -284,28 +287,30 @@ class wordfenceHash {
284
$knownFile = 1;
285
} else {
286
if($this->themesEnabled){
287
- $itemName = $this->knownFiles['themes'][$file][0];
288
- $itemVersion = $this->knownFiles['themes'][$file][1];
289
- $cKey = $this->knownFiles['themes'][$file][2];
290
- $this->haveIssues['themes'] = true;
291
- $this->engine->addIssue(
292
- 'file',
293
- 2,
294
- 'modifiedtheme' . $file . $md5,
295
- 'modifiedtheme' . $file,
296
- 'Modified theme file: ' . $file,
297
- "This file belongs to theme \"$itemName\" version \"$itemVersion\" and has been modified from the original distribution. It is common for site owners to modify their theme files, so if you have modified this file yourself you can safely ignore this warning.",
298
- array(
299
- 'file' => $file,
300
- 'cType' => 'theme',
301
- 'canDiff' => true,
302
- 'canFix' => true,
303
- 'canDelete' => false,
304
- 'cName' => $itemName,
305
- 'cVersion' => $itemVersion,
306
- 'cKey' => $cKey
307
- )
308
- );
309
}
310
311
}
@@ -330,7 +335,7 @@ class wordfenceHash {
330
//wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $realFile");
331
}
332
}
333
- public function wfHash($file){
334
wfUtils::errorsOff();
335
$md5 = @md5_file($file, false);
336
wfUtils::errorsOn();
@@ -342,7 +347,7 @@ class wordfenceHash {
342
}
343
$ctx = hash_init('sha256');
344
while (!feof($fp)) {
345
- hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536)));
346
}
347
$shac = hash_final($ctx, false);
348
return array($md5, $shac);
@@ -354,5 +359,12 @@ class wordfenceHash {
354
}
355
return false;
356
}
357
}
358
?>
1
<?php
2
require_once('wordfenceClass.php');
3
class wordfenceHash {
4
private $engine = false;
5
private $db = false;
6
private $startTime = false;
212
} else {
213
wordfence::status(4, 'info', "Scanning: $realFile");
214
}
215
+ $wfHash = self::wfHash($realFile);
216
if($wfHash){
217
$md5 = strtoupper($wfHash[0]);
218
$shac = strtoupper($wfHash[1]);
227
if($this->coreEnabled){
228
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
229
$fileContents = @file_get_contents($localFile);
230
+ if($fileContents && (! preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*#x2F;s', $fileContents))){ //<?php
231
+ if(! $this->isSafeFile($shac)){
232
+
233
+ $this->haveIssues['core'] = true;
234
+ $this->engine->addIssue(
235
+ 'file',
236
+ 1,
237
+ 'coreModified' . $file . $md5,
238
+ 'coreModified' . $file,
239
+ 'WordPress core file modified: ' . $file,
240
+ "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
241
+ array(
242
+ 'file' => $file,
243
+ 'cType' => 'core',
244
+ 'canDiff' => true,
245
+ 'canFix' => true,
246
+ 'canDelete' => false
247
+ )
248
+ );
249
+ }
250
+ }
251
+ }
252
+ }
253
+ } else if(isset($this->knownFiles['plugins'][$file])){
254
+ if(in_array($shac, $this->knownFiles['plugins'][$file])){
255
+ $knownFile = 1;
256
+ } else {
257
+ if($this->pluginsEnabled){
258
+ if(! $this->isSafeFile($shac)){
259
+ $itemName = $this->knownFiles['plugins'][$file][0];
260
+ $itemVersion = $this->knownFiles['plugins'][$file][1];
261
+ $cKey = $this->knownFiles['plugins'][$file][2];
262
+ $this->haveIssues['plugins'] = true;
263
$this->engine->addIssue(
264
'file',
265
+ 2,
266
+ 'modifiedplugin' . $file . $md5,
267
+ 'modifiedplugin' . $file,
268
+ 'Modified plugin file: ' . $file,
269
+ "This file belongs to plugin \"$itemName\" version \"$itemVersion\" and has been modified from the file that is distributed by WordPress.org for this version. Please use the link to see how the file has changed. If you have modified this file yourself, you can safely ignore this warning. If you see a lot of changed files in a plugin that have been made by the author, then try uninstalling and reinstalling the plugin to force an upgrade. Doing this is a workaround for plugin authors who don't manage their code correctly. [See our FAQ on www.wordfence.com for more info]",
270
array(
271
'file' => $file,
272
+ 'cType' => 'plugin',
273
'canDiff' => true,
274
'canFix' => true,
275
+ 'canDelete' => false,
276
+ 'cName' => $itemName,
277
+ 'cVersion' => $itemVersion,
278
+ 'cKey' => $cKey
279
)
280
);
281
}
282
}
283
284
}
285
} else if(isset($this->knownFiles['themes'][$file])){
287
$knownFile = 1;
288
} else {
289
if($this->themesEnabled){
290
+ if(! $this->isSafeFile($shac)){
291
+ $itemName = $this->knownFiles['themes'][$file][0];
292
+ $itemVersion = $this->knownFiles['themes'][$file][1];
293
+ $cKey = $this->knownFiles['themes'][$file][2];
294
+ $this->haveIssues['themes'] = true;
295
+ $this->engine->addIssue(
296
+ 'file',
297
+ 2,
298
+ 'modifiedtheme' . $file . $md5,
299
+ 'modifiedtheme' . $file,
300
+ 'Modified theme file: ' . $file,
301
+ "This file belongs to theme \"$itemName\" version \"$itemVersion\" and has been modified from the original distribution. It is common for site owners to modify their theme files, so if you have modified this file yourself you can safely ignore this warning.",
302
+ array(
303
+ 'file' => $file,
304
+ 'cType' => 'theme',
305
+ 'canDiff' => true,
306
+ 'canFix' => true,
307
+ 'canDelete' => false,
308
+ 'cName' => $itemName,
309
+ 'cVersion' => $itemVersion,
310
+ 'cKey' => $cKey
311
+ )
312
+ );
313
+ }
314
}
315
316
}
335
//wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $realFile");
336
}
337
}
338
+ public static function wfHash($file){
339
wfUtils::errorsOff();
340
$md5 = @md5_file($file, false);
341
wfUtils::errorsOn();
347
}
348
$ctx = hash_init('sha256');
349
while (!feof($fp)) {
350
+ hash_update($ctx, str_replace( array("\n","\r","\t"," ") ,"",fread($fp, 65536)));
351
}
352
$shac = hash_final($ctx, false);
353
return array($md5, $shac);
359
}
360
return false;
361
}
362
+ private function isSafeFile($shac){
363
+ $result = $this->engine->api->call('is_safe_file', array(), array('shac' => strtoupper($shac)));
364
+ if(isset($result['isSafe']) && $result['isSafe'] == 1){
365
+ return true;
366
+ }
367
+ return false;
368
+ }
369
}
370
?>
lib/wordfenceScanner.php CHANGED
@@ -13,6 +13,7 @@ class wordfenceScanner {
13
private $startTime = false;
14
private $lastStatusTime = false;
15
private $patterns = "";
16
public function __sleep(){
17
return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned', 'startTime', 'lastStatusTime', 'patterns');
18
}
@@ -132,94 +133,52 @@ class wordfenceScanner {
132
}
133
if($isPHP || wfConfig::get('scansEnabled_scanImages') ){
134
if(strpos($data, '$allowed'.'Sites') !== false && strpos($data, "define ('VER"."SION', '1.") !== false && strpos($data, "TimThum"."b script created by") !== false){
135
- $this->addResult(array(
136
- 'type' => 'file',
137
- 'severity' => 1,
138
- 'ignoreP' => $this->path . $file,
139
- 'ignoreC' => $fileSum,
140
- 'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
141
- 'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it.",
142
- 'data' => array(
143
- 'file' => $file,
144
- 'canDiff' => false,
145
- 'canFix' => false,
146
- 'canDelete' => true
147
- )
148
- ));
149
- break;
150
} else if(strpos($file, 'lib/wordfenceScanner.php') === false && preg_match($this->patterns['sigPattern'], $data, $matches)){
151
- $this->addResult(array(
152
- 'type' => 'file',
153
- 'severity' => 1,
154
- 'ignoreP' => $this->path . $file,
155
- 'ignoreC' => $fileSum,
156
- 'shortMsg' => "This file appears to be malicious",
157
- '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;\">\"" . $matches[1] . "\"</strong>.",
158
- 'data' => array(
159
- 'file' => $file,
160
- 'canDiff' => false,
161
- 'canFix' => false,
162
- 'canDelete' => true
163
- )
164
- ));
165
- break;
166
167
}
168
- /*
169
- $longestNospace = wfUtils::longestNospace($data);
170
- if($longestNospace > 1000 && (strpos($data, $this->patterns['pat1']) !== false || preg_match('/preg_replace\([^\(]+\/[a-z]*e/', $data)) ){
171
- $this->addResult(array(
172
- 'type' => 'file',
173
- 'severity' => 1,
174
- 'ignoreP' => $this->path . $file,
175
- 'ignoreC' => $fileSum,
176
- 'shortMsg' => "This file may contain malicious executable code",
177
- 'longMsg' => "This file is a PHP executable file and contains a line $longestNospace characters long without spaces that may be encoded data along with functions that may be used to execute that code. If you know about this file you can choose to ignore it to exclude it from future scans.",
178
- 'data' => array(
179
- 'file' => $file,
180
- 'canDiff' => false,
181
- 'canFix' => false,
182
- 'canDelete' => true
183
- )
184
- ));
185
- break;
186
- }
187
- */
188
if(preg_match($this->patterns['pat2'], $data)){
189
- $this->addResult(array(
190
- 'type' => 'file',
191
- 'severity' => 1,
192
- 'ignoreP' => $this->path . $file,
193
- 'ignoreC' => $fileSum,
194
- 'shortMsg' => "This file may contain malicious executable code",
195
- 'longMsg' => "This file is a PHP executable file and contains an " . $this->patterns['word1'] . " function and " . $this->patterns['word2'] . " decoding function on the same line. This is a common technique used by hackers to hide and execute code. If you know about this file you can choose to ignore it to exclude it from future scans.",
196
- 'data' => array(
197
- 'file' => $file,
198
- 'canDiff' => false,
199
- 'canFix' => false,
200
- 'canDelete' => true
201
- )
202
- ));
203
- break;
204
- }
205
- if(wfConfig::get('scansEnabled_highSense')){
206
- $badStringFound = false;
207
- if(strpos($data, $this->patterns['badstrings'][0]) !== false){
208
- for($i = 1; $i < sizeof($this->patterns['badstrings']); $i++){
209
- if(strpos($data, $this->patterns['badstrings'][$i]) !== false){
210
- $badStringFound = $this->patterns['badstrings'][$i];
211
- break;
212
- }
213
- }
214
- }
215
- if($badStringFound){
216
$this->addResult(array(
217
'type' => 'file',
218
'severity' => 1,
219
'ignoreP' => $this->path . $file,
220
'ignoreC' => $fileSum,
221
'shortMsg' => "This file may contain malicious executable code",
222
- 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '" . $badStringFound . "' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans.",
223
'data' => array(
224
'file' => $file,
225
'canDiff' => false,
@@ -230,6 +189,36 @@ class wordfenceScanner {
230
break;
231
}
232
}
233
if(! $dontScanForURLs){
234
$this->urlHoover->hoover($file, $data);
235
}
@@ -267,39 +256,43 @@ class wordfenceScanner {
267
continue;
268
}
269
if($result['badList'] == 'goog-malware-shavar'){
270
- $this->addResult(array(
271
- 'type' => 'file',
272
- 'severity' => 1,
273
- 'ignoreP' => $this->path . $file,
274
- 'ignoreC' => md5_file($this->path . $file),
275
- 'shortMsg' => "File contains suspected malware URL: " . $this->path . $file,
276
- 'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . $this->patterns['word3'] . " when scanning files so the URL may not be visible if you view this file. The URL is: " . $result['URL'] . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
277
- 'data' => array(
278
- 'file' => $file,
279
- 'badURL' => $result['URL'],
280
- 'canDiff' => false,
281
- 'canFix' => false,
282
- 'canDelete' => true,
283
- 'gsb' => 'goog-malware-shavar'
284
- )
285
- ));
286
} else if($result['badList'] == 'googpub-phish-shavar'){
287
- $this->addResult(array(
288
- 'type' => 'file',
289
- 'severity' => 1,
290
- 'ignoreP' => $this->path . $file,
291
- 'ignoreC' => md5_file($this->path . $file),
292
- 'shortMsg' => "File contains suspected phishing URL: " . $this->path . $file,
293
- 'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . $result['URL'],
294
- 'data' => array(
295
- 'file' => $file,
296
- 'badURL' => $result['URL'],
297
- 'canDiff' => false,
298
- 'canFix' => false,
299
- 'canDelete' => true,
300
- 'gsb' => 'googpub-phish-shavar'
301
- )
302
- ));
303
}
304
}
305
}
@@ -309,23 +302,6 @@ class wordfenceScanner {
309
private function writeScanningStatus(){
310
wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " additional files at " . sprintf('%.2f', ($this->totalFilesScanned / (microtime(true) - $this->startTime))) . " per second");
311
}
312
- private function addEncIssue($ignoreP, $ignoreC, $encoding, $file){
313
- $this->addResult(array(
314
- 'type' => 'file',
315
- 'severity' => 1,
316
- 'ignoreP' => $ignoreP,
317
- 'ignoreC' => $ignoreC,
318
- 'shortMsg' => "File contains $encoding encoded programming language: " . $file,
319
- 'longMsg' => "This file contains programming language code that has been encoded using $encoding. This is often used by hackers to hide their tracks.",
320
- 'data' => array(
321
- 'file' => $file,
322
- 'canDiff' => false,
323
- 'canFix' => false,
324
- 'canDelete' => true
325
- )
326
- ));
327
-
328
- }
329
public static function containsCode($arr){
330
foreach($arr as $elem){
331
if(preg_match($this->patterns['pat3'], $elem)){
@@ -351,6 +327,18 @@ class wordfenceScanner {
351
//We don't have a results for this file so append
352
$this->results[] = $result;
353
}
354
}
355
356
?>
13
private $startTime = false;
14
private $lastStatusTime = false;
15
private $patterns = "";
16
+ private $api = false;
17
public function __sleep(){
18
return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned', 'startTime', 'lastStatusTime', 'patterns');
19
}
133
}
134
if($isPHP || wfConfig::get('scansEnabled_scanImages') ){
135
if(strpos($data, '$allowed'.'Sites') !== false && strpos($data, "define ('VER"."SION', '1.") !== false && strpos($data, "TimThum"."b script created by") !== false){
136
+ if(! $this->isSafeFile($this->path . $file)){
137
+ $this->addResult(array(
138
+ 'type' => 'file',
139
+ 'severity' => 1,
140
+ 'ignoreP' => $this->path . $file,
141
+ 'ignoreC' => $fileSum,
142
+ 'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
143
+ 'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it.",
144
+ 'data' => array(
145
+ 'file' => $file,
146
+ 'canDiff' => false,
147
+ 'canFix' => false,
148
+ 'canDelete' => true
149
+ )
150
+ ));
151
+ break;
152
+ }
153
} else if(strpos($file, 'lib/wordfenceScanner.php') === false && preg_match($this->patterns['sigPattern'], $data, $matches)){
154
+ if(! $this->isSafeFile($this->path . $file)){
155
+ $this->addResult(array(
156
+ 'type' => 'file',
157
+ 'severity' => 1,
158
+ 'ignoreP' => $this->path . $file,
159
+ 'ignoreC' => $fileSum,
160
+ 'shortMsg' => "This file appears to be malicious",
161
+ '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;\">\"" . $matches[1] . "\"</strong>.",
162
+ 'data' => array(
163
+ 'file' => $file,
164
+ 'canDiff' => false,
165
+ 'canFix' => false,
166
+ 'canDelete' => true
167
+ )
168
+ ));
169
+ break;
170
+ }
171
172
}
173
if(preg_match($this->patterns['pat2'], $data)){
174
+ if(! $this->isSafeFile($this->path . $file)){
175
$this->addResult(array(
176
'type' => 'file',
177
'severity' => 1,
178
'ignoreP' => $this->path . $file,
179
'ignoreC' => $fileSum,
180
'shortMsg' => "This file may contain malicious executable code",
181
+ 'longMsg' => "This file is a PHP executable file and contains an " . $this->patterns['word1'] . " function and " . $this->patterns['word2'] . " decoding function on the same line. This is a common technique used by hackers to hide and execute code. If you know about this file you can choose to ignore it to exclude it from future scans.",
182
'data' => array(
183
'file' => $file,
184
'canDiff' => false,
189
break;
190
}
191
}
192
+ if(wfConfig::get('scansEnabled_highSense')){
193
+ $badStringFound = false;
194
+ if(strpos($data, $this->patterns['badstrings'][0]) !== false){
195
+ for($i = 1; $i < sizeof($this->patterns['badstrings']); $i++){
196
+ if(strpos($data, $this->patterns['badstrings'][$i]) !== false){
197
+ $badStringFound = $this->patterns['badstrings'][$i];
198
+ break;
199
+ }
200
+ }
201
+ }
202
+ if($badStringFound){
203
+ if(! $this->isSafeFile($this->path . $file)){
204
+ $this->addResult(array(
205
+ 'type' => 'file',
206
+ 'severity' => 1,
207
+ 'ignoreP' => $this->path . $file,
208
+ 'ignoreC' => $fileSum,
209
+ 'shortMsg' => "This file may contain malicious executable code",
210
+ 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '" . $badStringFound . "' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans.",
211
+ 'data' => array(
212
+ 'file' => $file,
213
+ 'canDiff' => false,
214
+ 'canFix' => false,
215
+ 'canDelete' => true
216
+ )
217
+ ));
218
+ break;
219
+ }
220
+ }
221
+ }
222
if(! $dontScanForURLs){
223
$this->urlHoover->hoover($file, $data);
224
}
256
continue;
257
}
258
if($result['badList'] == 'goog-malware-shavar'){
259
+ if(! $this->isSafeFile($this->path . $file)){
260
+ $this->addResult(array(
261
+ 'type' => 'file',
262
+ 'severity' => 1,
263
+ 'ignoreP' => $this->path . $file,
264
+ 'ignoreC' => md5_file($this->path . $file),
265
+ 'shortMsg' => "File contains suspected malware URL: " . $this->path . $file,
266
+ 'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . $this->patterns['word3'] . " when scanning files so the URL may not be visible if you view this file. The URL is: " . $result['URL'] . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
267
+ 'data' => array(
268
+ 'file' => $file,
269
+ 'badURL' => $result['URL'],
270
+ 'canDiff' => false,
271
+ 'canFix' => false,
272
+ 'canDelete' => true,
273
+ 'gsb' => 'goog-malware-shavar'
274
+ )
275
+ ));
276
+ }
277
} else if($result['badList'] == 'googpub-phish-shavar'){
278
+ if(! $this->isSafeFile($this->path . $file)){
279
+ $this->addResult(array(
280
+ 'type' => 'file',
281
+ 'severity' => 1,
282
+ 'ignoreP' => $this->path . $file,
283
+ 'ignoreC' => md5_file($this->path . $file),
284
+ 'shortMsg' => "File contains suspected phishing URL: " . $this->path . $file,
285
+ 'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . $result['URL'],
286
+ 'data' => array(
287
+ 'file' => $file,
288
+ 'badURL' => $result['URL'],
289
+ 'canDiff' => false,
290
+ 'canFix' => false,
291
+ 'canDelete' => true,
292
+ 'gsb' => 'googpub-phish-shavar'
293
+ )
294
+ ));
295
+ }
296
}
297
}
298
}
302
private function writeScanningStatus(){
303
wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " additional files at " . sprintf('%.2f', ($this->totalFilesScanned / (microtime(true) - $this->startTime))) . " per second");
304
}
305
public static function containsCode($arr){
306
foreach($arr as $elem){
307
if(preg_match($this->patterns['pat3'], $elem)){
327
//We don't have a results for this file so append
328
$this->results[] = $result;
329
}
330
+ private function isSafeFile($file){
331
+ if(! $this->api){
332
+ $this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
333
+ }
334
+
335
+ $wfHash = wordfenceHash::wfHash($file);
336
+ $result = $this->api->call('is_safe_file', array(), array('shac' => strtoupper($wfHash[1])));
337
+ if(isset($result['isSafe']) && $result['isSafe'] == 1){
338
+ return true;
339
+ }
340
+ return false;
341
+ }
342
}
343
344
?>
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
=== Wordfence Security ===
2
Contributors: mmaunder
3
- Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability
4
Requires at least: 3.3.1
5
Tested up to: 3.9
6
- Stable tag: 5.0.5
7
8
- Wordfence Security is a free enterprise class security plugin that makes your site up to 50 times faster and more secure.
9
10
== Description ==
11
@@ -162,6 +162,17 @@ cause a security hole on your site.
162
163
== Changelog ==
164
165
= 5.0.5 =
166
* Fix: Removed mysql_real_escape_string because it’s deprecated. Using WP’s internal escape.
167
* Fix: Wordfence issues list would be deleted halfway through scan under certain conditions.
1
=== Wordfence Security ===
2
Contributors: mmaunder
3
+ Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm
4
Requires at least: 3.3.1
5
Tested up to: 3.9
6
+ Stable tag: 5.0.6
7
8
+ Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
10
== Description ==
11
162
163
== Changelog ==
164
165
+ = 5.0.6 =
166
+ * Feature: Prevent discovery of usernames through '?/author=N' scans. New option under login security which you can enable.
167
+ * Fix: Introduced new global hash whitelist on our servers that drastically reduces false positives in all scans especially theme and plugin scans.
168
+ * Fix: Fixed issue that corrupted .htaccess because stat cache would store file size and cause filesize() to report incorrect size when reading/writing .htaccess.
169
+ * Fix: Fixed LiteSpeed issue where Falcon Engine would not serve cached pages under LiteSpeed and LiteSpeed warned about unknown server variable in .htaccess.
170
+ * Fix: Fixed issue where Wordfence Security Network won't block known bad IP after first login attempt if "Don't let WordPress reveal valid users in login errors" option is not enabled.
171
+ * Fix: Sites installed under a directory would sometimes see Falcon not serving cached docs.
172
+ * Fix: If you are a premium customer and you have 2FA enabled and your key expires, fixed issue that may have caused you to get locked out.
173
+ * Improvement: If your Premium API key now expires, we simply downgrade you to free scanning and continue rather than disabling Wordfence.
174
+ * Improvement: Email warnings a few days before your Premium key expires so you have a chance to upgrade for uninterrupted service.
175
+
176
= 5.0.5 =
177
* Fix: Removed mysql_real_escape_string because it’s deprecated. Using WP’s internal escape.
178
* Fix: Wordfence issues list would be deleted halfway through scan under certain conditions.
wordfence.php CHANGED
@@ -4,13 +4,13 @@ Plugin Name: Wordfence Security
4
Plugin URI: http://www.wordfence.com/
5
Description: Wordfence Security - Anti-virus, Firewal and Site Speedup
6
Author: Wordfence
7
- Version: 5.0.5
8
Author URI: http://www.wordfence.com/
9
*/
10
if(defined('WP_INSTALLING') && WP_INSTALLING){
11
return;
12
}
13
- define('WORDFENCE_VERSION', '5.0.5');
14
if(get_option('wordfenceActivated') != 1){
15
add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
}
4
Plugin URI: http://www.wordfence.com/
5
Description: Wordfence Security - Anti-virus, Firewal and Site Speedup
6
Author: Wordfence
7
+ Version: 5.0.6
8
Author URI: http://www.wordfence.com/
9
*/
10
if(defined('WP_INSTALLING') && WP_INSTALLING){
11
return;
12
}
13
+ define('WORDFENCE_VERSION', '5.0.6');
14
if(get_option('wordfenceActivated') != 1){
15
add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
}