Version Description
- Fixed scans hanging on Dreamhost and other hosts.
- Made Wordfence more memory efficient.
- Wordfence scans are now broken into steps so we can scan a huge number of files, posts and comments.
- Alert emails now include IP address, hostname lookup and geographic location (city if available).
- Improved scan locking. No longer time based but uses flock() if on unix or time on Windows.
- Suppressed warnings that WF was generating.
- Improve handling of non-standard wp-content directories.
- Fix restored files were still showing as changed if they contained international characters.
- Improve permission denied message if attempting to repair a file.
- Fixed problem that caused scans to not start because some hosts take too long to look up their own name.
- Fixed issue with Wordfence menu that caused it to not appear or conflict with other menus under certain conditions.
- Upgraded to API version 1.6
- Improved geo lookup code for IP's.
- Fixed debug mode output in live status box - coloring was wrong.
- Added ajax status message to WF admin pages.
- Fixed colorbox popup so that it doesn't jump around on refresh.
Download this release
Release Info
Developer | mmaunder |
Plugin | Wordfence Security – Firewall & Malware Scan |
Version | 2.1.0 |
Comparing to | |
See all releases |
Code changes from version 2.0.7 to 2.1.0
- css/main.css +17 -1
- images/icons/ajaxRed16.gif +0 -0
- js/admin.js +27 -17
- lib/Diff.php +1 -1
- lib/Diff/Renderer/Html/Array.php +2 -2
- lib/Diff/SequenceMatcher.php +1 -1
- lib/diffResult.php +8 -1
- lib/email_genericAlert.php +1 -1
- lib/menu_scan.php +5 -1
- lib/wfConfig.php +1 -1
- lib/wfCrawl.php +1 -1
- lib/wfDB.php +2 -0
- lib/wfIssues.php +11 -4
- lib/wfLog.php +4 -79
- lib/wfScanEngine.php +380 -208
- lib/wfUtils.php +152 -14
- lib/wordfenceClass.php +96 -139
- lib/wordfenceConstants.php +1 -1
- lib/wordfenceHash.php +22 -3
- lib/wordfenceScanner.php +18 -15
- lib/wordfenceURLHoover.php +17 -4
- readme.txt +19 -1
- wfscan.php +18 -8
- wordfence.php +2 -3
css/main.css
CHANGED
@@ -268,4 +268,20 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
268 |
.wferror {
|
269 |
color: #F00;
|
270 |
}
|
271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
.wferror {
|
269 |
color: #F00;
|
270 |
}
|
271 |
+
#wordfenceWorking {
|
272 |
+
padding: 2px 8px 2px 24px;
|
273 |
+
z-index: 100000;
|
274 |
+
position: fixed;
|
275 |
+
right: 2px;
|
276 |
+
bottom: 2px;
|
277 |
+
border: 1px solid #000;
|
278 |
+
background-color: #F00;
|
279 |
+
color: #FFF;
|
280 |
+
font-size: 12px;
|
281 |
+
font-weight: bold;
|
282 |
+
font-family: Arial;
|
283 |
+
text-align: center;
|
284 |
+
background-image: url('../images/icons/ajaxRed16.gif');
|
285 |
+
background-position: 2px 2px;
|
286 |
+
background-repeat: no-repeat;
|
287 |
+
}
|
images/icons/ajaxRed16.gif
ADDED
Binary file
|
js/admin.js
CHANGED
@@ -63,8 +63,16 @@ window['wordfenceAdmin'] = {
|
|
63 |
}
|
64 |
jQuery(document).bind('cbox_closed', function(){ self.colorboxIsOpen = false; self.colorboxServiceQueue(); });
|
65 |
}
|
|
|
66 |
|
67 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
startActivityLogUpdates: function(){
|
69 |
var self = this;
|
70 |
setInterval(function(){
|
@@ -79,7 +87,7 @@ window['wordfenceAdmin'] = {
|
|
79 |
var self = this;
|
80 |
this.ajax('wordfence_activityLogUpdate', {
|
81 |
lastctime: this.lastALogCtime
|
82 |
-
}, function(res){ self.doneUpdateActivityLog(res); }, function(){ self.activityLogUpdatePending = false; });
|
83 |
|
84 |
},
|
85 |
doneUpdateActivityLog: function(res){
|
@@ -216,7 +224,7 @@ window['wordfenceAdmin'] = {
|
|
216 |
this.ajax('wordfence_ticker', {
|
217 |
alsoGet: alsoGet,
|
218 |
otherParams: otherParams
|
219 |
-
}, function(res){ self.handleTickerReturn(res); }, function(){ self.tickerUpdatePending = false; });
|
220 |
},
|
221 |
handleTickerReturn: function(res){
|
222 |
this.tickerUpdatePending = false;
|
@@ -314,8 +322,7 @@ window['wordfenceAdmin'] = {
|
|
314 |
}
|
315 |
});
|
316 |
}
|
317 |
-
}
|
318 |
-
);
|
319 |
},
|
320 |
activateWF: function(key){
|
321 |
jQuery('.wfAjax24').show();
|
@@ -441,7 +448,7 @@ window['wordfenceAdmin'] = {
|
|
441 |
}
|
442 |
return true;
|
443 |
},
|
444 |
-
ajax: function(action, data, cb, cbErr){
|
445 |
if(typeof(data) == 'string'){
|
446 |
if(data.length > 0){
|
447 |
data += '&';
|
@@ -455,12 +462,16 @@ window['wordfenceAdmin'] = {
|
|
455 |
cbErr = function(){};
|
456 |
}
|
457 |
var self = this;
|
|
|
|
|
|
|
458 |
jQuery.ajax({
|
459 |
type: 'POST',
|
460 |
url: WordfenceAdminVars.ajaxURL,
|
461 |
dataType: "json",
|
462 |
data: data,
|
463 |
success: function(json){
|
|
|
464 |
if(json && json.nonce){
|
465 |
self.nonce = json.nonce;
|
466 |
}
|
@@ -469,7 +480,7 @@ window['wordfenceAdmin'] = {
|
|
469 |
}
|
470 |
cb(json);
|
471 |
},
|
472 |
-
error: cbErr
|
473 |
});
|
474 |
},
|
475 |
colorbox: function(width, heading, body){
|
@@ -495,11 +506,12 @@ window['wordfenceAdmin'] = {
|
|
495 |
}, function(res){ self.doneDeleteFile(res); });
|
496 |
},
|
497 |
doneDeleteFile: function(res){
|
|
|
|
|
498 |
if(res.ok){
|
499 |
-
var self = this;
|
500 |
this.loadIssues(function(){ self.colorbox('400px', "Success deleting file", "The file " + res.file + " containing " + res.filesize + " bytes was successfully deleted."); });
|
501 |
-
} else if(res.
|
502 |
-
this.loadIssues();
|
503 |
}
|
504 |
},
|
505 |
restoreFile: function(issueID){
|
@@ -509,26 +521,24 @@ window['wordfenceAdmin'] = {
|
|
509 |
}, function(res){ self.doneRestoreFile(res); });
|
510 |
},
|
511 |
doneRestoreFile: function(res){
|
512 |
-
this
|
513 |
if(res.ok){
|
514 |
-
this.colorbox("400px", "File restored OK", "The file " + res.file + " was restored succesfully.");
|
|
|
|
|
515 |
}
|
516 |
},
|
517 |
deleteIssue: function(id){
|
518 |
var self = this;
|
519 |
this.ajax('wordfence_deleteIssue', { id: id }, function(res){
|
520 |
self.loadIssues();
|
521 |
-
if(res.errMsg){
|
522 |
-
self.colorbox('400px', "An error occured", res.errMsg);
|
523 |
-
}
|
524 |
});
|
525 |
},
|
526 |
updateIssueStatus: function(id, st){
|
527 |
var self = this;
|
528 |
this.ajax('wordfence_updateIssueStatus', { id: id, 'status': st }, function(res){
|
529 |
-
|
530 |
-
|
531 |
-
self.colorbox('400px', "An error occured", res.errMsg);
|
532 |
}
|
533 |
});
|
534 |
},
|
63 |
}
|
64 |
jQuery(document).bind('cbox_closed', function(){ self.colorboxIsOpen = false; self.colorboxServiceQueue(); });
|
65 |
}
|
66 |
+
this.showLoading();
|
67 |
|
68 |
},
|
69 |
+
showLoading: function(){
|
70 |
+
this.removeLoading();
|
71 |
+
jQuery('<div id="wordfenceWorking">Wordfence is working...</div>').appendTo('body');
|
72 |
+
},
|
73 |
+
removeLoading: function(){
|
74 |
+
jQuery('#wordfenceWorking').remove();
|
75 |
+
},
|
76 |
startActivityLogUpdates: function(){
|
77 |
var self = this;
|
78 |
setInterval(function(){
|
87 |
var self = this;
|
88 |
this.ajax('wordfence_activityLogUpdate', {
|
89 |
lastctime: this.lastALogCtime
|
90 |
+
}, function(res){ self.doneUpdateActivityLog(res); }, function(){ self.activityLogUpdatePending = false; }, true);
|
91 |
|
92 |
},
|
93 |
doneUpdateActivityLog: function(res){
|
224 |
this.ajax('wordfence_ticker', {
|
225 |
alsoGet: alsoGet,
|
226 |
otherParams: otherParams
|
227 |
+
}, function(res){ self.handleTickerReturn(res); }, function(){ self.tickerUpdatePending = false; }, true);
|
228 |
},
|
229 |
handleTickerReturn: function(res){
|
230 |
this.tickerUpdatePending = false;
|
322 |
}
|
323 |
});
|
324 |
}
|
325 |
+
}, false, false);
|
|
|
326 |
},
|
327 |
activateWF: function(key){
|
328 |
jQuery('.wfAjax24').show();
|
448 |
}
|
449 |
return true;
|
450 |
},
|
451 |
+
ajax: function(action, data, cb, cbErr, noLoading){
|
452 |
if(typeof(data) == 'string'){
|
453 |
if(data.length > 0){
|
454 |
data += '&';
|
462 |
cbErr = function(){};
|
463 |
}
|
464 |
var self = this;
|
465 |
+
if(! noLoading){
|
466 |
+
this.showLoading();
|
467 |
+
}
|
468 |
jQuery.ajax({
|
469 |
type: 'POST',
|
470 |
url: WordfenceAdminVars.ajaxURL,
|
471 |
dataType: "json",
|
472 |
data: data,
|
473 |
success: function(json){
|
474 |
+
self.removeLoading();
|
475 |
if(json && json.nonce){
|
476 |
self.nonce = json.nonce;
|
477 |
}
|
480 |
}
|
481 |
cb(json);
|
482 |
},
|
483 |
+
error: function(){ self.removeLoading(); cbErr(); }
|
484 |
});
|
485 |
},
|
486 |
colorbox: function(width, heading, body){
|
506 |
}, function(res){ self.doneDeleteFile(res); });
|
507 |
},
|
508 |
doneDeleteFile: function(res){
|
509 |
+
var cb = false;
|
510 |
+
var self = this;
|
511 |
if(res.ok){
|
|
|
512 |
this.loadIssues(function(){ self.colorbox('400px', "Success deleting file", "The file " + res.file + " containing " + res.filesize + " bytes was successfully deleted."); });
|
513 |
+
} else if(res.cerrorMsg){
|
514 |
+
this.loadIssues(function(){ self.colorbox('400px', 'An error occured', res.cerrorMsg); });
|
515 |
}
|
516 |
},
|
517 |
restoreFile: function(issueID){
|
521 |
}, function(res){ self.doneRestoreFile(res); });
|
522 |
},
|
523 |
doneRestoreFile: function(res){
|
524 |
+
var self = this;
|
525 |
if(res.ok){
|
526 |
+
this.loadIssues(function(){ self.colorbox("400px", "File restored OK", "The file " + res.file + " was restored succesfully."); });
|
527 |
+
} else if(res.cerrorMsg){
|
528 |
+
this.loadIssues(function(){ self.colorbox('400px', 'An error occured', res.cerrorMsg); });
|
529 |
}
|
530 |
},
|
531 |
deleteIssue: function(id){
|
532 |
var self = this;
|
533 |
this.ajax('wordfence_deleteIssue', { id: id }, function(res){
|
534 |
self.loadIssues();
|
|
|
|
|
|
|
535 |
});
|
536 |
},
|
537 |
updateIssueStatus: function(id, st){
|
538 |
var self = this;
|
539 |
this.ajax('wordfence_updateIssueStatus', { id: id, 'status': st }, function(res){
|
540 |
+
if(res.ok){
|
541 |
+
self.loadIssues();
|
|
|
542 |
}
|
543 |
});
|
544 |
},
|
lib/Diff.php
CHANGED
@@ -173,4 +173,4 @@ class Diff
|
|
173 |
$this->groupedCodes = $sequenceMatcher->getGroupedOpcodes();
|
174 |
return $this->groupedCodes;
|
175 |
}
|
176 |
-
}
|
173 |
$this->groupedCodes = $sequenceMatcher->getGroupedOpcodes();
|
174 |
return $this->groupedCodes;
|
175 |
}
|
176 |
+
}
|
lib/Diff/Renderer/Html/Array.php
CHANGED
@@ -219,6 +219,6 @@ class Diff_Renderer_Html_Array extends Diff_Renderer_Abstract
|
|
219 |
*/
|
220 |
private function htmlSafe($string)
|
221 |
{
|
222 |
-
return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
|
223 |
}
|
224 |
-
}
|
219 |
*/
|
220 |
private function htmlSafe($string)
|
221 |
{
|
222 |
+
return @htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
|
223 |
}
|
224 |
+
}
|
lib/Diff/SequenceMatcher.php
CHANGED
@@ -739,4 +739,4 @@ class Diff_SequenceMatcher
|
|
739 |
return 1;
|
740 |
}
|
741 |
}
|
742 |
-
}
|
739 |
return 1;
|
740 |
}
|
741 |
}
|
742 |
+
}
|
lib/diffResult.php
CHANGED
@@ -30,7 +30,14 @@
|
|
30 |
?>
|
31 |
</table>
|
32 |
|
33 |
-
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
|
36 |
<div class="diffFooter">© 2011 Wordfence — Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</a>
|
30 |
?>
|
31 |
</table>
|
32 |
|
33 |
+
<?php
|
34 |
+
if($diffResult){
|
35 |
+
echo $diffResult;
|
36 |
+
} else {
|
37 |
+
echo "<br />There are no differences between the original file and the file in the repository.";
|
38 |
+
}
|
39 |
+
|
40 |
+
?>
|
41 |
|
42 |
|
43 |
<div class="diffFooter">© 2011 Wordfence — Visit <a href="http://wordfence.com/">Wordfence.com</a> for help, security updates and more.</a>
|
lib/email_genericAlert.php
CHANGED
@@ -2,7 +2,7 @@ This alert was generated by Wordfence on "<?php echo $blogName; ?>" at <?php ech
|
|
2 |
|
3 |
|
4 |
<?php echo $alertMsg; ?>
|
5 |
-
|
6 |
|
7 |
--
|
8 |
To change your alert options for Wordfence, visit:
|
2 |
|
3 |
|
4 |
<?php echo $alertMsg; ?>
|
5 |
+
<?php if($IPMsg){ echo "\n$IPMsg\n"; } ?>
|
6 |
|
7 |
--
|
8 |
To change your alert options for Wordfence, visit:
|
lib/menu_scan.php
CHANGED
@@ -43,7 +43,11 @@
|
|
43 |
foreach($events as $e){
|
44 |
if(strpos($e['msg'], 'SUM_') !== 0){
|
45 |
if( $debugOn || $e['level'] < 4){
|
46 |
-
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
}
|
49 |
$newestItem = $e['ctime'];
|
43 |
foreach($events as $e){
|
44 |
if(strpos($e['msg'], 'SUM_') !== 0){
|
45 |
if( $debugOn || $e['level'] < 4){
|
46 |
+
$typeClass = '';
|
47 |
+
if($debugOn){
|
48 |
+
$typeClass = ' wf' . $e['type'];
|
49 |
+
}
|
50 |
+
echo '<div class="wfActivityLine' . $typeClass . '">[' . date('M d H:i:s', $e['ctime']) . '] ' . $e['msg'] . '</div>';
|
51 |
}
|
52 |
}
|
53 |
$newestItem = $e['ctime'];
|
lib/wfConfig.php
CHANGED
@@ -368,7 +368,7 @@ class wfConfig {
|
|
368 |
}
|
369 |
public static function set($key, $val){
|
370 |
if(is_array($val)){
|
371 |
-
$trace=debug_backtrace(); $caller=array_shift($trace); error_log("wfConfig::set() got array as second param. Please use
|
372 |
}
|
373 |
|
374 |
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
|
368 |
}
|
369 |
public static function set($key, $val){
|
370 |
if(is_array($val)){
|
371 |
+
$trace=debug_backtrace(); $caller=array_shift($trace); error_log("wfConfig::set() got array as second param. Please use set_ser(). " . $caller['file'] . " line " . $caller['line']);
|
372 |
}
|
373 |
|
374 |
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
|
lib/wfCrawl.php
CHANGED
@@ -22,7 +22,7 @@ class wfCrawl {
|
|
22 |
}
|
23 |
}
|
24 |
$wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
25 |
-
$host =
|
26 |
if(! $host){
|
27 |
$db->query("insert into $table (IP, patternSig, status, lastUpdate, PTR) values (%s, UNHEX(MD5('%s')), '%s', unix_timestamp(), '%s') ON DUPLICATE KEY UPDATE status='%s', lastUpdate=unix_timestamp(), PTR='%s'", $IPn, $hostPattern, 'noPTR', '', 'noPTR', '');
|
28 |
return false;
|
22 |
}
|
23 |
}
|
24 |
$wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
25 |
+
$host = wfUtils::reverseLookup($IP);
|
26 |
if(! $host){
|
27 |
$db->query("insert into $table (IP, patternSig, status, lastUpdate, PTR) values (%s, UNHEX(MD5('%s')), '%s', unix_timestamp(), '%s') ON DUPLICATE KEY UPDATE status='%s', lastUpdate=unix_timestamp(), PTR='%s'", $IPn, $hostPattern, 'noPTR', '', 'noPTR', '');
|
28 |
return false;
|
lib/wfDB.php
CHANGED
@@ -37,6 +37,7 @@ class wfDB {
|
|
37 |
}
|
38 |
mysql_select_db($this->dbname, $dbh);
|
39 |
$this->dbh = $dbh;
|
|
|
40 |
} else {
|
41 |
$handleKey = md5($dbhost . $dbuser . $dbpassword . $dbname);
|
42 |
if(isset(self::$dbhCache[$handleKey])){
|
@@ -51,6 +52,7 @@ class wfDB {
|
|
51 |
mysql_select_db($this->dbname, $dbh);
|
52 |
self::$dbhCache[$handleKey] = $dbh;
|
53 |
$this->dbh = self::$dbhCache[$handleKey];
|
|
|
54 |
}
|
55 |
}
|
56 |
}
|
37 |
}
|
38 |
mysql_select_db($this->dbname, $dbh);
|
39 |
$this->dbh = $dbh;
|
40 |
+
$this->query("SET NAMES 'utf8'");
|
41 |
} else {
|
42 |
$handleKey = md5($dbhost . $dbuser . $dbpassword . $dbname);
|
43 |
if(isset(self::$dbhCache[$handleKey])){
|
52 |
mysql_select_db($this->dbname, $dbh);
|
53 |
self::$dbhCache[$handleKey] = $dbh;
|
54 |
$this->dbh = self::$dbhCache[$handleKey];
|
55 |
+
$this->query("SET NAMES 'utf8'");
|
56 |
}
|
57 |
}
|
58 |
}
|
lib/wfIssues.php
CHANGED
@@ -1,18 +1,25 @@
|
|
1 |
<?php
|
2 |
require_once('wfUtils.php');
|
3 |
class wfIssues {
|
|
|
|
|
|
|
4 |
private $updateCalled = false;
|
5 |
-
public $lastError = '';
|
6 |
private $issuesTable = '';
|
7 |
private $newIssues = array();
|
8 |
public $totalIssues = 0;
|
9 |
public $totalCriticalIssues = 0;
|
10 |
public $totalWarningIssues = 0;
|
11 |
-
|
|
|
|
|
12 |
public function __construct(){
|
13 |
global $wpdb;
|
14 |
$this->issuesTable = $wpdb->base_prefix . 'wfIssues';
|
15 |
}
|
|
|
|
|
|
|
16 |
public function addIssue($type, $severity,
|
17 |
|
18 |
$ignoreP, /* some piece of data used for md5 for permanent ignores */
|
@@ -30,12 +37,12 @@ class wfIssues {
|
|
30 |
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return false; }
|
31 |
}
|
32 |
|
33 |
-
$this->totalIssues++;
|
34 |
if($severity == 1){
|
35 |
$this->totalCriticalIssues++;
|
36 |
} else if($severity == 2){
|
37 |
$this->totalWarningIssues++;
|
38 |
}
|
|
|
39 |
$this->newIssues[] = array(
|
40 |
'type' => $type,
|
41 |
'severity' => $severity,
|
@@ -192,7 +199,7 @@ class wfIssues {
|
|
192 |
}
|
193 |
$arr = wfConfig::get_ser('wf_summaryItems', array());
|
194 |
//$arr['scanTimeAgo'] = wfUtils::makeTimeAgo(sprintf('%.0f', time() - $arr['scanTime']));
|
195 |
-
$arr['scanRunning'] =
|
196 |
$arr['scheduledScansEnabled'] = wfConfig::get('scheduledScansEnabled');
|
197 |
$secsToGo = wp_next_scheduled('wordfence_scheduled_scan') - time();
|
198 |
if($secsToGo < 1){
|
1 |
<?php
|
2 |
require_once('wfUtils.php');
|
3 |
class wfIssues {
|
4 |
+
private $db = false;
|
5 |
+
|
6 |
+
//Properties that are serialized on sleep:
|
7 |
private $updateCalled = false;
|
|
|
8 |
private $issuesTable = '';
|
9 |
private $newIssues = array();
|
10 |
public $totalIssues = 0;
|
11 |
public $totalCriticalIssues = 0;
|
12 |
public $totalWarningIssues = 0;
|
13 |
+
public function __sleep(){ //Same order here as vars above
|
14 |
+
return array('updateCalled', 'issuesTable', 'newIssues', 'totalIssues', 'totalCriticalIssues', 'totalWarningIssues');
|
15 |
+
}
|
16 |
public function __construct(){
|
17 |
global $wpdb;
|
18 |
$this->issuesTable = $wpdb->base_prefix . 'wfIssues';
|
19 |
}
|
20 |
+
public function __wakeup(){
|
21 |
+
$this->db = new wfDB();
|
22 |
+
}
|
23 |
public function addIssue($type, $severity,
|
24 |
|
25 |
$ignoreP, /* some piece of data used for md5 for permanent ignores */
|
37 |
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return false; }
|
38 |
}
|
39 |
|
|
|
40 |
if($severity == 1){
|
41 |
$this->totalCriticalIssues++;
|
42 |
} else if($severity == 2){
|
43 |
$this->totalWarningIssues++;
|
44 |
}
|
45 |
+
$this->totalIssues++;
|
46 |
$this->newIssues[] = array(
|
47 |
'type' => $type,
|
48 |
'severity' => $severity,
|
199 |
}
|
200 |
$arr = wfConfig::get_ser('wf_summaryItems', array());
|
201 |
//$arr['scanTimeAgo'] = wfUtils::makeTimeAgo(sprintf('%.0f', time() - $arr['scanTime']));
|
202 |
+
$arr['scanRunning'] = wfUtils::isScanRunning() ? '1' : '0';
|
203 |
$arr['scheduledScansEnabled'] = wfConfig::get('scheduledScansEnabled');
|
204 |
$secsToGo = wp_next_scheduled('wordfence_scheduled_scan') - time();
|
205 |
if($secsToGo < 1){
|
lib/wfLog.php
CHANGED
@@ -4,7 +4,6 @@ require_once('wfUtils.php');
|
|
4 |
require_once('wfBrowscap.php');
|
5 |
class wfLog {
|
6 |
private $hitsTable = '';
|
7 |
-
private $locsTable = '';
|
8 |
private $apiKey = '';
|
9 |
private $wp_version = '';
|
10 |
private $db = false;
|
@@ -16,13 +15,11 @@ class wfLog {
|
|
16 |
global $wpdb;
|
17 |
$this->hitsTable = $wpdb->base_prefix . 'wfHits';
|
18 |
$this->loginsTable = $wpdb->base_prefix . 'wfLogins';
|
19 |
-
$this->locsTable = $wpdb->base_prefix . 'wfLocs';
|
20 |
$this->blocksTable = $wpdb->base_prefix . 'wfBlocks';
|
21 |
$this->lockOutTable = $wpdb->base_prefix . 'wfLockedOut';
|
22 |
$this->leechTable = $wpdb->base_prefix . 'wfLeechers';
|
23 |
$this->badLeechersTable = $wpdb->base_prefix . 'wfBadLeechers';
|
24 |
$this->scanTable = $wpdb->base_prefix . 'wfScanners';
|
25 |
-
$this->reverseTable = $wpdb->base_prefix . 'wfReverseCache';
|
26 |
$this->throttleTable = $wpdb->base_prefix . 'wfThrottleLog';
|
27 |
$this->statusTable = $wpdb->base_prefix . 'wfStatus';
|
28 |
}
|
@@ -419,61 +416,8 @@ class wfLog {
|
|
419 |
$IPs[] = $res['IP'];
|
420 |
}
|
421 |
}
|
422 |
-
$
|
423 |
-
|
424 |
-
$toResolve = array();
|
425 |
-
foreach($IPs as $IP){
|
426 |
-
$r1 = $this->getDB()->query("select IP, ctime, failed, city, region, countryName, countryCode, lat, lon, unix_timestamp() - ctime as age from " . $this->locsTable . " where IP=%s", $IP);
|
427 |
-
if($r1){
|
428 |
-
if($row = mysql_fetch_assoc($r1)){
|
429 |
-
if($row['age'] > WORDFENCE_MAX_IPLOC_AGE){
|
430 |
-
$this->getDB()->query("delete from " . $this->locsTable . " where IP=%s", $row['IP']);
|
431 |
-
} else {
|
432 |
-
if($row['failed'] == 1){
|
433 |
-
$IPLocs[$IP] = false;
|
434 |
-
} else {
|
435 |
-
$IPLocs[$IP] = $row;
|
436 |
-
}
|
437 |
-
}
|
438 |
-
}
|
439 |
-
}
|
440 |
-
if(! isset($IPLocs[$IP])){
|
441 |
-
$toResolve[] = $IP;
|
442 |
-
}
|
443 |
-
}
|
444 |
-
if(sizeof($toResolve) > 0){
|
445 |
-
$api = new wfAPI($this->apiKey, $this->wp_version);
|
446 |
-
$freshIPs = $api->call('resolve_ips', array(), array(
|
447 |
-
'ips' => implode(',', $toResolve)
|
448 |
-
));
|
449 |
-
if(is_array($freshIPs)){
|
450 |
-
foreach($freshIPs as $IP => $value){
|
451 |
-
if($value == 'failed'){
|
452 |
-
$this->getDB()->query("insert IGNORE into " . $this->locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", $IP);
|
453 |
-
$IPLocs[$IP] = false;
|
454 |
-
} else {
|
455 |
-
$this->getDB()->query("insert IGNORE into " . $this->locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
|
456 |
-
$IP,
|
457 |
-
$value[3], //city
|
458 |
-
$value[2], //region
|
459 |
-
$value[1], //countryName
|
460 |
-
$value[0],//countryCode
|
461 |
-
$value[4],//lat
|
462 |
-
$value[5]//lon
|
463 |
-
);
|
464 |
-
$IPLocs[$IP] = array(
|
465 |
-
'IP' => $IP,
|
466 |
-
'city' => $value[3],
|
467 |
-
'region' => $value[2],
|
468 |
-
'countryName' => $value[1],
|
469 |
-
'countryCode' => $value[0],
|
470 |
-
'lat' => $value[4],
|
471 |
-
'lon' => $value[5]
|
472 |
-
);
|
473 |
-
}
|
474 |
-
}
|
475 |
-
}
|
476 |
-
}
|
477 |
foreach($results as &$res){
|
478 |
if(isset($IPLocs[$res['IP']])){
|
479 |
$res['loc'] = $IPLocs[$res['IP']];
|
@@ -540,7 +484,7 @@ class wfLog {
|
|
540 |
if($action == 'block'){
|
541 |
$IP = wfUtils::getIP();
|
542 |
if(wfConfig::get('alertOn_block')){
|
543 |
-
wordfence::alert("Blocking IP $IP", "Wordfence has blocked IP address $IP.\
|
544 |
}
|
545 |
wordfence::status(2, 'info', "Blocking IP $IP. $reason");
|
546 |
$this->blockIP($IP, $reason);
|
@@ -595,25 +539,6 @@ class wfLog {
|
|
595 |
}
|
596 |
return self::$gbSafeCache[$cacheKey]; //return cached value
|
597 |
}
|
598 |
-
public function reverseLookup($IP){
|
599 |
-
$IPn = wfUtils::inet_aton($IP);
|
600 |
-
$host = $this->getDB()->querySingle("select host from " . $this->reverseTable . " where IP=%s and unix_timestamp() - lastUpdate < %d", $IPn, WORDFENCE_REVERSE_LOOKUP_CACHE_TIME);
|
601 |
-
if(! $host){
|
602 |
-
$ptr = implode(".", array_reverse(explode(".",$IP))) . ".in-addr.arpa";
|
603 |
-
$host = dns_get_record($ptr, DNS_PTR);
|
604 |
-
if($host == null){
|
605 |
-
$host = 'NONE';
|
606 |
-
} else {
|
607 |
-
$host = $host[0]['target'];
|
608 |
-
}
|
609 |
-
$this->getDB()->query("insert into " . $this->reverseTable . " (IP, host, lastUpdate) values (%s, '%s', unix_timestamp()) ON DUPLICATE KEY UPDATE host='%s', lastUpdate=unix_timestamp()", $IPn, $host, $host);
|
610 |
-
}
|
611 |
-
if($host == 'NONE'){
|
612 |
-
return '';
|
613 |
-
} else {
|
614 |
-
return $host;
|
615 |
-
}
|
616 |
-
}
|
617 |
public function addStatus($level, $type, $msg){
|
618 |
//$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg;
|
619 |
$this->getDB()->query("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", sprintf('%.6f', microtime(true)), $level, $type, $msg);
|
@@ -636,7 +561,7 @@ class wfLog {
|
|
636 |
return $results;
|
637 |
}
|
638 |
public function getSummaryEvents(){
|
639 |
-
$res = $this->getDB()->query("select ctime, level, type, msg from " . $this->statusTable . " where level = 10 order by ctime desc limit 100"
|
640 |
$results = array();
|
641 |
$lastTime = false;
|
642 |
while($rec = mysql_fetch_assoc($res)){
|
4 |
require_once('wfBrowscap.php');
|
5 |
class wfLog {
|
6 |
private $hitsTable = '';
|
|
|
7 |
private $apiKey = '';
|
8 |
private $wp_version = '';
|
9 |
private $db = false;
|
15 |
global $wpdb;
|
16 |
$this->hitsTable = $wpdb->base_prefix . 'wfHits';
|
17 |
$this->loginsTable = $wpdb->base_prefix . 'wfLogins';
|
|
|
18 |
$this->blocksTable = $wpdb->base_prefix . 'wfBlocks';
|
19 |
$this->lockOutTable = $wpdb->base_prefix . 'wfLockedOut';
|
20 |
$this->leechTable = $wpdb->base_prefix . 'wfLeechers';
|
21 |
$this->badLeechersTable = $wpdb->base_prefix . 'wfBadLeechers';
|
22 |
$this->scanTable = $wpdb->base_prefix . 'wfScanners';
|
|
|
23 |
$this->throttleTable = $wpdb->base_prefix . 'wfThrottleLog';
|
24 |
$this->statusTable = $wpdb->base_prefix . 'wfStatus';
|
25 |
}
|
416 |
$IPs[] = $res['IP'];
|
417 |
}
|
418 |
}
|
419 |
+
$IPLocs = wfUtils::getIPsGeo($IPs); //Creates an array with IP as key and data as value
|
420 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
421 |
foreach($results as &$res){
|
422 |
if(isset($IPLocs[$res['IP']])){
|
423 |
$res['loc'] = $IPLocs[$res['IP']];
|
484 |
if($action == 'block'){
|
485 |
$IP = wfUtils::getIP();
|
486 |
if(wfConfig::get('alertOn_block')){
|
487 |
+
wordfence::alert("Blocking IP $IP", "Wordfence has blocked IP address $IP.\nThe reason is: \"$reason\".", $IP);
|
488 |
}
|
489 |
wordfence::status(2, 'info', "Blocking IP $IP. $reason");
|
490 |
$this->blockIP($IP, $reason);
|
539 |
}
|
540 |
return self::$gbSafeCache[$cacheKey]; //return cached value
|
541 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
542 |
public function addStatus($level, $type, $msg){
|
543 |
//$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg;
|
544 |
$this->getDB()->query("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", sprintf('%.6f', microtime(true)), $level, $type, $msg);
|
561 |
return $results;
|
562 |
}
|
563 |
public function getSummaryEvents(){
|
564 |
+
$res = $this->getDB()->query("select ctime, level, type, msg from " . $this->statusTable . " where level = 10 order by ctime desc limit 100");
|
565 |
$results = array();
|
566 |
$lastTime = false;
|
567 |
while($rec = mysql_fetch_assoc($res)){
|
lib/wfScanEngine.php
CHANGED
@@ -7,27 +7,67 @@ require_once('wfIssues.php');
|
|
7 |
require_once('wfDB.php');
|
8 |
require_once('wfUtils.php');
|
9 |
class wfScanEngine {
|
10 |
-
private $i = false;
|
11 |
private $api = false;
|
12 |
-
private $
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
private $wp_version = false;
|
14 |
private $apiKey = false;
|
15 |
private $errorStopped = false;
|
16 |
-
private $dictWords = array();
|
17 |
private $startTime = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
public function __construct(){
|
19 |
$this->startTime = time();
|
20 |
$this->i = new wfIssues();
|
|
|
|
|
21 |
$this->wp_version = wfUtils::getWPVersion();
|
22 |
$this->apiKey = wfConfig::get('apiKey');
|
23 |
$this->api = new wfAPI($this->apiKey, $this->wp_version);
|
24 |
include('wfDict.php'); //$dictWords
|
25 |
$this->dictWords = $dictWords;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
}
|
27 |
public function go(){
|
28 |
-
$this->status(1, 'info', "Initializing scan. Memory available: " . @ini_get('memory_limit') );
|
29 |
-
$this->i->deleteNew();
|
30 |
-
|
31 |
try {
|
32 |
$this->doScan();
|
33 |
if(! $this->errorStopped){
|
@@ -42,79 +82,60 @@ class wfScanEngine {
|
|
42 |
}
|
43 |
wordfence::scheduleNextScan(true);
|
44 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
public function emailNewIssues(){
|
46 |
$this->i->emailNewIssues();
|
47 |
}
|
48 |
private function doScan(){
|
49 |
-
$this->
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
$unknownFiles = $this->scanKnownFiles();
|
56 |
-
if($this->errorStopped){
|
57 |
-
return;
|
58 |
-
}
|
59 |
-
if(wfConfig::get('scansEnabled_fileContents')){
|
60 |
-
$this->scanFileContents($unknownFiles);
|
61 |
-
if($this->errorStopped){
|
62 |
-
return;
|
63 |
-
}
|
64 |
-
}
|
65 |
-
if(wfConfig::get('scansEnabled_posts')){
|
66 |
-
$this->scanPosts();
|
67 |
-
if($this->errorStopped){
|
68 |
-
return;
|
69 |
}
|
70 |
-
|
71 |
-
if(wfConfig::get('scansEnabled_comments')){
|
72 |
-
$this->scanComments();
|
73 |
-
if($this->errorStopped){ return; }
|
74 |
-
}
|
75 |
-
if(wfConfig::get('scansEnabled_passwds')){
|
76 |
-
$this->scanAllPasswords();
|
77 |
-
if($this->errorStopped){ return; }
|
78 |
-
}
|
79 |
-
if(wfConfig::get('scansEnabled_diskSpace')){
|
80 |
-
$this->scanDiskSpace();
|
81 |
-
if($this->errorStopped){ return; }
|
82 |
-
}
|
83 |
-
if(wfConfig::get('scansEnabled_dns')){
|
84 |
-
$this->scanDNSChanges();
|
85 |
-
if($this->errorStopped){ return; }
|
86 |
-
}
|
87 |
-
if(wfConfig::get('scansEnabled_oldVersions')){
|
88 |
-
$this->scanOldVersions();
|
89 |
-
if($this->errorStopped){ return; }
|
90 |
}
|
91 |
$summary = $this->i->getSummaryItems();
|
92 |
$this->status(1, 'info', "Scan Complete. Scanned " . $summary['totalFiles'] . " files, " . $summary['totalPlugins'] . " plugins, " . $summary['totalThemes'] . " themes, " . ($summary['totalPages'] + $summary['totalPosts']) . " pages, " . $summary['totalComments'] . " comments and " . $summary['totalRows'] . " records in " . (time() - $this->startTime) . " seconds.");
|
93 |
-
if($this->i->totalIssues
|
94 |
$this->status(10, 'info', "SUM_FINAL:Scan complete. You have " . $this->i->totalIssues . " new issues to fix. See below for details.");
|
95 |
} else {
|
96 |
$this->status(10, 'info', "SUM_FINAL:Scan complete. Congratulations, there were no problems found.");
|
97 |
}
|
98 |
return;
|
99 |
}
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
);
|
|
|
|
|
108 |
if(wfConfig::get('scansEnabled_core')){
|
109 |
-
$coreScanEnabled = true;
|
110 |
-
$statusIDX['core'] = wordfence::statusStart("Comparing core WordPress files against originals in repository");
|
111 |
} else {
|
112 |
wordfence::statusDisabled("Skipping core scan");
|
113 |
}
|
114 |
if(wfConfig::get('isPaid')){
|
115 |
if(wfConfig::get('scansEnabled_plugins')){
|
116 |
-
$pluginScanEnabled = true;
|
117 |
-
$statusIDX['plugin'] = wordfence::statusStart("Premium: Comparing plugin files against originals in repository");
|
118 |
} else {
|
119 |
wordfence::statusDisabled("Skipping comparing plugin files against originals in repository");
|
120 |
}
|
@@ -123,8 +144,8 @@ class wfScanEngine {
|
|
123 |
}
|
124 |
if(wfConfig::get('isPaid')){
|
125 |
if(wfConfig::get('scansEnabled_themes')){
|
126 |
-
$themeScanEnabled = true;
|
127 |
-
$statusIDX['theme'] = wordfence::statusStart("Premium: Comparing theme files against originals in repository");
|
128 |
} else {
|
129 |
wordfence::statusDisabled("Skipping comparing theme files against originals in repository");
|
130 |
}
|
@@ -133,20 +154,19 @@ class wfScanEngine {
|
|
133 |
}
|
134 |
|
135 |
if(wfConfig::get('scansEnabled_malware')){
|
136 |
-
$statusIDX['unknown'] = wordfence::statusStart("Scanning for known malware files");
|
137 |
-
$malwareScanEnabled = true;
|
138 |
} else {
|
139 |
wordfence::statusDisabled("Skipping malware scan");
|
140 |
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
|
141 |
}
|
142 |
-
|
143 |
-
if((! $summaryUpdateRequired) && (! ($coreScanEnabled || $pluginScanEnabled || $themeScanEnabled || $malwareScanEnabled))){
|
144 |
$this->status(2, 'info', "Finishing this stage because we don't have to do a summary update and we don't need to do a core, plugin, theme or malware scan.");
|
145 |
return array();
|
146 |
}
|
147 |
|
148 |
//CORE SCAN
|
149 |
-
$hasher = new wordfenceHash(strlen(ABSPATH));
|
150 |
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
|
151 |
$baseContents = scandir(ABSPATH);
|
152 |
$scanOutside = wfConfig::get('other_scanOutside');
|
@@ -162,13 +182,18 @@ class wfScanEngine {
|
|
162 |
}
|
163 |
}
|
164 |
$this->status(2, 'info', "Hashing your WordPress files for comparison against originals.");
|
165 |
-
$
|
|
|
|
|
|
|
|
|
|
|
166 |
$this->status(2, 'info', "Done hash. Updating summary items.");
|
167 |
-
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($hasher->totalData));
|
168 |
-
$this->i->updateSummaryItem('totalFiles', $hasher->totalFiles);
|
169 |
-
$this->i->updateSummaryItem('totalDirs', $hasher->totalDirs);
|
170 |
-
$this->i->updateSummaryItem('linesOfPHP', $hasher->linesOfPHP);
|
171 |
-
$this->i->updateSummaryItem('linesOfJCH', $hasher->linesOfJCH);
|
172 |
|
173 |
if(! function_exists( 'get_plugins')){
|
174 |
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
@@ -185,7 +210,7 @@ class wfScanEngine {
|
|
185 |
$this->status(2, 'info', "Found " . sizeof($themes) . " themes");
|
186 |
$this->i->updateSummaryItem('totalThemes', sizeof($themes));
|
187 |
//Return now because we needed to do a summary update but don't have any other work to do.
|
188 |
-
if(! ($coreScanEnabled || $pluginScanEnabled || $themeScanEnabled || $malwareScanEnabled)){
|
189 |
$this->status(2, 'info', "Finishing up because we have done our required summary update and don't need to do a core, plugin, theme or malware scan.");
|
190 |
return array();
|
191 |
}
|
@@ -201,13 +226,13 @@ class wfScanEngine {
|
|
201 |
}
|
202 |
$this->status(2, 'info', "Sending request to Wordfence servers to do main scan.");
|
203 |
$scanData = array(
|
204 |
-
'pluginScanEnabled' => $pluginScanEnabled,
|
205 |
-
'themeScanEnabled' => $themeScanEnabled,
|
206 |
-
'coreScanEnabled' => $coreScanEnabled,
|
207 |
-
'malwareScanEnabled' => $malwareScanEnabled,
|
208 |
'plugins' => $plugins,
|
209 |
'themes' => $themes,
|
210 |
-
'hashes' => wordfenceHash::bin2hex($hashes)
|
211 |
);
|
212 |
$result1 = $this->api->call('main_scan', array(), array(
|
213 |
'data' => json_encode($scanData)
|
@@ -241,29 +266,35 @@ class wfScanEngine {
|
|
241 |
}
|
242 |
}
|
243 |
foreach($haveIssues as $type => $have){
|
244 |
-
if($statusIDX[$type] !== false){
|
245 |
-
wordfence::statusEnd($statusIDX[$type], $have);
|
246 |
}
|
247 |
}
|
248 |
-
|
|
|
249 |
}
|
250 |
-
private function
|
251 |
-
$statusIDX = wordfence::statusStart('Scanning file contents for infections and vulnerabilities');
|
252 |
-
$
|
253 |
-
if(! is_array($unknownFiles)){
|
254 |
-
$unknownFiles = array();
|
255 |
-
}
|
256 |
-
$this->
|
257 |
-
$
|
258 |
$this->status(2, 'info', "Starting scan of file contents");
|
259 |
-
|
|
|
|
|
|
|
|
|
260 |
$this->status(2, 'info', "Done file contents scan");
|
261 |
-
if($scanner->errorMsg){
|
262 |
-
$this->errorStop($scanner->errorMsg);
|
263 |
}
|
|
|
264 |
$haveIssues = false;
|
265 |
$haveIssuesGSB = false;
|
266 |
-
foreach($
|
267 |
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
268 |
if($this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data'])){
|
269 |
if(empty($issue['data']['gsb']) === false){
|
@@ -273,38 +304,48 @@ class wfScanEngine {
|
|
273 |
}
|
274 |
}
|
275 |
}
|
276 |
-
|
277 |
-
wordfence::statusEnd($
|
|
|
278 |
}
|
279 |
-
private function
|
280 |
-
$statusIDX = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
281 |
$blogsToScan = $this->getBlogsToScan('posts');
|
282 |
$wfdb = new wfDB();
|
283 |
-
$
|
284 |
-
$postDat = array();
|
285 |
foreach($blogsToScan as $blog){
|
286 |
$q1 = $wfdb->query("select ID from " . $blog['table'] . " where post_type IN ('page', 'post') and post_status = 'publish'");
|
287 |
while($idRow = mysql_fetch_assoc($q1)){
|
288 |
-
$
|
289 |
-
$h->hoover($blog['blog_id'] . '-' . $row['ID'], $row['post_title'] . ' ' . $row['post_content']);
|
290 |
-
$postDat[$blog['blog_id'] . '-' . $row['ID']] = array(
|
291 |
-
'contentMD5' => md5($row['post_content']),
|
292 |
-
'title' => $row['post_title'],
|
293 |
-
'type' => $row['post_type'],
|
294 |
-
'postDate' => $row['post_date'],
|
295 |
-
'isMultisite' => $blog['isMultisite'],
|
296 |
-
'domain' => $blog['domain'],
|
297 |
-
'path' => $blog['path'],
|
298 |
-
'blog_id' => $blog['blog_id']
|
299 |
-
);
|
300 |
-
|
301 |
}
|
302 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
$this->status(2, 'info', "Examining URLs found in posts we scanned for dangerous websites");
|
304 |
-
$hooverResults = $
|
305 |
$this->status(2, 'info', "Done examining URls");
|
306 |
-
if($
|
307 |
-
$this->errorStop($
|
308 |
wordfence::statusEndErr();
|
309 |
return;
|
310 |
|
@@ -314,8 +355,8 @@ class wfScanEngine {
|
|
314 |
$arr = explode('-', $idString);
|
315 |
$blogID = $arr[0];
|
316 |
$postID = $arr[1];
|
317 |
-
$uctype = ucfirst($
|
318 |
-
$type = $
|
319 |
foreach($hresults as $result){
|
320 |
if($result['badList'] == 'goog-malware-shavar'){
|
321 |
$shortMsg = "$uctype contains a suspected malware URL.";
|
@@ -332,19 +373,19 @@ class wfScanEngine {
|
|
332 |
switch_to_blog($blogID);
|
333 |
}
|
334 |
$ignoreP = $idString;
|
335 |
-
$ignoreC = $idString . $
|
336 |
if($this->addIssue('postBadURL', 1, $ignoreP, $ignoreC, $shortMsg, $longMsg, array(
|
337 |
'postID' => $postID,
|
338 |
'badURL' => $result['URL'],
|
339 |
-
'postTitle' => $
|
340 |
-
'type' => $
|
341 |
'uctype' => $uctype,
|
342 |
'permalink' => get_permalink($postID),
|
343 |
'editPostLink' => get_edit_post_link($postID),
|
344 |
-
'postDate' => $
|
345 |
-
'isMultisite' => $
|
346 |
-
'domain' => $
|
347 |
-
'path' => $
|
348 |
'blog_id' => $blogID
|
349 |
))){
|
350 |
$haveIssues = true;
|
@@ -354,51 +395,16 @@ class wfScanEngine {
|
|
354 |
}
|
355 |
}
|
356 |
}
|
357 |
-
|
358 |
-
|
359 |
-
public function isBadComment($author, $email, $url, $IP, $content){
|
360 |
-
$content = $author . ' ' . $email . ' ' . $url . ' ' . $IP . ' ' . $content;
|
361 |
-
$cDesc = '';
|
362 |
-
if($author){
|
363 |
-
$cDesc = "Author: $author ";
|
364 |
-
}
|
365 |
-
if($email){
|
366 |
-
$cDesc .= "Email: $email ";
|
367 |
-
}
|
368 |
-
$cDesc = "Source IP: $IP ";
|
369 |
-
$this->status(2, 'info', "Scanning comment with $cDesc");
|
370 |
-
|
371 |
-
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
372 |
-
$h->hoover(1, $content);
|
373 |
-
$hooverResults = $h->getBaddies();
|
374 |
-
if($h->errorMsg){
|
375 |
-
return false;
|
376 |
-
}
|
377 |
-
if(sizeof($hooverResults) > 0 && isset($hooverResults[1])){
|
378 |
-
$hresults = $hooverResults[1];
|
379 |
-
foreach($hresults as $result){
|
380 |
-
if($result['badList'] == 'goog-malware-shavar'){
|
381 |
-
$this->status(2, 'info', "Marking comment as spam for containing a malware URL. Comment has $cDesc");
|
382 |
-
return true;
|
383 |
-
} else if($result['badList'] == 'googpub-phish-shavar'){
|
384 |
-
$this->status(2, 'info', "Marking comment as spam for containing a phishing URL. Comment has $cDesc");
|
385 |
-
return true;
|
386 |
-
} else {
|
387 |
-
//A list type that may be new and the plugin has not been upgraded yet.
|
388 |
-
continue;
|
389 |
-
}
|
390 |
-
}
|
391 |
-
}
|
392 |
-
$this->status(2, 'info', "Scanned comment with $cDesc");
|
393 |
-
return false;
|
394 |
}
|
395 |
-
private function
|
396 |
-
$statusIDX = wordfence::statusStart('Scanning comments for URL\'s in Google\'s Safe Browsing List');
|
397 |
-
|
398 |
-
$
|
399 |
-
$
|
400 |
-
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
401 |
$blogsToScan = $this->getBlogsToScan('comments');
|
|
|
402 |
foreach($blogsToScan as $blog){
|
403 |
$q1 = $wfdb->query("select comment_ID from " . $blog['table'] . " where comment_approved=1");
|
404 |
if( ! $q1){
|
@@ -410,23 +416,34 @@ class wfScanEngine {
|
|
410 |
}
|
411 |
|
412 |
while($idRow = mysql_fetch_assoc($q1)){
|
413 |
-
$
|
414 |
-
$h->hoover($blog['blog_id'] . '-' . $row['comment_ID'], $row['comment_author_url'] . ' ' . $row['comment_author'] . ' ' . $row['comment_content']);
|
415 |
-
$commentDat[$blog['blog_id'] . '-' . $row['comment_ID']] = array(
|
416 |
-
'contentMD5' => md5($row['comment_content'] . $row['comment_author'] . $row['comment_author_url']),
|
417 |
-
'author' => $row['comment_author'],
|
418 |
-
'type' => ($row['comment_type'] ? $row['comment_type'] : 'comment'),
|
419 |
-
'date' => $row['comment_date'],
|
420 |
-
'isMultisite' => $blog['isMultisite'],
|
421 |
-
'domain' => $blog['domain'],
|
422 |
-
'path' => $blog['path'],
|
423 |
-
'blog_id' => $blog['blog_id']
|
424 |
-
);
|
425 |
}
|
426 |
}
|
427 |
-
|
428 |
-
|
429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
430 |
wordfence::statusEndErr();
|
431 |
return;
|
432 |
}
|
@@ -435,8 +452,8 @@ class wfScanEngine {
|
|
435 |
$arr = explode('-', $idString);
|
436 |
$blogID = $arr[0];
|
437 |
$commentID = $arr[1];
|
438 |
-
$uctype = ucfirst($
|
439 |
-
$type = $
|
440 |
foreach($hresults as $result){
|
441 |
if($result['badList'] == 'goog-malware-shavar'){
|
442 |
$shortMsg = "$uctype contains a suspected malware URL.";
|
@@ -452,18 +469,18 @@ class wfScanEngine {
|
|
452 |
switch_to_blog($blogID);
|
453 |
}
|
454 |
$ignoreP = $idString;
|
455 |
-
$ignoreC = $idString . '-' . $
|
456 |
if($this->addIssue('commentBadURL', 1, $ignoreP, $ignoreC, $shortMsg, $longMsg, array(
|
457 |
'commentID' => $commentID,
|
458 |
'badURL' => $result['URL'],
|
459 |
-
'author' => $
|
460 |
'type' => $type,
|
461 |
'uctype' => $uctype,
|
462 |
'editCommentLink' => get_edit_comment_link($commentID),
|
463 |
-
'commentDate' => $
|
464 |
-
'isMultisite' => $
|
465 |
-
'domain' => $
|
466 |
-
'path' => $
|
467 |
'blog_id' => $blogID
|
468 |
))){
|
469 |
$haveIssues = true;
|
@@ -473,7 +490,43 @@ class wfScanEngine {
|
|
473 |
}
|
474 |
}
|
475 |
}
|
476 |
-
wordfence::statusEnd($statusIDX, $haveIssues);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
}
|
478 |
public function getBlogsToScan($table){
|
479 |
$wfdb = new wfDB();
|
@@ -518,8 +571,8 @@ class wfScanEngine {
|
|
518 |
}
|
519 |
return false;
|
520 |
}
|
521 |
-
private function
|
522 |
-
$statusIDX = wordfence::statusStart('Scanning for weak passwords');
|
523 |
global $wpdb;
|
524 |
$ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users");
|
525 |
$haveIssues = false;
|
@@ -528,11 +581,11 @@ class wfScanEngine {
|
|
528 |
$isWeak = $this->scanUserPassword($user->ID);
|
529 |
if($isWeak){ $haveIssues = true; }
|
530 |
}
|
531 |
-
wordfence::statusEnd($statusIDX, $haveIssues);
|
532 |
}
|
533 |
public function scanUserPassword($userID){
|
534 |
require_once( ABSPATH . 'wp-includes/class-phpass.php');
|
535 |
-
$
|
536 |
$userDat = get_userdata($userID);
|
537 |
$this->status(2, 'info', "Checking password strength of user '" . $userDat->user_login . "'");
|
538 |
$shortMsg = "";
|
@@ -552,7 +605,7 @@ class wfScanEngine {
|
|
552 |
}
|
553 |
$haveIssue = false;
|
554 |
for($i = 0; $i < sizeof($words); $i++){
|
555 |
-
if($
|
556 |
$this->status(2, 'info', "Adding issue " . $shortMsg);
|
557 |
if($this->addIssue('easyPassword', $level, $userDat->ID, $userDat->ID . '-' . $userDat->user_pass, $shortMsg, $longMsg, array(
|
558 |
'ID' => $userDat->ID,
|
@@ -570,13 +623,13 @@ class wfScanEngine {
|
|
570 |
$this->status(2, 'info', "Completed checking password strength of user '" . $userDat->user_login . "'");
|
571 |
return $haveIssue;
|
572 |
}
|
573 |
-
private function
|
574 |
-
$statusIDX = wordfence::statusStart("Scanning to check available disk space");
|
575 |
$total = disk_total_space('.');
|
576 |
$free = disk_free_space('.');
|
577 |
$this->status(2, 'info', "Total space: $total Free space: $free");
|
578 |
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
|
579 |
-
wordfence::statusEnd($statusIDX, false);
|
580 |
return;
|
581 |
}
|
582 |
$level = false;
|
@@ -587,22 +640,22 @@ class wfScanEngine {
|
|
587 |
} else if($spaceLeft < 1.5){
|
588 |
$level = 2;
|
589 |
} else {
|
590 |
-
wordfence::statusEnd($statusIDX, false);
|
591 |
return;
|
592 |
}
|
593 |
if($this->addIssue('diskSpace', $level, 'diskSpace' . $level, 'diskSpace' . $level, "You have $spaceLeft" . "% disk space remaining", "You only have $spaceLeft" . "% of your disk space remaining. Please free up disk space or your website may stop serving requests.", array(
|
594 |
'spaceLeft' => $spaceLeft ))){
|
595 |
-
wordfence::statusEnd($statusIDX, true);
|
596 |
} else {
|
597 |
-
wordfence::statusEnd($statusIDX, false);
|
598 |
}
|
599 |
}
|
600 |
-
private function
|
601 |
if(! function_exists('dns_get_record')){
|
602 |
$this->status(1, 'info', "Skipping DNS scan because this system does not support dns_get_record()");
|
603 |
return;
|
604 |
}
|
605 |
-
$statusIDX = wordfence::statusStart("Scanning DNS for unauthorized changes");
|
606 |
$haveIssues = false;
|
607 |
$home = get_home_url();
|
608 |
if(preg_match('/https?:\/\/([^\/]+)/i', $home, $matches)){
|
@@ -694,10 +747,10 @@ class wfScanEngine {
|
|
694 |
|
695 |
wfConfig::set('wf_dnsLogged', 1);
|
696 |
}
|
697 |
-
wordfence::statusEnd($statusIDX, $haveIssues);
|
698 |
}
|
699 |
-
private function
|
700 |
-
$statusIDX = wordfence::statusStart("Scanning for old themes, plugins and core files");
|
701 |
if(! function_exists( 'get_preferred_from_update_core')){
|
702 |
require_once(ABSPATH . 'wp-admin/includes/update.php');
|
703 |
}
|
@@ -753,7 +806,7 @@ class wfScanEngine {
|
|
753 |
|
754 |
}
|
755 |
}
|
756 |
-
wordfence::statusEnd($statusIDX, $haveIssues);
|
757 |
}
|
758 |
private function errorStop($msg){
|
759 |
$this->errorStopped = true;
|
@@ -766,6 +819,125 @@ class wfScanEngine {
|
|
766 |
private function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
|
767 |
return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
|
768 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
769 |
}
|
770 |
|
771 |
?>
|
7 |
require_once('wfDB.php');
|
8 |
require_once('wfUtils.php');
|
9 |
class wfScanEngine {
|
|
|
10 |
private $api = false;
|
11 |
+
private $dictWords = array();
|
12 |
+
|
13 |
+
//Beginning of serialized properties on sleep
|
14 |
+
private $hasher = false;
|
15 |
+
private $hashes = false;
|
16 |
+
private $jobList = array();
|
17 |
+
private $i = false;
|
18 |
private $wp_version = false;
|
19 |
private $apiKey = false;
|
20 |
private $errorStopped = false;
|
|
|
21 |
private $startTime = 0;
|
22 |
+
private $scanStep = 0;
|
23 |
+
private $maxExecTime = 10; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
|
24 |
+
private $malwareScanEnabled = false;
|
25 |
+
private $pluginScanEnabled = false;
|
26 |
+
private $coreScanEnabled = false;
|
27 |
+
private $themeScanEnabled = false;
|
28 |
+
private $unknownFiles = array();
|
29 |
+
private $fileContentsResults = false;
|
30 |
+
private $scanner = false;
|
31 |
+
private $scanQueue = array();
|
32 |
+
private $hoover = false;
|
33 |
+
private $scanData = array();
|
34 |
+
private $statusIDX = array(
|
35 |
+
'core' => false,
|
36 |
+
'plugin' => false,
|
37 |
+
'theme' => false,
|
38 |
+
'unknown' => false
|
39 |
+
);
|
40 |
+
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
41 |
+
return array('hasher', 'hashes', 'jobList', 'i', 'wp_version', 'apiKey', 'errorStopped', 'startTime', 'scanStep', 'maxExecTime', 'malwareScanEnabled', 'pluginScanEnabled', 'coreScanEnabled', 'themeScanEnabled', 'unknownFiles', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX');
|
42 |
+
}
|
43 |
public function __construct(){
|
44 |
$this->startTime = time();
|
45 |
$this->i = new wfIssues();
|
46 |
+
$this->i->deleteNew();
|
47 |
+
$this->cycleStartTime = time();
|
48 |
$this->wp_version = wfUtils::getWPVersion();
|
49 |
$this->apiKey = wfConfig::get('apiKey');
|
50 |
$this->api = new wfAPI($this->apiKey, $this->wp_version);
|
51 |
include('wfDict.php'); //$dictWords
|
52 |
$this->dictWords = $dictWords;
|
53 |
+
foreach(array('init', 'main', 'finish') as $op){ $this->jobList[] = 'knownFiles_' . $op; };
|
54 |
+
foreach(array('fileContents', 'posts', 'comments', 'passwds', 'dns', 'diskSpace', 'oldVersions') as $scanType){
|
55 |
+
if(wfConfig::get('scansEnabled_' . $scanType)){
|
56 |
+
if(method_exists($this, 'scan_' . $scanType . '_init')){
|
57 |
+
foreach(array('init', 'main', 'finish') as $op){ $this->jobList[] = $scanType . '_' . $op; };
|
58 |
+
} else {
|
59 |
+
$this->jobList[] = $scanType;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
public function __wakeup(){
|
65 |
+
$this->cycleStartTime = time();
|
66 |
+
$this->api = new wfAPI($this->apiKey, $this->wp_version);
|
67 |
+
include('wfDict.php'); //$dictWords
|
68 |
+
$this->dictWords = $dictWords;
|
69 |
}
|
70 |
public function go(){
|
|
|
|
|
|
|
71 |
try {
|
72 |
$this->doScan();
|
73 |
if(! $this->errorStopped){
|
82 |
}
|
83 |
wordfence::scheduleNextScan(true);
|
84 |
}
|
85 |
+
public function forkIfNeeded(){
|
86 |
+
if(time() - $this->cycleStartTime > $this->maxExecTime){
|
87 |
+
wordfence::status(2, 'info', "Forking during hash scan to ensure continuity.");
|
88 |
+
$this->fork();
|
89 |
+
}
|
90 |
+
}
|
91 |
+
public function fork(){
|
92 |
+
wfConfig::set_ser('wfsd_engine', $this);
|
93 |
+
wfUtils::clearScanLock();
|
94 |
+
self::startScan(true);
|
95 |
+
exit(0);
|
96 |
+
}
|
97 |
public function emailNewIssues(){
|
98 |
$this->i->emailNewIssues();
|
99 |
}
|
100 |
private function doScan(){
|
101 |
+
while(sizeof($this->jobList) > 0){
|
102 |
+
$jobName = $this->jobList[0];
|
103 |
+
call_user_func(array($this, 'scan_' . $jobName));
|
104 |
+
array_shift($this->jobList); //only shift once we're done because we may pause halfway through a job and need to pick up where we left off
|
105 |
+
if($this->errorStopped){
|
106 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
}
|
108 |
+
$this->fork();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
}
|
110 |
$summary = $this->i->getSummaryItems();
|
111 |
$this->status(1, 'info', "Scan Complete. Scanned " . $summary['totalFiles'] . " files, " . $summary['totalPlugins'] . " plugins, " . $summary['totalThemes'] . " themes, " . ($summary['totalPages'] + $summary['totalPosts']) . " pages, " . $summary['totalComments'] . " comments and " . $summary['totalRows'] . " records in " . (time() - $this->startTime) . " seconds.");
|
112 |
+
if($this->i->totalIssues > 0){
|
113 |
$this->status(10, 'info', "SUM_FINAL:Scan complete. You have " . $this->i->totalIssues . " new issues to fix. See below for details.");
|
114 |
} else {
|
115 |
$this->status(10, 'info', "SUM_FINAL:Scan complete. Congratulations, there were no problems found.");
|
116 |
}
|
117 |
return;
|
118 |
}
|
119 |
+
public function getCurrentJob(){
|
120 |
+
return $this->jobList[0];
|
121 |
+
}
|
122 |
+
private function scan_knownFiles_init(){
|
123 |
+
$this->status(1, 'info', "Contacting Wordfence to initiate scan");
|
124 |
+
$this->api->call('log_scan', array(), array());
|
125 |
+
if($this->api->errorMsg){
|
126 |
+
$this->errorStop($this->api->errorMsg);
|
127 |
+
return;
|
128 |
+
}
|
129 |
if(wfConfig::get('scansEnabled_core')){
|
130 |
+
$this->coreScanEnabled = true;
|
131 |
+
$this->statusIDX['core'] = wordfence::statusStart("Comparing core WordPress files against originals in repository");
|
132 |
} else {
|
133 |
wordfence::statusDisabled("Skipping core scan");
|
134 |
}
|
135 |
if(wfConfig::get('isPaid')){
|
136 |
if(wfConfig::get('scansEnabled_plugins')){
|
137 |
+
$this->pluginScanEnabled = true;
|
138 |
+
$this->statusIDX['plugin'] = wordfence::statusStart("Premium: Comparing plugin files against originals in repository");
|
139 |
} else {
|
140 |
wordfence::statusDisabled("Skipping comparing plugin files against originals in repository");
|
141 |
}
|
144 |
}
|
145 |
if(wfConfig::get('isPaid')){
|
146 |
if(wfConfig::get('scansEnabled_themes')){
|
147 |
+
$this->themeScanEnabled = true;
|
148 |
+
$this->statusIDX['theme'] = wordfence::statusStart("Premium: Comparing theme files against originals in repository");
|
149 |
} else {
|
150 |
wordfence::statusDisabled("Skipping comparing theme files against originals in repository");
|
151 |
}
|
154 |
}
|
155 |
|
156 |
if(wfConfig::get('scansEnabled_malware')){
|
157 |
+
$this->statusIDX['unknown'] = wordfence::statusStart("Scanning for known malware files");
|
158 |
+
$this->malwareScanEnabled = true;
|
159 |
} else {
|
160 |
wordfence::statusDisabled("Skipping malware scan");
|
161 |
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
|
162 |
}
|
163 |
+
if((! $this->i->summaryUpdateRequired()) && (! ($this->coreScanEnabled || $this->pluginScanEnabled || $this->themeScanEnabled || $this->malwareScanEnabled))){
|
|
|
164 |
$this->status(2, 'info', "Finishing this stage because we don't have to do a summary update and we don't need to do a core, plugin, theme or malware scan.");
|
165 |
return array();
|
166 |
}
|
167 |
|
168 |
//CORE SCAN
|
169 |
+
$this->hasher = new wordfenceHash(strlen(ABSPATH));
|
170 |
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
|
171 |
$baseContents = scandir(ABSPATH);
|
172 |
$scanOutside = wfConfig::get('other_scanOutside');
|
182 |
}
|
183 |
}
|
184 |
$this->status(2, 'info', "Hashing your WordPress files for comparison against originals.");
|
185 |
+
$this->hasher->buildFileQueue(ABSPATH, $includeInScan);
|
186 |
+
}
|
187 |
+
private function scan_knownFiles_main(){
|
188 |
+
$this->hashes = $this->hasher->genHashes($this);
|
189 |
+
}
|
190 |
+
private function scan_knownFiles_finish(){
|
191 |
$this->status(2, 'info', "Done hash. Updating summary items.");
|
192 |
+
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($this->hasher->totalData));
|
193 |
+
$this->i->updateSummaryItem('totalFiles', $this->hasher->totalFiles);
|
194 |
+
$this->i->updateSummaryItem('totalDirs', $this->hasher->totalDirs);
|
195 |
+
$this->i->updateSummaryItem('linesOfPHP', $this->hasher->linesOfPHP);
|
196 |
+
$this->i->updateSummaryItem('linesOfJCH', $this->hasher->linesOfJCH);
|
197 |
|
198 |
if(! function_exists( 'get_plugins')){
|
199 |
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
210 |
$this->status(2, 'info', "Found " . sizeof($themes) . " themes");
|
211 |
$this->i->updateSummaryItem('totalThemes', sizeof($themes));
|
212 |
//Return now because we needed to do a summary update but don't have any other work to do.
|
213 |
+
if(! ($this->coreScanEnabled || $this->pluginScanEnabled || $this->themeScanEnabled || $this->malwareScanEnabled)){
|
214 |
$this->status(2, 'info', "Finishing up because we have done our required summary update and don't need to do a core, plugin, theme or malware scan.");
|
215 |
return array();
|
216 |
}
|
226 |
}
|
227 |
$this->status(2, 'info', "Sending request to Wordfence servers to do main scan.");
|
228 |
$scanData = array(
|
229 |
+
'pluginScanEnabled' => $this->pluginScanEnabled,
|
230 |
+
'themeScanEnabled' => $this->themeScanEnabled,
|
231 |
+
'coreScanEnabled' => $this->coreScanEnabled,
|
232 |
+
'malwareScanEnabled' => $this->malwareScanEnabled,
|
233 |
'plugins' => $plugins,
|
234 |
'themes' => $themes,
|
235 |
+
'hashes' => wordfenceHash::bin2hex($this->hashes)
|
236 |
);
|
237 |
$result1 = $this->api->call('main_scan', array(), array(
|
238 |
'data' => json_encode($scanData)
|
266 |
}
|
267 |
}
|
268 |
foreach($haveIssues as $type => $have){
|
269 |
+
if($this->statusIDX[$type] !== false){
|
270 |
+
wordfence::statusEnd($this->statusIDX[$type], $have);
|
271 |
}
|
272 |
}
|
273 |
+
|
274 |
+
$this->unknownFiles = $result1['unknownFiles'];
|
275 |
}
|
276 |
+
private function scan_fileContents_init(){
|
277 |
+
$this->statusIDX['infect'] = wordfence::statusStart('Scanning file contents for infections and vulnerabilities');
|
278 |
+
$this->statusIDX['GSB'] = wordfence::statusStart('Scanning files for URLs in Google\'s Safe Browsing List');
|
279 |
+
if(! is_array($this->unknownFiles)){
|
280 |
+
$this->unknownFiles = array();
|
281 |
+
}
|
282 |
+
$this->scanner = new wordfenceScanner($this->apiKey, $this->wp_version, $this->unknownFiles, ABSPATH);
|
283 |
+
$this->unknownFiles = false;
|
284 |
$this->status(2, 'info', "Starting scan of file contents");
|
285 |
+
}
|
286 |
+
private function scan_fileContents_main(){
|
287 |
+
$this->fileContentsResults = $this->scanner->scan($this);
|
288 |
+
}
|
289 |
+
private function scan_fileContents_finish(){
|
290 |
$this->status(2, 'info', "Done file contents scan");
|
291 |
+
if($this->scanner->errorMsg){
|
292 |
+
$this->errorStop($this->scanner->errorMsg);
|
293 |
}
|
294 |
+
$this->scanner = null;
|
295 |
$haveIssues = false;
|
296 |
$haveIssuesGSB = false;
|
297 |
+
foreach($this->fileContentsResults as $issue){
|
298 |
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
299 |
if($this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data'])){
|
300 |
if(empty($issue['data']['gsb']) === false){
|
304 |
}
|
305 |
}
|
306 |
}
|
307 |
+
$this->fileContentsResults = null;
|
308 |
+
wordfence::statusEnd($this->statusIDX['infect'], $haveIssues);
|
309 |
+
wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
|
310 |
}
|
311 |
+
private function scan_posts_init(){
|
312 |
+
$this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
313 |
$blogsToScan = $this->getBlogsToScan('posts');
|
314 |
$wfdb = new wfDB();
|
315 |
+
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
|
|
316 |
foreach($blogsToScan as $blog){
|
317 |
$q1 = $wfdb->query("select ID from " . $blog['table'] . " where post_type IN ('page', 'post') and post_status = 'publish'");
|
318 |
while($idRow = mysql_fetch_assoc($q1)){
|
319 |
+
$this->scanQueue[] = array($blog, $idRow['ID']);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
}
|
321 |
}
|
322 |
+
}
|
323 |
+
private function scan_posts_main(){
|
324 |
+
$wfdb = new wfDB();
|
325 |
+
while($elem = array_shift($this->scanQueue)){
|
326 |
+
$blog = $elem[0];
|
327 |
+
$postID = $elem[1];
|
328 |
+
$row = $wfdb->querySingleRec("select ID, post_title, post_type, post_date, post_content from " . $blog['table'] . " where ID=%d", $postID);
|
329 |
+
$this->hoover->hoover($blog['blog_id'] . '-' . $row['ID'], $row['post_title'] . ' ' . $row['post_content']);
|
330 |
+
$this->scanData[$blog['blog_id'] . '-' . $row['ID']] = array(
|
331 |
+
'contentMD5' => md5($row['post_content']),
|
332 |
+
'title' => $row['post_title'],
|
333 |
+
'type' => $row['post_type'],
|
334 |
+
'postDate' => $row['post_date'],
|
335 |
+
'isMultisite' => $blog['isMultisite'],
|
336 |
+
'domain' => $blog['domain'],
|
337 |
+
'path' => $blog['path'],
|
338 |
+
'blog_id' => $blog['blog_id']
|
339 |
+
);
|
340 |
+
$this->forkIfNeeded();
|
341 |
+
}
|
342 |
+
}
|
343 |
+
private function scan_posts_finish(){
|
344 |
$this->status(2, 'info', "Examining URLs found in posts we scanned for dangerous websites");
|
345 |
+
$hooverResults = $this->hoover->getBaddies();
|
346 |
$this->status(2, 'info', "Done examining URls");
|
347 |
+
if($this->hoover->errorMsg){
|
348 |
+
$this->errorStop($this->hoover->errorMsg);
|
349 |
wordfence::statusEndErr();
|
350 |
return;
|
351 |
|
355 |
$arr = explode('-', $idString);
|
356 |
$blogID = $arr[0];
|
357 |
$postID = $arr[1];
|
358 |
+
$uctype = ucfirst($this->scanData[$idString]['type']);
|
359 |
+
$type = $this->scanData[$idString]['type'];
|
360 |
foreach($hresults as $result){
|
361 |
if($result['badList'] == 'goog-malware-shavar'){
|
362 |
$shortMsg = "$uctype contains a suspected malware URL.";
|
373 |
switch_to_blog($blogID);
|
374 |
}
|
375 |
$ignoreP = $idString;
|
376 |
+
$ignoreC = $idString . $this->scanData[$idString]['contentMD5'];
|
377 |
if($this->addIssue('postBadURL', 1, $ignoreP, $ignoreC, $shortMsg, $longMsg, array(
|
378 |
'postID' => $postID,
|
379 |
'badURL' => $result['URL'],
|
380 |
+
'postTitle' => $this->scanData[$idString]['title'],
|
381 |
+
'type' => $this->scanData[$idString]['type'],
|
382 |
'uctype' => $uctype,
|
383 |
'permalink' => get_permalink($postID),
|
384 |
'editPostLink' => get_edit_post_link($postID),
|
385 |
+
'postDate' => $this->scanData[$idString]['postDate'],
|
386 |
+
'isMultisite' => $this->scanData[$idString]['isMultisite'],
|
387 |
+
'domain' => $this->scanData[$idString]['domain'],
|
388 |
+
'path' => $this->scanData[$idString]['path'],
|
389 |
'blog_id' => $blogID
|
390 |
))){
|
391 |
$haveIssues = true;
|
395 |
}
|
396 |
}
|
397 |
}
|
398 |
+
$this->scanData = array();
|
399 |
+
wordfence::statusEnd($this->statusIDX['posts'], $haveIssues);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
}
|
401 |
+
private function scan_comments_init(){
|
402 |
+
$this->statusIDX['comments'] = wordfence::statusStart('Scanning comments for URL\'s in Google\'s Safe Browsing List');
|
403 |
+
$this->scanData = array();
|
404 |
+
$this->scanQueue = array();
|
405 |
+
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
|
|
406 |
$blogsToScan = $this->getBlogsToScan('comments');
|
407 |
+
$wfdb = new wfDB();
|
408 |
foreach($blogsToScan as $blog){
|
409 |
$q1 = $wfdb->query("select comment_ID from " . $blog['table'] . " where comment_approved=1");
|
410 |
if( ! $q1){
|
416 |
}
|
417 |
|
418 |
while($idRow = mysql_fetch_assoc($q1)){
|
419 |
+
$this->scanQueue[] = array($blog, $idRow['comment_ID']);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
420 |
}
|
421 |
}
|
422 |
+
}
|
423 |
+
private function scan_comments_main(){
|
424 |
+
$wfdb = new wfDB();
|
425 |
+
while($elem = array_shift($this->scanQueue)){
|
426 |
+
$blog = $elem[0];
|
427 |
+
$commentID = $elem[1];
|
428 |
+
$row = $wfdb->querySingleRec("select comment_ID, comment_date, comment_type, comment_author, comment_author_url, comment_content from " . $blog['table'] . " where comment_ID=%d", $commentID);
|
429 |
+
$this->hoover->hoover($blog['blog_id'] . '-' . $row['comment_ID'], $row['comment_author_url'] . ' ' . $row['comment_author'] . ' ' . $row['comment_content']);
|
430 |
+
$this->scanData[$blog['blog_id'] . '-' . $row['comment_ID']] = array(
|
431 |
+
'contentMD5' => md5($row['comment_content'] . $row['comment_author'] . $row['comment_author_url']),
|
432 |
+
'author' => $row['comment_author'],
|
433 |
+
'type' => ($row['comment_type'] ? $row['comment_type'] : 'comment'),
|
434 |
+
'date' => $row['comment_date'],
|
435 |
+
'isMultisite' => $blog['isMultisite'],
|
436 |
+
'domain' => $blog['domain'],
|
437 |
+
'path' => $blog['path'],
|
438 |
+
'blog_id' => $blog['blog_id']
|
439 |
+
);
|
440 |
+
$this->forkIfNeeded();
|
441 |
+
}
|
442 |
+
}
|
443 |
+
private function scan_comments_finish(){
|
444 |
+
$hooverResults = $this->hoover->getBaddies();
|
445 |
+
if($this->hoover->errorMsg){
|
446 |
+
$this->errorStop($this->hoover->errorMsg);
|
447 |
wordfence::statusEndErr();
|
448 |
return;
|
449 |
}
|
452 |
$arr = explode('-', $idString);
|
453 |
$blogID = $arr[0];
|
454 |
$commentID = $arr[1];
|
455 |
+
$uctype = ucfirst($this->scanData[$idString]['type']);
|
456 |
+
$type = $this->scanData[$idString]['type'];
|
457 |
foreach($hresults as $result){
|
458 |
if($result['badList'] == 'goog-malware-shavar'){
|
459 |
$shortMsg = "$uctype contains a suspected malware URL.";
|
469 |
switch_to_blog($blogID);
|
470 |
}
|
471 |
$ignoreP = $idString;
|
472 |
+
$ignoreC = $idString . '-' . $this->scanData[$idString]['contentMD5'];
|
473 |
if($this->addIssue('commentBadURL', 1, $ignoreP, $ignoreC, $shortMsg, $longMsg, array(
|
474 |
'commentID' => $commentID,
|
475 |
'badURL' => $result['URL'],
|
476 |
+
'author' => $this->scanData[$idString]['author'],
|
477 |
'type' => $type,
|
478 |
'uctype' => $uctype,
|
479 |
'editCommentLink' => get_edit_comment_link($commentID),
|
480 |
+
'commentDate' => $this->scanData[$idString]['date'],
|
481 |
+
'isMultisite' => $this->scanData[$idString]['isMultisite'],
|
482 |
+
'domain' => $this->scanData[$idString]['domain'],
|
483 |
+
'path' => $this->scanData[$idString]['path'],
|
484 |
'blog_id' => $blogID
|
485 |
))){
|
486 |
$haveIssues = true;
|
490 |
}
|
491 |
}
|
492 |
}
|
493 |
+
wordfence::statusEnd($this->statusIDX['comments'], $haveIssues);
|
494 |
+
}
|
495 |
+
public function isBadComment($author, $email, $url, $IP, $content){
|
496 |
+
$content = $author . ' ' . $email . ' ' . $url . ' ' . $IP . ' ' . $content;
|
497 |
+
$cDesc = '';
|
498 |
+
if($author){
|
499 |
+
$cDesc = "Author: $author ";
|
500 |
+
}
|
501 |
+
if($email){
|
502 |
+
$cDesc .= "Email: $email ";
|
503 |
+
}
|
504 |
+
$cDesc = "Source IP: $IP ";
|
505 |
+
$this->status(2, 'info', "Scanning comment with $cDesc");
|
506 |
+
|
507 |
+
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
508 |
+
$h->hoover(1, $content);
|
509 |
+
$hooverResults = $h->getBaddies();
|
510 |
+
if($h->errorMsg){
|
511 |
+
return false;
|
512 |
+
}
|
513 |
+
if(sizeof($hooverResults) > 0 && isset($hooverResults[1])){
|
514 |
+
$hresults = $hooverResults[1];
|
515 |
+
foreach($hresults as $result){
|
516 |
+
if($result['badList'] == 'goog-malware-shavar'){
|
517 |
+
$this->status(2, 'info', "Marking comment as spam for containing a malware URL. Comment has $cDesc");
|
518 |
+
return true;
|
519 |
+
} else if($result['badList'] == 'googpub-phish-shavar'){
|
520 |
+
$this->status(2, 'info', "Marking comment as spam for containing a phishing URL. Comment has $cDesc");
|
521 |
+
return true;
|
522 |
+
} else {
|
523 |
+
//A list type that may be new and the plugin has not been upgraded yet.
|
524 |
+
continue;
|
525 |
+
}
|
526 |
+
}
|
527 |
+
}
|
528 |
+
$this->status(2, 'info', "Scanned comment with $cDesc");
|
529 |
+
return false;
|
530 |
}
|
531 |
public function getBlogsToScan($table){
|
532 |
$wfdb = new wfDB();
|
571 |
}
|
572 |
return false;
|
573 |
}
|
574 |
+
private function scan_passwds(){
|
575 |
+
$this->statusIDX['passwds'] = wordfence::statusStart('Scanning for weak passwords');
|
576 |
global $wpdb;
|
577 |
$ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users");
|
578 |
$haveIssues = false;
|
581 |
$isWeak = $this->scanUserPassword($user->ID);
|
582 |
if($isWeak){ $haveIssues = true; }
|
583 |
}
|
584 |
+
wordfence::statusEnd($this->statusIDX['passwds'], $haveIssues);
|
585 |
}
|
586 |
public function scanUserPassword($userID){
|
587 |
require_once( ABSPATH . 'wp-includes/class-phpass.php');
|
588 |
+
$passwdHasher = new PasswordHash(8, TRUE);
|
589 |
$userDat = get_userdata($userID);
|
590 |
$this->status(2, 'info', "Checking password strength of user '" . $userDat->user_login . "'");
|
591 |
$shortMsg = "";
|
605 |
}
|
606 |
$haveIssue = false;
|
607 |
for($i = 0; $i < sizeof($words); $i++){
|
608 |
+
if($passwdHasher->CheckPassword($words[$i], $userDat->user_pass)){
|
609 |
$this->status(2, 'info', "Adding issue " . $shortMsg);
|
610 |
if($this->addIssue('easyPassword', $level, $userDat->ID, $userDat->ID . '-' . $userDat->user_pass, $shortMsg, $longMsg, array(
|
611 |
'ID' => $userDat->ID,
|
623 |
$this->status(2, 'info', "Completed checking password strength of user '" . $userDat->user_login . "'");
|
624 |
return $haveIssue;
|
625 |
}
|
626 |
+
private function scan_diskSpace(){
|
627 |
+
$this->statusIDX['diskSpace'] = wordfence::statusStart("Scanning to check available disk space");
|
628 |
$total = disk_total_space('.');
|
629 |
$free = disk_free_space('.');
|
630 |
$this->status(2, 'info', "Total space: $total Free space: $free");
|
631 |
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
|
632 |
+
wordfence::statusEnd($this->statusIDX['diskSpace'], false);
|
633 |
return;
|
634 |
}
|
635 |
$level = false;
|
640 |
} else if($spaceLeft < 1.5){
|
641 |
$level = 2;
|
642 |
} else {
|
643 |
+
wordfence::statusEnd($this->statusIDX['diskSpace'], false);
|
644 |
return;
|
645 |
}
|
646 |
if($this->addIssue('diskSpace', $level, 'diskSpace' . $level, 'diskSpace' . $level, "You have $spaceLeft" . "% disk space remaining", "You only have $spaceLeft" . "% of your disk space remaining. Please free up disk space or your website may stop serving requests.", array(
|
647 |
'spaceLeft' => $spaceLeft ))){
|
648 |
+
wordfence::statusEnd($this->statusIDX['diskSpace'], true);
|
649 |
} else {
|
650 |
+
wordfence::statusEnd($this->statusIDX['diskSpace'], false);
|
651 |
}
|
652 |
}
|
653 |
+
private function scan_dns(){
|
654 |
if(! function_exists('dns_get_record')){
|
655 |
$this->status(1, 'info', "Skipping DNS scan because this system does not support dns_get_record()");
|
656 |
return;
|
657 |
}
|
658 |
+
$this->statusIDX['dns'] = wordfence::statusStart("Scanning DNS for unauthorized changes");
|
659 |
$haveIssues = false;
|
660 |
$home = get_home_url();
|
661 |
if(preg_match('/https?:\/\/([^\/]+)/i', $home, $matches)){
|
747 |
|
748 |
wfConfig::set('wf_dnsLogged', 1);
|
749 |
}
|
750 |
+
wordfence::statusEnd($this->statusIDX['dns'], $haveIssues);
|
751 |
}
|
752 |
+
private function scan_oldVersions(){
|
753 |
+
$this->statusIDX['oldVersions'] = wordfence::statusStart("Scanning for old themes, plugins and core files");
|
754 |
if(! function_exists( 'get_preferred_from_update_core')){
|
755 |
require_once(ABSPATH . 'wp-admin/includes/update.php');
|
756 |
}
|
806 |
|
807 |
}
|
808 |
}
|
809 |
+
wordfence::statusEnd($this->statusIDX['oldVersions'], $haveIssues);
|
810 |
}
|
811 |
private function errorStop($msg){
|
812 |
$this->errorStopped = true;
|
819 |
private function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
|
820 |
return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
|
821 |
}
|
822 |
+
public static function startScan($isFork = false){
|
823 |
+
wordfence::status(4, 'info', "Entering start scan routine");
|
824 |
+
if(wfUtils::isScanRunning()){
|
825 |
+
return "A scan is already running.";
|
826 |
+
}
|
827 |
+
|
828 |
+
$cron_url = plugins_url('wordfence/wfscan.php?isFork=' . ($isFork ? '1' : '0'));
|
829 |
+
wordfence::status(4, 'info', "Cron URL is: " . $cron_url);
|
830 |
+
$cronKey = wfUtils::bigRandomHex();
|
831 |
+
wordfence::status(4, 'info', "cronKey is: " . $cronKey);
|
832 |
+
wfConfig::set('currentCronKey', time() . ',' . $cronKey);
|
833 |
+
wordfence::status(4, 'info', "cronKey is set");
|
834 |
+
$result = wp_remote_post( $cron_url, array(
|
835 |
+
'timeout' => 0.5,
|
836 |
+
'blocking' => true,
|
837 |
+
'sslverify' => false,
|
838 |
+
'headers' => array(
|
839 |
+
'x-wordfence-cronkey' => $cronKey
|
840 |
+
)
|
841 |
+
) );
|
842 |
+
$procResp = self::processResponse($result);
|
843 |
+
if($procResp){ return $procResp; }
|
844 |
+
//If the currentCronKey was eaten, then cron executed so return
|
845 |
+
wfConfig::clearCache(); if(! wfConfig::get('currentCronKey')){
|
846 |
+
wordfence::status(4, 'info', "cronkey is empty so cron executed. Returning.");
|
847 |
+
return false;
|
848 |
+
}
|
849 |
+
|
850 |
+
|
851 |
+
//This request is for hosts that don't know their own name. i.e. they don't have example.com in their hosts file or DNS pointing to their own IP address or loopback address. So we throw a hail mary to loopback.
|
852 |
+
wordfence::status(4, 'info', "cronkey is still set so sleeping for 0.2 seconds and checking again before trying another approach");
|
853 |
+
usleep(200000);
|
854 |
+
wfConfig::clearCache();
|
855 |
+
if(wfConfig::get('currentCronKey')){ //cron key is still set, so cron hasn't executed yet. Maybe the request didn't go through
|
856 |
+
wordfence::status(4, 'info', "cronkey is still set so about to manually set host header and try again");
|
857 |
+
$cron_url2 = preg_replace('/^(https?):\/\/[^\/]+/', '$1://127.0.0.1', $cron_url);
|
858 |
+
wordfence::status(4, 'info', "cron url is: $cron_url2");
|
859 |
+
$siteURL = site_url();
|
860 |
+
wordfence::status(4, 'info', "siteURL is: $siteURL");
|
861 |
+
if(preg_match('/^https?:\/\/([^\/]+)/i', site_url(), $matches)){
|
862 |
+
$host = $matches[1];
|
863 |
+
wordfence::status(4, 'info', "Extracted host $host from siteURL and trying remote post with manual host header set.");
|
864 |
+
$result = wp_remote_post( $cron_url2, array(
|
865 |
+
'timeout' => 0.5,
|
866 |
+
'blocking' => true,
|
867 |
+
'sslverify' => false,
|
868 |
+
'headers' => array(
|
869 |
+
'x-wordfence-cronkey' => $cronKey,
|
870 |
+
'Host' => $host
|
871 |
+
)
|
872 |
+
) );
|
873 |
+
$procResp = self::processResponse($result);
|
874 |
+
if($procResp){ return $procResp; }
|
875 |
+
}
|
876 |
+
} else {
|
877 |
+
return false;
|
878 |
+
}
|
879 |
+
|
880 |
+
//Try again with longer timeout. Dreamhost was giving DNS lookup timeouts
|
881 |
+
usleep(200000);
|
882 |
+
wfConfig::clearCache();
|
883 |
+
if(wfConfig::get('currentCronKey')){
|
884 |
+
wordfence::status(4, "info", "cronkey is still set. Trying hostname again with longer timeout.");
|
885 |
+
$result = wp_remote_post( $cron_url, array(
|
886 |
+
'timeout' => 10,
|
887 |
+
'blocking' => true,
|
888 |
+
'sslverify' => false,
|
889 |
+
'headers' => array(
|
890 |
+
'x-wordfence-cronkey' => $cronKey
|
891 |
+
)
|
892 |
+
) );
|
893 |
+
$procResp = self::processResponse($result);
|
894 |
+
if($procResp){ return $procResp; }
|
895 |
+
//If the currentCronKey was eaten, then cron executed so return
|
896 |
+
wfConfig::clearCache(); if(! wfConfig::get('currentCronKey')){
|
897 |
+
wordfence::status(4, 'info', "cronkey is empty so cron executed. Returning.");
|
898 |
+
return false;
|
899 |
+
}
|
900 |
+
}
|
901 |
+
return false;
|
902 |
+
}
|
903 |
+
public function processResponse($result){
|
904 |
+
if(is_wp_error($result)){
|
905 |
+
wordfence::status(4, 'info', "Debug output of WP_Error: " . implode('; ', $result->get_error_messages()) );
|
906 |
+
} else {
|
907 |
+
if(is_array($result)){
|
908 |
+
if(is_array($result['response'])){
|
909 |
+
wordfence::status(4, 'info', "POST response: " . $result['response']['code'] . ' ' . $result['response']['message']);
|
910 |
+
}
|
911 |
+
wordfence::status(4, 'info', "POST response body: " . $result['body']);
|
912 |
+
}
|
913 |
+
}
|
914 |
+
if((! is_wp_error($result)) && is_array($result) && empty($result['body']) === false){
|
915 |
+
if(strpos($result['body'], 'WFSOURCEVISIBLE') !== false){
|
916 |
+
wordfence::status(4, 'info', "wfscan.php source is visible.");
|
917 |
+
$msg = "Wordfence can't run because the source code of your WordPress plugin files is visible from the Internet. This is a serious security risk which you need to fix. Please look for .htaccess files in your WordPress root directory and your wp-content/ and wp-content/plugins/ directories that may contain malicious code designed to reveal your site source code to a hacker.";
|
918 |
+
$htfiles = array();
|
919 |
+
if(file_exists(ABSPATH . 'wp-content/.htaccess')){
|
920 |
+
array_push($htfiles, '<a href="' . wfUtils::getSiteBaseURL() . '?_wfsf=view&nonce=' . wp_create_nonce('wp-ajax') . '&file=wp-content/.htaccess" target="_blank">wp-content/.htaccess</a>');
|
921 |
+
}
|
922 |
+
if(file_exists(ABSPATH . 'wp-content/plugins/.htaccess')){
|
923 |
+
array_push($htfiles, '<a href="' . wfUtils::getSiteBaseURL() . '?_wfsf=view&nonce=' . wp_create_nonce('wp-ajax') . '&file=wp-content/plugins/.htaccess" target="_blank">wp-content/plugins/.htaccess</a>');
|
924 |
+
}
|
925 |
+
if(sizeof($htfiles) > 0){
|
926 |
+
$msg .= "<br /><br />Click to view the .htaccess files below that may be the cause of this problem:<br />" . implode('<br />', $htfiles);
|
927 |
+
}
|
928 |
+
return $msg;
|
929 |
+
|
930 |
+
} else if(strpos($result['body'], '{') !== false && strpos($result['body'], 'errorMsg') !== false){
|
931 |
+
wordfence::status(4, 'info', "Got response from cron containing json");
|
932 |
+
$resp = json_decode($result['body'], true);
|
933 |
+
if(empty($resp['errorMsg']) === false){
|
934 |
+
wordfence::status(4, 'info', "Got an error message from cron: " . $resp['errorMsg']);
|
935 |
+
return $resp['errorMsg'];
|
936 |
+
}
|
937 |
+
}
|
938 |
+
}
|
939 |
+
return false;
|
940 |
+
}
|
941 |
}
|
942 |
|
943 |
?>
|
lib/wfUtils.php
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
<?php
|
2 |
class wfUtils {
|
3 |
-
private static $
|
|
|
4 |
public static function makeTimeAgo($secs, $noSeconds = false) {
|
5 |
if($secs < 1){
|
6 |
return "a moment";
|
@@ -66,7 +67,8 @@ class wfUtils {
|
|
66 |
return plugins_url() . '/wordfence/';
|
67 |
}
|
68 |
public static function getPluginBaseDir(){
|
69 |
-
return
|
|
|
70 |
}
|
71 |
public static function getIP(){
|
72 |
$ip = 0;
|
@@ -81,18 +83,7 @@ class wfUtils {
|
|
81 |
public static function getRequestedURL(){
|
82 |
return ($_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
|
83 |
}
|
84 |
-
|
85 |
-
if(! isset(self::$reverseLookupCache[$IP])){
|
86 |
-
$ptr = implode(".", array_reverse(explode(".",$IP))) . ".in-addr.arpa";
|
87 |
-
$host = dns_get_record($ptr, DNS_PTR);
|
88 |
-
if($host == null){
|
89 |
-
self::$reverseLookupCache[$IP] = '';
|
90 |
-
} else {
|
91 |
-
self::$reverseLookupCache[$IP] = $host[0]['target'];
|
92 |
-
}
|
93 |
-
}
|
94 |
-
return self::$reverseLookupCache[$IP];
|
95 |
-
}
|
96 |
public static function editUserLink($userID){
|
97 |
return get_admin_url() . 'user-edit.php?user_id=' . $userID;
|
98 |
}
|
@@ -196,6 +187,153 @@ class wfUtils {
|
|
196 |
}
|
197 |
return false;
|
198 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
}
|
200 |
|
201 |
|
1 |
<?php
|
2 |
class wfUtils {
|
3 |
+
private static $isWindows = false;
|
4 |
+
public static $scanLockFH = false;
|
5 |
public static function makeTimeAgo($secs, $noSeconds = false) {
|
6 |
if($secs < 1){
|
7 |
return "a moment";
|
67 |
return plugins_url() . '/wordfence/';
|
68 |
}
|
69 |
public static function getPluginBaseDir(){
|
70 |
+
return WP_CONTENT_DIR . '/plugins/';
|
71 |
+
//return ABSPATH . 'wp-content/plugins/';
|
72 |
}
|
73 |
public static function getIP(){
|
74 |
$ip = 0;
|
83 |
public static function getRequestedURL(){
|
84 |
return ($_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
|
85 |
}
|
86 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
public static function editUserLink($userID){
|
88 |
return get_admin_url() . 'user-edit.php?user_id=' . $userID;
|
89 |
}
|
187 |
}
|
188 |
return false;
|
189 |
}
|
190 |
+
public static function isWindows(){
|
191 |
+
if(! self::$isWindows){
|
192 |
+
if(preg_match('/^win/', PHP_OS)){
|
193 |
+
self::$isWindows = 'yes';
|
194 |
+
} else {
|
195 |
+
self::$isWindows = 'no';
|
196 |
+
}
|
197 |
+
}
|
198 |
+
return self::$isWindows == 'yes' ? true : false;
|
199 |
+
}
|
200 |
+
public static function getScanLock(){
|
201 |
+
if(self::isWindows()){
|
202 |
+
//Windows does not support non-blocking flock, so we use time.
|
203 |
+
$scanRunning = wfConfig::get('wf_scanRunning');
|
204 |
+
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
wfConfig::set('wf_scanRunning', time());
|
208 |
+
return true;
|
209 |
+
} else {
|
210 |
+
self::$scanLockFH = fopen(__FILE__, 'r');
|
211 |
+
if(flock(self::$scanLockFH, LOCK_EX | LOCK_NB)){
|
212 |
+
return true;
|
213 |
+
} else {
|
214 |
+
return false;
|
215 |
+
}
|
216 |
+
}
|
217 |
+
}
|
218 |
+
public static function clearScanLock(){
|
219 |
+
if(self::isWindows()){
|
220 |
+
wfConfig::set('wf_scanRunning', '');
|
221 |
+
} else {
|
222 |
+
if(self::$scanLockFH){
|
223 |
+
@fclose(self::$scanLockFH);
|
224 |
+
self::$scanLockFH = false;
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
}
|
229 |
+
public static function isScanRunning(){
|
230 |
+
$scanRunning = true;
|
231 |
+
if(self::getScanLock()){
|
232 |
+
$scanRunning = false;
|
233 |
+
}
|
234 |
+
self::clearScanLock();
|
235 |
+
return $scanRunning;
|
236 |
+
}
|
237 |
+
public static function getIPGeo($IP){ //Works with int or dotted
|
238 |
+
|
239 |
+
$locs = self::getIPsGeo(array($IP));
|
240 |
+
if(isset($locs[$IP])){
|
241 |
+
return $locs[$IP];
|
242 |
+
} else {
|
243 |
+
return false;
|
244 |
+
}
|
245 |
+
}
|
246 |
+
public static function getIPsGeo($IPs){ //works with int or dotted. Outputs same format it receives.
|
247 |
+
$IPs = array_unique($IPs);
|
248 |
+
$isInt = false;
|
249 |
+
if(strpos($IPs[0], '.') === false){
|
250 |
+
$isInt = true;
|
251 |
+
}
|
252 |
+
$toResolve = array();
|
253 |
+
$db = new wfDB();
|
254 |
+
global $wp_version;
|
255 |
+
global $wpdb;
|
256 |
+
$locsTable = $wpdb->base_prefix . 'wfLocs';
|
257 |
+
$IPLocs = array();
|
258 |
+
foreach($IPs as $IP){
|
259 |
+
$r1 = $db->query("select IP, ctime, failed, city, region, countryName, countryCode, lat, lon, unix_timestamp() - ctime as age from " . $locsTable . " where IP=%s", ($isInt ? $IP : self::inet_aton($IP)) );
|
260 |
+
if($r1){
|
261 |
+
if($row = mysql_fetch_assoc($r1)){
|
262 |
+
if($row['age'] > WORDFENCE_MAX_IPLOC_AGE){
|
263 |
+
$db->query("delete from " . $locsTable . " where IP=%s", $row['IP']);
|
264 |
+
} else {
|
265 |
+
if($row['failed'] == 1){
|
266 |
+
$IPLocs[$IP] = false;
|
267 |
+
} else {
|
268 |
+
if(! $isInt){
|
269 |
+
$row['IP'] = self::inet_ntoa($row['IP']);
|
270 |
+
}
|
271 |
+
$IPLocs[$IP] = $row;
|
272 |
+
}
|
273 |
+
}
|
274 |
+
}
|
275 |
+
}
|
276 |
+
if(! isset($IPLocs[$IP])){
|
277 |
+
$toResolve[] = $IP;
|
278 |
+
}
|
279 |
+
}
|
280 |
+
if(sizeof($toResolve) > 0){
|
281 |
+
$api = new wfAPI(wfConfig::get('apiKey'), $wp_version);
|
282 |
+
$freshIPs = $api->call('resolve_ips', array(), array(
|
283 |
+
'ips' => implode(',', $toResolve)
|
284 |
+
));
|
285 |
+
if(is_array($freshIPs)){
|
286 |
+
foreach($freshIPs as $IP => $value){
|
287 |
+
if($value == 'failed'){
|
288 |
+
$db->query("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", ($isInt ? $IP : self::inet_aton($IP)) );
|
289 |
+
$IPLocs[$IP] = false;
|
290 |
+
} else {
|
291 |
+
$db->query("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
|
292 |
+
($isInt ? $IP : self::inet_aton($IP)),
|
293 |
+
$value[3], //city
|
294 |
+
$value[2], //region
|
295 |
+
$value[1], //countryName
|
296 |
+
$value[0],//countryCode
|
297 |
+
$value[4],//lat
|
298 |
+
$value[5]//lon
|
299 |
+
);
|
300 |
+
$IPLocs[$IP] = array(
|
301 |
+
'IP' => $IP,
|
302 |
+
'city' => $value[3],
|
303 |
+
'region' => $value[2],
|
304 |
+
'countryName' => $value[1],
|
305 |
+
'countryCode' => $value[0],
|
306 |
+
'lat' => $value[4],
|
307 |
+
'lon' => $value[5]
|
308 |
+
);
|
309 |
+
}
|
310 |
+
}
|
311 |
+
}
|
312 |
+
}
|
313 |
+
return $IPLocs;
|
314 |
+
}
|
315 |
+
public function reverseLookup($IP){
|
316 |
+
$db = new wfDB();
|
317 |
+
global $wpdb;
|
318 |
+
$reverseTable = $wpdb->base_prefix . 'wfReverseCache';
|
319 |
+
$IPn = wfUtils::inet_aton($IP);
|
320 |
+
$host = $db->querySingle("select host from " . $reverseTable . " where IP=%s and unix_timestamp() - lastUpdate < %d", $IPn, WORDFENCE_REVERSE_LOOKUP_CACHE_TIME);
|
321 |
+
if(! $host){
|
322 |
+
$ptr = implode(".", array_reverse(explode(".",$IP))) . ".in-addr.arpa";
|
323 |
+
$host = dns_get_record($ptr, DNS_PTR);
|
324 |
+
if($host == null){
|
325 |
+
$host = 'NONE';
|
326 |
+
} else {
|
327 |
+
$host = $host[0]['target'];
|
328 |
+
}
|
329 |
+
$db->query("insert into " . $reverseTable . " (IP, host, lastUpdate) values (%s, '%s', unix_timestamp()) ON DUPLICATE KEY UPDATE host='%s', lastUpdate=unix_timestamp()", $IPn, $host, $host);
|
330 |
+
}
|
331 |
+
if($host == 'NONE'){
|
332 |
+
return '';
|
333 |
+
} else {
|
334 |
+
return $host;
|
335 |
+
}
|
336 |
+
}
|
337 |
}
|
338 |
|
339 |
|
lib/wordfenceClass.php
CHANGED
@@ -165,8 +165,9 @@ class wordfence {
|
|
165 |
wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
|
166 |
}
|
167 |
$db = new wfDB();
|
|
|
168 |
//Upgrading from 1.5.6 or earlier needs:
|
169 |
-
$db->createKeyIfNotExists(
|
170 |
if(wfConfig::get('isPaid') == 'free'){
|
171 |
wfConfig::set('isPaid', '');
|
172 |
}
|
@@ -180,6 +181,10 @@ class wordfence {
|
|
180 |
@chmod(dirname(__FILE__) . '/../wfscan.php', 0755);
|
181 |
@chmod(dirname(__FILE__) . '/../visitor.php', 0755);
|
182 |
|
|
|
|
|
|
|
|
|
183 |
//Must be the final line
|
184 |
update_option('wordfence_version', WORDFENCE_VERSION);
|
185 |
}
|
@@ -269,7 +274,7 @@ class wordfence {
|
|
269 |
$user = get_user_by('email', $_POST['user_login']);
|
270 |
if($user){
|
271 |
if(wfConfig::get('alertOn_lostPasswdForm')){
|
272 |
-
wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email
|
273 |
}
|
274 |
}
|
275 |
if(wfConfig::get('loginSecurityEnabled')){
|
@@ -289,7 +294,7 @@ class wordfence {
|
|
289 |
}
|
290 |
public static function lockOutIP($IP, $reason){
|
291 |
if(wfConfig::get('alertOn_loginLockout')){
|
292 |
-
wordfence::alert("User locked out from signing in", "A user with IP address $IP has been locked out from the signing in or using the password recovery form for the following reason: $reason");
|
293 |
}
|
294 |
self::getLog()->lockOutIP(wfUtils::getIP(), $reason);
|
295 |
}
|
@@ -297,7 +302,7 @@ class wordfence {
|
|
297 |
return self::getLog()->isIPLockedOut($IP);
|
298 |
}
|
299 |
public static function veryFirstAction(){
|
300 |
-
$wfFunc =
|
301 |
if($wfFunc == 'unlockEmail'){
|
302 |
$email = trim($_POST['email']);
|
303 |
global $wpdb;
|
@@ -381,11 +386,11 @@ class wordfence {
|
|
381 |
self::getLog()->logLogin('loginOK', 0, $username);
|
382 |
if(user_can($userID, 'update_core')){
|
383 |
if(wfConfig::get('alertOn_adminLogin')){
|
384 |
-
wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.");
|
385 |
}
|
386 |
} else {
|
387 |
if(wfConfig::get('alertOn_nonAdminLogin')){
|
388 |
-
wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.");
|
389 |
}
|
390 |
}
|
391 |
}
|
@@ -445,26 +450,25 @@ class wordfence {
|
|
445 |
}
|
446 |
public static function getWPFileContent($file, $cType, $cName, $cVersion){
|
447 |
if($cType == 'plugin'){
|
448 |
-
|
449 |
-
|
450 |
-
|
|
|
|
|
|
|
451 |
} else if($cType == 'theme'){
|
452 |
-
|
453 |
-
|
|
|
|
|
|
|
454 |
} else if($cType == 'core'){
|
455 |
|
456 |
} else {
|
457 |
return array('errorMsg' => "An invalid type was specified to get file.");
|
458 |
}
|
459 |
-
|
460 |
-
$transKey = 'wf_wpFileContent_' . $file . '_' . $cType . '_' . $cName . '_' . $cVersion;
|
461 |
-
$transKey = preg_replace('/[^a-zA-Z0-9\_]+/', '_', $transKey);
|
462 |
-
$content = get_site_transient($transKey);
|
463 |
-
if($content){
|
464 |
-
return array('fileContent' => $content);
|
465 |
-
}
|
466 |
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
467 |
-
$
|
468 |
'file' => $file,
|
469 |
'cType' => $cType,
|
470 |
'cName' => $cName,
|
@@ -473,9 +477,8 @@ class wordfence {
|
|
473 |
if($api->errorMsg){
|
474 |
return array('errorMsg' => $api->errorMsg);
|
475 |
}
|
476 |
-
if($
|
477 |
-
|
478 |
-
return array('fileContent' => $dat['contents']);
|
479 |
} else {
|
480 |
return array('errorMsg' => "We could not fetch a core WordPress file from the Wordfence API.");
|
481 |
}
|
@@ -683,7 +686,7 @@ class wordfence {
|
|
683 |
$ips = explode(',', $_POST['ips']);
|
684 |
$res = array();
|
685 |
foreach($ips as $ip){
|
686 |
-
$res[$ip] =
|
687 |
}
|
688 |
return array('ok' => 1, 'ips' => $res);
|
689 |
}
|
@@ -795,32 +798,37 @@ class wordfence {
|
|
795 |
$wfIssues = new wfIssues();
|
796 |
$issue = $wfIssues->getIssueByID($issueID);
|
797 |
if(! $issue){
|
798 |
-
return array('
|
799 |
}
|
800 |
$dat = $issue['data'];
|
801 |
$result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']);
|
802 |
$file = $dat['file'];
|
803 |
-
if($result['
|
804 |
return $result;
|
805 |
} else if(! $result['fileContent']){
|
806 |
-
return array('
|
807 |
}
|
808 |
|
809 |
if(preg_match('/\.\./', $file)){
|
810 |
-
return array('
|
811 |
}
|
812 |
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
|
813 |
$fh = fopen($localFile, 'w');
|
814 |
if(! $fh){
|
815 |
$err = error_get_last();
|
816 |
-
|
|
|
|
|
|
|
|
|
|
|
817 |
}
|
818 |
flock($fh, LOCK_EX);
|
819 |
$bytes = fwrite($fh, $result['fileContent']);
|
820 |
flock($fh, LOCK_UN);
|
821 |
fclose($fh);
|
822 |
if($bytes < 1){
|
823 |
-
return array('
|
824 |
}
|
825 |
$wfIssues->updateIssue($issueID, 'delete');
|
826 |
return array(
|
@@ -849,7 +857,7 @@ class wordfence {
|
|
849 |
}
|
850 |
if($result['ok'] && isset($result['isPaid'])){
|
851 |
wfConfig::set('isPaid', $result['isPaid']);
|
852 |
-
$err =
|
853 |
if($err){
|
854 |
return array('errorMsg' => $err);
|
855 |
} else {
|
@@ -861,7 +869,7 @@ class wordfence {
|
|
861 |
}
|
862 |
public static function ajax_scan_callback(){
|
863 |
self::status(4, 'info', "Ajax request received to start scan.");
|
864 |
-
$err =
|
865 |
if($err){
|
866 |
return array('errorMsg' => $err);
|
867 |
} else {
|
@@ -869,84 +877,7 @@ class wordfence {
|
|
869 |
}
|
870 |
}
|
871 |
public static function startScan(){
|
872 |
-
|
873 |
-
$cron_url = plugins_url('wordfence/wfscan.php');
|
874 |
-
self::status(4, 'info', "Cron URL is: " . $cron_url);
|
875 |
-
$cronKey = wfUtils::bigRandomHex();
|
876 |
-
self::status(4, 'info', "cronKey is: " . $cronKey);
|
877 |
-
wfConfig::set('currentCronKey', time() . ',' . $cronKey);
|
878 |
-
self::status(4, 'info', "cronKey is set");
|
879 |
-
$result = wp_remote_post( $cron_url, array(
|
880 |
-
'timeout' => 0.5,
|
881 |
-
'blocking' => true,
|
882 |
-
'sslverify' => false,
|
883 |
-
'headers' => array(
|
884 |
-
'x-wordfence-cronkey' => $cronKey
|
885 |
-
)
|
886 |
-
) );
|
887 |
-
$procResp = self::processResponse($result);
|
888 |
-
if($procResp){ return $procResp; }
|
889 |
-
//If the currentCronKey was eaten, then cron executed so return
|
890 |
-
wfConfig::clearCache(); if(! wfConfig::get('currentCronKey')){
|
891 |
-
self::status(4, 'info', "cronkey is empty so cron executed. Returning.");
|
892 |
-
return false;
|
893 |
-
}
|
894 |
-
|
895 |
-
//This second request is for hosts that don't know their own name. i.e. they don't have example.com in their hosts file or DNS pointing to their own IP address or loopback address. So we throw a hail mary to loopback.
|
896 |
-
self::status(4, 'info', "cronkey is still set so sleeping for 0.2 seconds and checking again before trying another approach");
|
897 |
-
usleep(200000);
|
898 |
-
wfConfig::clearCache();
|
899 |
-
if(wfConfig::get('currentCronKey')){ //cron key is still set, so cron hasn't executed yet. Maybe the request didn't go through
|
900 |
-
self::status(4, 'info', "cronkey is still set so about to manually set host header and try again");
|
901 |
-
$cron_url = preg_replace('/^(https?):\/\/[^\/]+/', '$1://127.0.0.1', $cron_url);
|
902 |
-
self::status(4, 'info', "cron url is: $cron_url");
|
903 |
-
$siteURL = site_url();
|
904 |
-
self::status(4, 'info', "siteURL is: $siteURL");
|
905 |
-
if(preg_match('/^https?:\/\/([^\/]+)/i', site_url(), $matches)){
|
906 |
-
$host = $matches[1];
|
907 |
-
self::status(4, 'info', "Extracted host $host from siteURL and trying remote post with manual host header set.");
|
908 |
-
$result = wp_remote_post( $cron_url, array(
|
909 |
-
'timeout' => 0.5,
|
910 |
-
'blocking' => true,
|
911 |
-
'sslverify' => false,
|
912 |
-
'headers' => array(
|
913 |
-
'x-wordfence-cronkey' => $cronKey,
|
914 |
-
'Host' => $host
|
915 |
-
)
|
916 |
-
) );
|
917 |
-
$procResp = self::processResponse($result);
|
918 |
-
if($procResp){ return $procResp; }
|
919 |
-
}
|
920 |
-
}
|
921 |
-
return false;
|
922 |
-
}
|
923 |
-
public function processResponse($result){
|
924 |
-
if((! is_wp_error($result)) && is_array($result) && empty($result['body']) === false){
|
925 |
-
if(strpos($result['body'], 'WFSOURCEVISIBLE') !== false){
|
926 |
-
self::status(4, 'info', "wfscan.php source is visible.");
|
927 |
-
$msg = "Wordfence can't run because the source code of your WordPress plugin files is visible from the Internet. This is a serious security risk which you need to fix. Please look for .htaccess files in your WordPress root directory and your wp-content/ and wp-content/plugins/ directories that may contain malicious code designed to reveal your site source code to a hacker.";
|
928 |
-
$htfiles = array();
|
929 |
-
if(file_exists(ABSPATH . 'wp-content/.htaccess')){
|
930 |
-
array_push($htfiles, '<a href="' . wfUtils::getSiteBaseURL() . '?_wfsf=view&nonce=' . wp_create_nonce('wp-ajax') . '&file=wp-content/.htaccess" target="_blank">wp-content/.htaccess</a>');
|
931 |
-
}
|
932 |
-
if(file_exists(ABSPATH . 'wp-content/plugins/.htaccess')){
|
933 |
-
array_push($htfiles, '<a href="' . wfUtils::getSiteBaseURL() . '?_wfsf=view&nonce=' . wp_create_nonce('wp-ajax') . '&file=wp-content/plugins/.htaccess" target="_blank">wp-content/plugins/.htaccess</a>');
|
934 |
-
}
|
935 |
-
if(sizeof($htfiles) > 0){
|
936 |
-
$msg .= "<br /><br />Click to view the .htaccess files below that may be the cause of this problem:<br />" . implode('<br />', $htfiles);
|
937 |
-
}
|
938 |
-
return $msg;
|
939 |
-
|
940 |
-
} else if(strpos($result['body'], '{') !== false && strpos($result['body'], 'errorMsg') !== false){
|
941 |
-
self::status(4, 'info', "Got response from cron containing json");
|
942 |
-
$resp = json_decode($result['body'], true);
|
943 |
-
if(empty($resp['errorMsg']) === false){
|
944 |
-
self::status(4, 'info', "Got an error message from cron: " . $resp['errorMsg']);
|
945 |
-
return $resp['errorMsg'];
|
946 |
-
}
|
947 |
-
}
|
948 |
-
}
|
949 |
-
return false;
|
950 |
}
|
951 |
public static function templateRedir(){
|
952 |
$wfFunc = get_query_var('_wfsf');
|
@@ -989,7 +920,7 @@ class wordfence {
|
|
989 |
self::wfFunc_testmem();
|
990 |
} else if($wfFunc == 'testtime'){
|
991 |
self::wfFunc_testtime();
|
992 |
-
}
|
993 |
exit(0);
|
994 |
}
|
995 |
public static function memtest_error_handler($errno, $errstr, $errfile, $errline){
|
@@ -1063,7 +994,7 @@ class wordfence {
|
|
1063 |
}
|
1064 |
public static function wfFunc_IPTraf(){
|
1065 |
$IP = $_GET['IP'];
|
1066 |
-
$reverseLookup =
|
1067 |
if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){
|
1068 |
echo "An invalid IP address was specified.";
|
1069 |
exit(0);
|
@@ -1125,21 +1056,19 @@ class wordfence {
|
|
1125 |
}
|
1126 |
|
1127 |
$localFile = realpath(ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file']));
|
1128 |
-
if(strpos($localFile, ABSPATH) !== 0){
|
1129 |
-
echo "An invalid file was requested for comparison.";
|
1130 |
-
exit(0);
|
1131 |
-
}
|
1132 |
-
$diffOptions = array();
|
1133 |
$localContents = file_get_contents($localFile);
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
|
|
|
|
|
|
1143 |
require 'diffResult.php';
|
1144 |
exit(0);
|
1145 |
}
|
@@ -1154,7 +1083,7 @@ class wordfence {
|
|
1154 |
} else {
|
1155 |
self::$newVisit = true;
|
1156 |
}
|
1157 |
-
setcookie($cookieName, uniqid(), time() + 1800, '/');
|
1158 |
}
|
1159 |
public static function admin_init(){
|
1160 |
if(! wfUtils::isAdmin()){ return; }
|
@@ -1162,7 +1091,7 @@ class wordfence {
|
|
1162 |
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
|
1163 |
}
|
1164 |
|
1165 |
-
if(preg_match('/^Wordfence/',
|
1166 |
|
1167 |
wp_enqueue_style('wordfence-main-style', WP_PLUGIN_URL . '/wordfence/css/main.css', '', WORDFENCE_VERSION);
|
1168 |
wp_enqueue_style('wordfence-colorbox-style', WP_PLUGIN_URL . '/wordfence/css/colorbox.css', '', WORDFENCE_VERSION);
|
@@ -1203,7 +1132,7 @@ class wordfence {
|
|
1203 |
}
|
1204 |
}
|
1205 |
add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
|
1206 |
-
add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', WP_PLUGIN_URL . '/wordfence/images/wordfence-logo-16x16.png'
|
1207 |
if(wfConfig::get('liveTrafficEnabled')){
|
1208 |
add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
|
1209 |
}
|
@@ -1273,18 +1202,36 @@ class wordfence {
|
|
1273 |
return admin_url('admin.php?page=WordfenceSecOpt', 'http');
|
1274 |
}
|
1275 |
|
1276 |
-
public static function alert($subject, $alertMsg){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1277 |
$content = wfUtils::tmpl('email_genericAlert.php', array(
|
1278 |
'subject' => $subject,
|
1279 |
'blogName' => get_bloginfo('name', 'raw'),
|
1280 |
'alertMsg' => $alertMsg,
|
|
|
1281 |
'date' => date('l jS \of F Y \a\t h:i:s A'),
|
1282 |
'myHomeURL' => self::getMyHomeURL(),
|
1283 |
'myOptionsURL' => self::getMyOptionsURL()
|
1284 |
));
|
1285 |
$emails = wfConfig::getAlertEmails();
|
1286 |
if(sizeof($emails) < 1){ return; }
|
1287 |
-
$
|
|
|
1288 |
wp_mail(implode(',', $emails), $subject, $content);
|
1289 |
}
|
1290 |
public static function scheduleNextScan($force = false){
|
@@ -1316,24 +1263,34 @@ class wordfence {
|
|
1316 |
}
|
1317 |
return self::$wfLog;
|
1318 |
}
|
|
|
|
|
|
|
|
|
|
|
1319 |
public static function statusStart($msg){
|
1320 |
-
|
|
|
|
|
1321 |
self::status(10, 'info', 'SUM_START:' . $msg);
|
1322 |
-
return sizeof(
|
1323 |
}
|
1324 |
public static function statusEnd($idx, $haveIssues){
|
|
|
1325 |
if($haveIssues){
|
1326 |
-
self::status(10, 'info', 'SUM_ENDBAD:' .
|
1327 |
} else {
|
1328 |
-
self::status(10, 'info', 'SUM_ENDOK:' .
|
1329 |
}
|
1330 |
-
|
|
|
1331 |
}
|
1332 |
public static function statusEndErr(){
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
self
|
|
|
1337 |
}
|
1338 |
}
|
1339 |
}
|
165 |
wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
|
166 |
}
|
167 |
$db = new wfDB();
|
168 |
+
|
169 |
//Upgrading from 1.5.6 or earlier needs:
|
170 |
+
$db->createKeyIfNotExists('wfStatus', 'level', 'k2');
|
171 |
if(wfConfig::get('isPaid') == 'free'){
|
172 |
wfConfig::set('isPaid', '');
|
173 |
}
|
181 |
@chmod(dirname(__FILE__) . '/../wfscan.php', 0755);
|
182 |
@chmod(dirname(__FILE__) . '/../visitor.php', 0755);
|
183 |
|
184 |
+
global $wpdb;
|
185 |
+
$prefix = $wpdb->base_prefix;
|
186 |
+
$db->query("alter table $prefix"."wfConfig modify column val longblob");
|
187 |
+
|
188 |
//Must be the final line
|
189 |
update_option('wordfence_version', WORDFENCE_VERSION);
|
190 |
}
|
274 |
$user = get_user_by('email', $_POST['user_login']);
|
275 |
if($user){
|
276 |
if(wfConfig::get('alertOn_lostPasswdForm')){
|
277 |
+
wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email", wfUtils::getIP());
|
278 |
}
|
279 |
}
|
280 |
if(wfConfig::get('loginSecurityEnabled')){
|
294 |
}
|
295 |
public static function lockOutIP($IP, $reason){
|
296 |
if(wfConfig::get('alertOn_loginLockout')){
|
297 |
+
wordfence::alert("User locked out from signing in", "A user with IP address $IP has been locked out from the signing in or using the password recovery form for the following reason: $reason", $IP);
|
298 |
}
|
299 |
self::getLog()->lockOutIP(wfUtils::getIP(), $reason);
|
300 |
}
|
302 |
return self::getLog()->isIPLockedOut($IP);
|
303 |
}
|
304 |
public static function veryFirstAction(){
|
305 |
+
$wfFunc = @$_GET['_wfsf'];
|
306 |
if($wfFunc == 'unlockEmail'){
|
307 |
$email = trim($_POST['email']);
|
308 |
global $wpdb;
|
386 |
self::getLog()->logLogin('loginOK', 0, $username);
|
387 |
if(user_can($userID, 'update_core')){
|
388 |
if(wfConfig::get('alertOn_adminLogin')){
|
389 |
+
wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
|
390 |
}
|
391 |
} else {
|
392 |
if(wfConfig::get('alertOn_nonAdminLogin')){
|
393 |
+
wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.", wfUtils::getIP());
|
394 |
}
|
395 |
}
|
396 |
}
|
450 |
}
|
451 |
public static function getWPFileContent($file, $cType, $cName, $cVersion){
|
452 |
if($cType == 'plugin'){
|
453 |
+
if(preg_match('#^/?wp-content/plugins/[^/]+/#', $file)){
|
454 |
+
$file = preg_replace('#^/?wp-content/plugins/[^/]+/#', '', $file);
|
455 |
+
} else {
|
456 |
+
//If user is using non-standard wp-content dir, then use /plugins/ in pattern to figure out what to strip off
|
457 |
+
$file = preg_replace('#^.*[^/]+/plugins/[^/]+/#', '', $file);
|
458 |
+
}
|
459 |
} else if($cType == 'theme'){
|
460 |
+
if(preg_match('#/?wp-content/themes/[^/]+/#', $file)){
|
461 |
+
$file = preg_replace('#/?wp-content/themes/[^/]+/#', '', $file);
|
462 |
+
} else {
|
463 |
+
$file = preg_replace('#^.*[^/]+/themes/[^/]+/#', '', $file);
|
464 |
+
}
|
465 |
} else if($cType == 'core'){
|
466 |
|
467 |
} else {
|
468 |
return array('errorMsg' => "An invalid type was specified to get file.");
|
469 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
470 |
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
471 |
+
$contResult = $api->binCall('get_wp_file_content', array(
|
472 |
'file' => $file,
|
473 |
'cType' => $cType,
|
474 |
'cName' => $cName,
|
477 |
if($api->errorMsg){
|
478 |
return array('errorMsg' => $api->errorMsg);
|
479 |
}
|
480 |
+
if($contResult['data']){
|
481 |
+
return array('fileContent' => $contResult['data']);
|
|
|
482 |
} else {
|
483 |
return array('errorMsg' => "We could not fetch a core WordPress file from the Wordfence API.");
|
484 |
}
|
686 |
$ips = explode(',', $_POST['ips']);
|
687 |
$res = array();
|
688 |
foreach($ips as $ip){
|
689 |
+
$res[$ip] = wfUtils::reverseLookup($ip);
|
690 |
}
|
691 |
return array('ok' => 1, 'ips' => $res);
|
692 |
}
|
798 |
$wfIssues = new wfIssues();
|
799 |
$issue = $wfIssues->getIssueByID($issueID);
|
800 |
if(! $issue){
|
801 |
+
return array('cerrorMsg' => "We could not find that issue in our database.");
|
802 |
}
|
803 |
$dat = $issue['data'];
|
804 |
$result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']);
|
805 |
$file = $dat['file'];
|
806 |
+
if($result['cerrorMsg']){
|
807 |
return $result;
|
808 |
} else if(! $result['fileContent']){
|
809 |
+
return array('cerrorMsg' => "We could not get the original file to do a repair.");
|
810 |
}
|
811 |
|
812 |
if(preg_match('/\.\./', $file)){
|
813 |
+
return array('cerrorMsg' => "An invalid file was specified for repair.");
|
814 |
}
|
815 |
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
|
816 |
$fh = fopen($localFile, 'w');
|
817 |
if(! $fh){
|
818 |
$err = error_get_last();
|
819 |
+
if(preg_match('/Permission denied/i', $err['message'])){
|
820 |
+
$errMsg = "You don't have permission to repair that file. You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
|
821 |
+
} else {
|
822 |
+
$errMsg = "We could not write to that file. The error was: " . $err['message'];
|
823 |
+
}
|
824 |
+
return array('cerrorMsg' => $errMsg);
|
825 |
}
|
826 |
flock($fh, LOCK_EX);
|
827 |
$bytes = fwrite($fh, $result['fileContent']);
|
828 |
flock($fh, LOCK_UN);
|
829 |
fclose($fh);
|
830 |
if($bytes < 1){
|
831 |
+
return array('cerrorMsg' => "We could not write to that file. ($bytes bytes written) You may not have permission to modify files on your WordPress server.");
|
832 |
}
|
833 |
$wfIssues->updateIssue($issueID, 'delete');
|
834 |
return array(
|
857 |
}
|
858 |
if($result['ok'] && isset($result['isPaid'])){
|
859 |
wfConfig::set('isPaid', $result['isPaid']);
|
860 |
+
$err = wfScanEngine::startScan();
|
861 |
if($err){
|
862 |
return array('errorMsg' => $err);
|
863 |
} else {
|
869 |
}
|
870 |
public static function ajax_scan_callback(){
|
871 |
self::status(4, 'info', "Ajax request received to start scan.");
|
872 |
+
$err = wfScanEngine::startScan();
|
873 |
if($err){
|
874 |
return array('errorMsg' => $err);
|
875 |
} else {
|
877 |
}
|
878 |
}
|
879 |
public static function startScan(){
|
880 |
+
wfScanEngine::startScan();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
881 |
}
|
882 |
public static function templateRedir(){
|
883 |
$wfFunc = get_query_var('_wfsf');
|
920 |
self::wfFunc_testmem();
|
921 |
} else if($wfFunc == 'testtime'){
|
922 |
self::wfFunc_testtime();
|
923 |
+
}
|
924 |
exit(0);
|
925 |
}
|
926 |
public static function memtest_error_handler($errno, $errstr, $errfile, $errline){
|
994 |
}
|
995 |
public static function wfFunc_IPTraf(){
|
996 |
$IP = $_GET['IP'];
|
997 |
+
$reverseLookup = wfUtils::reverseLookup($IP);
|
998 |
if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){
|
999 |
echo "An invalid IP address was specified.";
|
1000 |
exit(0);
|
1056 |
}
|
1057 |
|
1058 |
$localFile = realpath(ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file']));
|
|
|
|
|
|
|
|
|
|
|
1059 |
$localContents = file_get_contents($localFile);
|
1060 |
+
if($localContents == $result['fileContent']){
|
1061 |
+
$diffResult = '';
|
1062 |
+
} else {
|
1063 |
+
$diff = new Diff(
|
1064 |
+
//Treat DOS and Unix files the same
|
1065 |
+
preg_split("/(?:\r\n|\n)/", $result['fileContent']),
|
1066 |
+
preg_split("/(?:\r\n|\n)/", $localContents),
|
1067 |
+
array()
|
1068 |
+
);
|
1069 |
+
$renderer = new Diff_Renderer_Html_SideBySide;
|
1070 |
+
$diffResult = $diff->Render($renderer);
|
1071 |
+
}
|
1072 |
require 'diffResult.php';
|
1073 |
exit(0);
|
1074 |
}
|
1083 |
} else {
|
1084 |
self::$newVisit = true;
|
1085 |
}
|
1086 |
+
@setcookie($cookieName, uniqid(), time() + 1800, '/');
|
1087 |
}
|
1088 |
public static function admin_init(){
|
1089 |
if(! wfUtils::isAdmin()){ return; }
|
1091 |
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
|
1092 |
}
|
1093 |
|
1094 |
+
if(preg_match('/^Wordfence/', @$_GET['page'])){
|
1095 |
|
1096 |
wp_enqueue_style('wordfence-main-style', WP_PLUGIN_URL . '/wordfence/css/main.css', '', WORDFENCE_VERSION);
|
1097 |
wp_enqueue_style('wordfence-colorbox-style', WP_PLUGIN_URL . '/wordfence/css/colorbox.css', '', WORDFENCE_VERSION);
|
1132 |
}
|
1133 |
}
|
1134 |
add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
|
1135 |
+
add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', WP_PLUGIN_URL . '/wordfence/images/wordfence-logo-16x16.png');
|
1136 |
if(wfConfig::get('liveTrafficEnabled')){
|
1137 |
add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
|
1138 |
}
|
1202 |
return admin_url('admin.php?page=WordfenceSecOpt', 'http');
|
1203 |
}
|
1204 |
|
1205 |
+
public static function alert($subject, $alertMsg, $IP){
|
1206 |
+
$IPMsg = "";
|
1207 |
+
if($IP){
|
1208 |
+
$IPMsg = "User IP: $IP\n";
|
1209 |
+
$reverse = wfUtils::reverseLookup($IP);
|
1210 |
+
if($reverse){
|
1211 |
+
$IPMsg .= "User hostname: " . $reverse . "\n";
|
1212 |
+
}
|
1213 |
+
$userLoc = wfUtils::getIPGeo($IP);
|
1214 |
+
if($userLoc){
|
1215 |
+
$IPMsg .= "User location: ";
|
1216 |
+
if($userLoc['city']){
|
1217 |
+
$IPMsg .= $userLoc['city'] . ', ';
|
1218 |
+
}
|
1219 |
+
$IPMsg .= $userLoc['countryName'] . "\n";
|
1220 |
+
}
|
1221 |
+
}
|
1222 |
$content = wfUtils::tmpl('email_genericAlert.php', array(
|
1223 |
'subject' => $subject,
|
1224 |
'blogName' => get_bloginfo('name', 'raw'),
|
1225 |
'alertMsg' => $alertMsg,
|
1226 |
+
'IPMsg' => $IPMsg,
|
1227 |
'date' => date('l jS \of F Y \a\t h:i:s A'),
|
1228 |
'myHomeURL' => self::getMyHomeURL(),
|
1229 |
'myOptionsURL' => self::getMyOptionsURL()
|
1230 |
));
|
1231 |
$emails = wfConfig::getAlertEmails();
|
1232 |
if(sizeof($emails) < 1){ return; }
|
1233 |
+
$shortSiteURL = preg_replace('/^https?:\/\//i', '', site_url());
|
1234 |
+
$subject = "[Wordfence Alert] $shortSiteURL " . $subject;
|
1235 |
wp_mail(implode(',', $emails), $subject, $content);
|
1236 |
}
|
1237 |
public static function scheduleNextScan($force = false){
|
1263 |
}
|
1264 |
return self::$wfLog;
|
1265 |
}
|
1266 |
+
public static function statusPrep(){
|
1267 |
+
wfConfig::set_ser('wfStatusStartMsgs', array());
|
1268 |
+
wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
|
1269 |
+
}
|
1270 |
+
//In the following functions statusStartMsgs is serialized into the DB so it persists between forks
|
1271 |
public static function statusStart($msg){
|
1272 |
+
$statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
|
1273 |
+
$statusStartMsgs[] = $msg;
|
1274 |
+
wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
|
1275 |
self::status(10, 'info', 'SUM_START:' . $msg);
|
1276 |
+
return sizeof($statusStartMsgs) - 1;
|
1277 |
}
|
1278 |
public static function statusEnd($idx, $haveIssues){
|
1279 |
+
$statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
|
1280 |
if($haveIssues){
|
1281 |
+
self::status(10, 'info', 'SUM_ENDBAD:' . $statusStartMsgs[$idx]);
|
1282 |
} else {
|
1283 |
+
self::status(10, 'info', 'SUM_ENDOK:' . $statusStartMsgs[$idx]);
|
1284 |
}
|
1285 |
+
$statusStartMsgs[$idx] = '';
|
1286 |
+
wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
|
1287 |
}
|
1288 |
public static function statusEndErr(){
|
1289 |
+
$statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
|
1290 |
+
for($i = 0; $i < sizeof($statusStartMsgs); $i++){
|
1291 |
+
if(empty($statusStartMsgs[$i]) === false){
|
1292 |
+
self::status(10, 'info', 'SUM_ENDERR:' . $statusStartMsgs[$i]);
|
1293 |
+
$statusStartMsgs[$i] = '';
|
1294 |
}
|
1295 |
}
|
1296 |
}
|
lib/wordfenceConstants.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<?php
|
2 |
-
define('WORDFENCE_API_VERSION', 1.
|
3 |
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_MAX_SCAN_TIME', 600);
|
5 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
1 |
<?php
|
2 |
+
define('WORDFENCE_API_VERSION', 1.6);
|
3 |
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_MAX_SCAN_TIME', 600);
|
5 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
lib/wordfenceHash.php
CHANGED
@@ -1,7 +1,10 @@
|
|
1 |
<?php
|
2 |
require_once('wordfenceClass.php');
|
3 |
class wordfenceHash {
|
|
|
|
|
4 |
private $whitespace = array("\n","\r","\t"," ");
|
|
|
5 |
public $totalData = 0; //To do a sanity check, don't use 'du' because it gets sparse files wrong and reports blocks used on disk. Use : find . -type f -ls | awk '{total += $7} END {print total}'
|
6 |
public $totalFiles = 0;
|
7 |
public $totalDirs = 0;
|
@@ -9,10 +12,16 @@ class wordfenceHash {
|
|
9 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
10 |
public $striplen = 0;
|
11 |
private $hashes = array();
|
|
|
|
|
|
|
12 |
public function __construct($striplen){
|
13 |
$this->striplen = $striplen;
|
14 |
}
|
15 |
-
public function
|
|
|
|
|
|
|
16 |
if($path[strlen($path) - 1] != '/'){
|
17 |
$path .= '/';
|
18 |
}
|
@@ -29,6 +38,13 @@ class wordfenceHash {
|
|
29 |
wordfence::status(2, 'info', "Hashing item in base dir: $file");
|
30 |
$this->_dirHash($file);
|
31 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
return $this->hashes;
|
33 |
}
|
34 |
private function _dirHash($path){
|
@@ -46,17 +62,20 @@ class wordfenceHash {
|
|
46 |
if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
|
47 |
$file = $path . $cont[$i];
|
48 |
if(is_file($file)){
|
49 |
-
$this->
|
50 |
} else if(is_dir($file)) {
|
51 |
$this->_dirHash($file);
|
52 |
}
|
53 |
}
|
54 |
} else {
|
55 |
if(is_file($path)){
|
56 |
-
$this->
|
57 |
}
|
58 |
}
|
59 |
}
|
|
|
|
|
|
|
60 |
private function processFile($file){
|
61 |
if(@filesize($file) > WORDFENCE_MAX_FILE_SIZE_TO_PROCESS){
|
62 |
wordfence::status(2, 'info', "Skipping file larger than 50 megs: $file");
|
1 |
<?php
|
2 |
require_once('wordfenceClass.php');
|
3 |
class wordfenceHash {
|
4 |
+
|
5 |
+
//Begin serialized vars
|
6 |
private $whitespace = array("\n","\r","\t"," ");
|
7 |
+
private $fileQ = array();
|
8 |
public $totalData = 0; //To do a sanity check, don't use 'du' because it gets sparse files wrong and reports blocks used on disk. Use : find . -type f -ls | awk '{total += $7} END {print total}'
|
9 |
public $totalFiles = 0;
|
10 |
public $totalDirs = 0;
|
12 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
13 |
public $striplen = 0;
|
14 |
private $hashes = array();
|
15 |
+
public function __sleep(){ //same order as above
|
16 |
+
return array('whitespace', 'fileQ', 'totalData', 'totalFiles', 'totalDirs', 'linesOfPHP', 'linesOfJCH', 'striplen', 'hashes');
|
17 |
+
}
|
18 |
public function __construct($striplen){
|
19 |
$this->striplen = $striplen;
|
20 |
}
|
21 |
+
public function __wakeup(){
|
22 |
+
|
23 |
+
}
|
24 |
+
public function buildFileQueue($path, $only = array()){ //base path and 'only' is a list of files and dirs in the bast that are the only ones that should be processed. Everything else in base is ignored. If only is empty then everything is processed.
|
25 |
if($path[strlen($path) - 1] != '/'){
|
26 |
$path .= '/';
|
27 |
}
|
38 |
wordfence::status(2, 'info', "Hashing item in base dir: $file");
|
39 |
$this->_dirHash($file);
|
40 |
}
|
41 |
+
|
42 |
+
}
|
43 |
+
public function genHashes($forkObj){
|
44 |
+
while($file = array_shift($this->fileQ)){
|
45 |
+
$this->processFile($file);
|
46 |
+
$forkObj->forkIfNeeded();
|
47 |
+
}
|
48 |
return $this->hashes;
|
49 |
}
|
50 |
private function _dirHash($path){
|
62 |
if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
|
63 |
$file = $path . $cont[$i];
|
64 |
if(is_file($file)){
|
65 |
+
$this->qFile($file);
|
66 |
} else if(is_dir($file)) {
|
67 |
$this->_dirHash($file);
|
68 |
}
|
69 |
}
|
70 |
} else {
|
71 |
if(is_file($path)){
|
72 |
+
$this->qFile($path);
|
73 |
}
|
74 |
}
|
75 |
}
|
76 |
+
private function qFile($file){
|
77 |
+
$this->fileQ[] = $file;
|
78 |
+
}
|
79 |
private function processFile($file){
|
80 |
if(@filesize($file) > WORDFENCE_MAX_FILE_SIZE_TO_PROCESS){
|
81 |
wordfence::status(2, 'info', "Skipping file larger than 50 megs: $file");
|
lib/wordfenceScanner.php
CHANGED
@@ -5,28 +5,30 @@ require_once('wordfenceURLHoover.php');
|
|
5 |
class wordfenceScanner {
|
6 |
protected $path = '';
|
7 |
protected $fileList = array();
|
8 |
-
protected $hostFileList = array();
|
9 |
-
protected $urlFileList = array();
|
10 |
protected $results = array();
|
11 |
public $errorMsg = false;
|
12 |
private $apiKey = false;
|
13 |
private $wordpressVersion = '';
|
14 |
-
public function
|
|
|
|
|
|
|
|
|
|
|
15 |
$this->apiKey = $apiKey;
|
16 |
$this->wordpressVersion = $wordpressVersion;
|
17 |
-
|
18 |
-
public function scan($path, $fileList){
|
19 |
-
$this->errorMsg = false;
|
20 |
if($path[strlen($path) - 1] != '/'){
|
21 |
$path .= '/';
|
22 |
}
|
23 |
-
$this->hostFileList = array();
|
24 |
$this->path = $path;
|
25 |
-
$this->fileList = $fileList;
|
26 |
$this->results = array();
|
|
|
27 |
//First extract hosts or IP's and their URL's into $this->hostsFound and URL's into $this->urlsFound
|
28 |
-
$urlHoover = new wordfenceURLHoover($this->apiKey, $this->wordpressVersion);
|
29 |
-
|
|
|
|
|
30 |
if(! file_exists($this->path . $file)){
|
31 |
continue;
|
32 |
}
|
@@ -107,9 +109,9 @@ class wordfenceScanner {
|
|
107 |
break;
|
108 |
}
|
109 |
|
110 |
-
$urlHoover->hoover($file, $data);
|
111 |
} else {
|
112 |
-
$urlHoover->hoover($file, $data);
|
113 |
}
|
114 |
|
115 |
if($totalRead > 2 * 1024 * 1024){
|
@@ -118,14 +120,15 @@ class wordfenceScanner {
|
|
118 |
}
|
119 |
fclose($fh);
|
120 |
$mtime = sprintf("%.5f", microtime(true) - $stime);
|
|
|
121 |
}
|
122 |
if(function_exists('memory_get_usage')){
|
123 |
wordfence::status(3, 'info', "Total memory being used: " . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . "MB");
|
124 |
}
|
125 |
wordfence::status(2, 'info', "Asking Wordfence to check URL's against malware list.");
|
126 |
-
$hooverResults = $urlHoover->getBaddies();
|
127 |
-
if($urlHoover->errorMsg){
|
128 |
-
$this->errorMsg = $urlHoover->errorMsg;
|
129 |
return false;
|
130 |
}
|
131 |
foreach($hooverResults as $file => $hresults){
|
5 |
class wordfenceScanner {
|
6 |
protected $path = '';
|
7 |
protected $fileList = array();
|
|
|
|
|
8 |
protected $results = array();
|
9 |
public $errorMsg = false;
|
10 |
private $apiKey = false;
|
11 |
private $wordpressVersion = '';
|
12 |
+
public function __sleep(){
|
13 |
+
return array('path', 'fileList', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover');
|
14 |
+
}
|
15 |
+
public function __wakeup(){
|
16 |
+
}
|
17 |
+
public function __construct($apiKey, $wordpressVersion, $fileList, $path){
|
18 |
$this->apiKey = $apiKey;
|
19 |
$this->wordpressVersion = $wordpressVersion;
|
20 |
+
$this->fileList = $fileList;
|
|
|
|
|
21 |
if($path[strlen($path) - 1] != '/'){
|
22 |
$path .= '/';
|
23 |
}
|
|
|
24 |
$this->path = $path;
|
|
|
25 |
$this->results = array();
|
26 |
+
$this->errorMsg = false;
|
27 |
//First extract hosts or IP's and their URL's into $this->hostsFound and URL's into $this->urlsFound
|
28 |
+
$this->urlHoover = new wordfenceURLHoover($this->apiKey, $this->wordpressVersion);
|
29 |
+
}
|
30 |
+
public function scan($forkObj){
|
31 |
+
while($file = array_shift($this->fileList)){
|
32 |
if(! file_exists($this->path . $file)){
|
33 |
continue;
|
34 |
}
|
109 |
break;
|
110 |
}
|
111 |
|
112 |
+
$this->urlHoover->hoover($file, $data);
|
113 |
} else {
|
114 |
+
$this->urlHoover->hoover($file, $data);
|
115 |
}
|
116 |
|
117 |
if($totalRead > 2 * 1024 * 1024){
|
120 |
}
|
121 |
fclose($fh);
|
122 |
$mtime = sprintf("%.5f", microtime(true) - $stime);
|
123 |
+
$forkObj->forkIfNeeded();
|
124 |
}
|
125 |
if(function_exists('memory_get_usage')){
|
126 |
wordfence::status(3, 'info', "Total memory being used: " . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . "MB");
|
127 |
}
|
128 |
wordfence::status(2, 'info', "Asking Wordfence to check URL's against malware list.");
|
129 |
+
$hooverResults = $this->urlHoover->getBaddies();
|
130 |
+
if($this->urlHoover->errorMsg){
|
131 |
+
$this->errorMsg = $this->urlHoover->errorMsg;
|
132 |
return false;
|
133 |
}
|
134 |
foreach($hooverResults as $file => $hresults){
|
lib/wordfenceURLHoover.php
CHANGED
@@ -2,13 +2,24 @@
|
|
2 |
require_once('wfAPI.php');
|
3 |
class wordfenceURLHoover {
|
4 |
private $debug = false;
|
5 |
-
private $URLsByID = array();
|
6 |
public $errorMsg = false;
|
7 |
-
private $hostKeyCache = array();
|
8 |
-
private $api = false;
|
9 |
private $table = '';
|
|
|
|
|
10 |
private $dRegex = 'aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw|xn--lgbbat1ad8j|xn--fiqs8s|xn--fiqz9s|xn--wgbh1c|xn--j6w193g|xn--h2brj9c|xn--mgbbh1a71e|xn--fpcrj9c3d|xn--gecrj9c|xn--s9brj9c|xn--xkc2dl3a5ee0h|xn--45brj9c|xn--mgba3a4f16a|xn--mgbayh7gpa|xn--mgbc0a9azcg|xn--ygbi2ammx|xn--wgbl6a|xn--p1ai|xn--mgberp4a5d4ar|xn--90a3ac|xn--yfro4i67o|xn--clchc0ea0b2g2a9gcd|xn--3e0b707e|xn--fzc2c9e2c|xn--xkc2al3hye2a|xn--mgbtf8fl|xn--kprw13d|xn--kpry57d|xn--o3cw4h|xn--pgbs0dh|xn--mgbaam7a8h|xn--54b7fta0cc|xn--90ae|xn--node|xn--4dbrk0ce|xn--80ao21a|xn--mgb9awbf|xn--mgbai9azgqp6j|xn--j1amh|xn--mgb2ddes|xn--kgbechtv|xn--hgbk6aj7f53bba|xn--0zwm56d|xn--g6w251d|xn--80akhbyknj4f|xn--11b5bs3a9aj6g|xn--jxalpdlp|xn--9t4b11yi5a|xn--deba0ad|xn--zckzah|xn--hlcj6aya9esc7a';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
public function __construct($apiKey, $wordpressVersion){
|
|
|
|
|
12 |
$this->api = new wfAPI($apiKey, $wordpressVersion);
|
13 |
$this->db = new wfDB();
|
14 |
global $wpdb;
|
@@ -65,9 +76,11 @@ class wordfenceURLHoover {
|
|
65 |
return true;
|
66 |
}
|
67 |
private function makeHostKey($host){
|
|
|
68 |
if(isset($this->hostKeyCache[$host])){
|
69 |
return $this->hostKeyCache[$host];
|
70 |
}
|
|
|
71 |
$hostParts = explode('.', $host);
|
72 |
$hostKey = '';
|
73 |
if(sizeof($hostParts) == 2){
|
@@ -75,7 +88,7 @@ class wordfenceURLHoover {
|
|
75 |
} else if(sizeof($hostParts) > 2){
|
76 |
$hostKey = substr(hash('sha256', $hostParts[sizeof($hostParts) - 3] . '.' . $hostParts[sizeof($hostParts) - 2] . '.' . $hostParts[sizeof($hostParts) - 1] . '/', true), 0, 4);
|
77 |
}
|
78 |
-
|
79 |
return $hostKey;
|
80 |
}
|
81 |
public function getBaddies(){
|
2 |
require_once('wfAPI.php');
|
3 |
class wordfenceURLHoover {
|
4 |
private $debug = false;
|
|
|
5 |
public $errorMsg = false;
|
6 |
+
//private $hostKeyCache = array();
|
|
|
7 |
private $table = '';
|
8 |
+
private $apiKey = false;
|
9 |
+
private $wordpressVersion = false;
|
10 |
private $dRegex = 'aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw|xn--lgbbat1ad8j|xn--fiqs8s|xn--fiqz9s|xn--wgbh1c|xn--j6w193g|xn--h2brj9c|xn--mgbbh1a71e|xn--fpcrj9c3d|xn--gecrj9c|xn--s9brj9c|xn--xkc2dl3a5ee0h|xn--45brj9c|xn--mgba3a4f16a|xn--mgbayh7gpa|xn--mgbc0a9azcg|xn--ygbi2ammx|xn--wgbl6a|xn--p1ai|xn--mgberp4a5d4ar|xn--90a3ac|xn--yfro4i67o|xn--clchc0ea0b2g2a9gcd|xn--3e0b707e|xn--fzc2c9e2c|xn--xkc2al3hye2a|xn--mgbtf8fl|xn--kprw13d|xn--kpry57d|xn--o3cw4h|xn--pgbs0dh|xn--mgbaam7a8h|xn--54b7fta0cc|xn--90ae|xn--node|xn--4dbrk0ce|xn--80ao21a|xn--mgb9awbf|xn--mgbai9azgqp6j|xn--j1amh|xn--mgb2ddes|xn--kgbechtv|xn--hgbk6aj7f53bba|xn--0zwm56d|xn--g6w251d|xn--80akhbyknj4f|xn--11b5bs3a9aj6g|xn--jxalpdlp|xn--9t4b11yi5a|xn--deba0ad|xn--zckzah|xn--hlcj6aya9esc7a';
|
11 |
+
private $api = false;
|
12 |
+
private $db = false;
|
13 |
+
public function __sleep(){
|
14 |
+
return array('debug', 'errorMsg', 'table', 'apiKey', 'wordpressVersion', 'dRegex');
|
15 |
+
}
|
16 |
+
public function __wakeup(){
|
17 |
+
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
|
18 |
+
$this->db = new wfDB();
|
19 |
+
}
|
20 |
public function __construct($apiKey, $wordpressVersion){
|
21 |
+
$this->apiKey = $apiKey;
|
22 |
+
$this->wordpressVersion = $wordpressVersion;
|
23 |
$this->api = new wfAPI($apiKey, $wordpressVersion);
|
24 |
$this->db = new wfDB();
|
25 |
global $wpdb;
|
76 |
return true;
|
77 |
}
|
78 |
private function makeHostKey($host){
|
79 |
+
/*
|
80 |
if(isset($this->hostKeyCache[$host])){
|
81 |
return $this->hostKeyCache[$host];
|
82 |
}
|
83 |
+
*/
|
84 |
$hostParts = explode('.', $host);
|
85 |
$hostKey = '';
|
86 |
if(sizeof($hostParts) == 2){
|
88 |
} else if(sizeof($hostParts) > 2){
|
89 |
$hostKey = substr(hash('sha256', $hostParts[sizeof($hostParts) - 3] . '.' . $hostParts[sizeof($hostParts) - 2] . '.' . $hostParts[sizeof($hostParts) - 1] . '/', true), 0, 4);
|
90 |
}
|
91 |
+
//$this->hostKeyCache[$host] = $hostKey;
|
92 |
return $hostKey;
|
93 |
}
|
94 |
public function getBaddies(){
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
|
|
3 |
Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
|
4 |
Requires at least: 3.3.1
|
5 |
Tested up to: 3.3.2
|
6 |
-
Stable tag: 2.0
|
7 |
|
8 |
Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
|
9 |
|
@@ -152,6 +152,24 @@ or a theme, because often these have been updated to fix a security hole.
|
|
152 |
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
|
153 |
|
154 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
= 2.0.7 =
|
156 |
* Fixed CSS bug that changed plugins page layout in admin area
|
157 |
* Added memory benchmark utility.
|
3 |
Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
|
4 |
Requires at least: 3.3.1
|
5 |
Tested up to: 3.3.2
|
6 |
+
Stable tag: 2.1.0
|
7 |
|
8 |
Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
|
9 |
|
152 |
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
|
153 |
|
154 |
== Changelog ==
|
155 |
+
= 2.1.0 =
|
156 |
+
* Fixed scans hanging on Dreamhost and other hosts.
|
157 |
+
* Made Wordfence more memory efficient.
|
158 |
+
* Wordfence scans are now broken into steps so we can scan a huge number of files, posts and comments.
|
159 |
+
* Alert emails now include IP address, hostname lookup and geographic location (city if available).
|
160 |
+
* Improved scan locking. No longer time based but uses flock() if on unix or time on Windows.
|
161 |
+
* Suppressed warnings that WF was generating.
|
162 |
+
* Improve handling of non-standard wp-content directories.
|
163 |
+
* Fix restored files were still showing as changed if they contained international characters.
|
164 |
+
* Improve permission denied message if attempting to repair a file.
|
165 |
+
* Fixed problem that caused scans to not start because some hosts take too long to look up their own name.
|
166 |
+
* Fixed issue with Wordfence menu that caused it to not appear or conflict with other menus under certain conditions.
|
167 |
+
* Upgraded to API version 1.6
|
168 |
+
* Improved geo lookup code for IP's.
|
169 |
+
* Fixed debug mode output in live status box - coloring was wrong.
|
170 |
+
* Added ajax status message to WF admin pages.
|
171 |
+
* Fixed colorbox popup so that it doesn't jump around on refresh.
|
172 |
+
|
173 |
= 2.0.7 =
|
174 |
* Fixed CSS bug that changed plugins page layout in admin area
|
175 |
* Added memory benchmark utility.
|
wfscan.php
CHANGED
@@ -68,11 +68,9 @@ class wfScan {
|
|
68 |
self::becomeAdmin();
|
69 |
|
70 |
wordfence::status(4, 'info', "Checking if scan is already running");
|
71 |
-
|
72 |
-
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
|
73 |
self::errorExit("There is already a scan running.");
|
74 |
}
|
75 |
-
wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
|
76 |
wordfence::status(4, 'info', "Requesting max memory");
|
77 |
wfUtils::requestMaxMemory();
|
78 |
wordfence::status(4, 'info', "Setting up error handling environment");
|
@@ -84,11 +82,23 @@ class wfScan {
|
|
84 |
@error_reporting(E_ALL);
|
85 |
@ini_set('display_errors','On');
|
86 |
wordfence::status(4, 'info', "Setting up scanRunning and starting scan");
|
87 |
-
|
88 |
-
$scan =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
$scan->go();
|
90 |
-
|
91 |
-
wfConfig::set('wf_scanRunning', '');
|
92 |
}
|
93 |
public static function obHandler($buf){
|
94 |
if(strlen($buf) > 1000){
|
@@ -102,7 +112,7 @@ class wfScan {
|
|
102 |
wordfence::status(1, 'error', "$errstr ($errno) File: $errfile Line: $errline");
|
103 |
}
|
104 |
public static function shutdown(){
|
105 |
-
|
106 |
}
|
107 |
private static function errorExit($msg){
|
108 |
echo json_encode(array('errorMsg' => $msg));
|
68 |
self::becomeAdmin();
|
69 |
|
70 |
wordfence::status(4, 'info', "Checking if scan is already running");
|
71 |
+
if(! wfUtils::getScanLock()){
|
|
|
72 |
self::errorExit("There is already a scan running.");
|
73 |
}
|
|
|
74 |
wordfence::status(4, 'info', "Requesting max memory");
|
75 |
wfUtils::requestMaxMemory();
|
76 |
wordfence::status(4, 'info', "Setting up error handling environment");
|
82 |
@error_reporting(E_ALL);
|
83 |
@ini_set('display_errors','On');
|
84 |
wordfence::status(4, 'info', "Setting up scanRunning and starting scan");
|
85 |
+
$isFork = ($_GET['isFork'] == '1' ? true : false);
|
86 |
+
$scan = wfConfig::get_ser('wfsd_engine', false);
|
87 |
+
if($scan){
|
88 |
+
//Set false so that we don't get stuck in a loop where we're repeating scan stages.
|
89 |
+
wfConfig::set('wfsd_engine', '');
|
90 |
+
} else {
|
91 |
+
if($isFork){ //We encountered an error so blank scan and exit
|
92 |
+
wordfence::status(2, 'error', "Scan could not continue because the stored data could not be retrieved after a fork.");
|
93 |
+
//wfConfig::set('wfsd_engine', '');
|
94 |
+
exit();
|
95 |
+
} else {
|
96 |
+
wordfence::statusPrep(); //Re-initializes all status counters
|
97 |
+
$scan = new wfScanEngine();
|
98 |
+
}
|
99 |
+
}
|
100 |
$scan->go();
|
101 |
+
wfUtils::clearScanLock();
|
|
|
102 |
}
|
103 |
public static function obHandler($buf){
|
104 |
if(strlen($buf) > 1000){
|
112 |
wordfence::status(1, 'error', "$errstr ($errno) File: $errfile Line: $errline");
|
113 |
}
|
114 |
public static function shutdown(){
|
115 |
+
wfUtils::clearScanLock();
|
116 |
}
|
117 |
private static function errorExit($msg){
|
118 |
echo json_encode(array('errorMsg' => $msg));
|
wordfence.php
CHANGED
@@ -4,11 +4,10 @@ Plugin Name: Wordfence Security
|
|
4 |
Plugin URI: http://wordfence.com/
|
5 |
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
|
6 |
Author: Mark Maunder
|
7 |
-
Version: 2.0
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
-
define('WORDFENCE_VERSION', '2.0
|
11 |
-
|
12 |
|
13 |
require_once('lib/wordfenceConstants.php');
|
14 |
require_once('lib/wordfenceClass.php');
|
4 |
Plugin URI: http://wordfence.com/
|
5 |
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
|
6 |
Author: Mark Maunder
|
7 |
+
Version: 2.1.0
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
+
define('WORDFENCE_VERSION', '2.1.0');
|
|
|
11 |
|
12 |
require_once('lib/wordfenceConstants.php');
|
13 |
require_once('lib/wordfenceClass.php');
|