Version Description
- A complete rearchitecture of Wordfence scanning to massively improve performance.
- Our free customers are now 100% back in business. Apologies for the delay, but this was worth the wait.
- Wordfence is now 4X faster for both free and paid customers.
- Significantly reduced CPU and memory overhead.
- Significantly reduced network througput when communicating with Wordfence scanning servers.
- Big performance improvement on our own scanning servers which allows us to continue to provide Wordfence free for the forseeable future.
- Upgraded scanning API to version 2.4
- Upgraded Geo IP database to October version.
- Moved core, theme, plugin and malware scanning into hashing recursive routine for big performance gain.
- Removed need for fileQ in hashing routine for reduction in memory usage and reduction in DB write size.
- Removed send-packet architecture and now processing files locally by fetching comparison data from scanning server instead.
- Removed wfModTracker - old module that is no longer used.
- Malware is now scanned by fetching hash prefixes from WF server instead of sending hashes of every file to our server. Much more efficient.
- Made status messages in summary console a little more user friendly.
Download this release
Release Info
Developer | mmaunder |
Plugin | Wordfence Security – Firewall & Malware Scan |
Version | 3.3.2 |
Comparing to | |
See all releases |
Code changes from version 3.2.7 to 3.3.2
- js/admin.js +9 -1
- lib/GeoIP.dat +0 -0
- lib/wfAPI.php +5 -2
- lib/wfModTracker.php +0 -125
- lib/wfScanEngine.php +42 -131
- lib/wfSchema.php +6 -3
- lib/wordfenceClass.php +14 -4
- lib/wordfenceConstants.php +1 -1
- lib/wordfenceHash.php +223 -131
- lib/wordfenceScanner.php +9 -20
- readme.txt +18 -2
- wordfence.php +2 -2
js/admin.js
CHANGED
@@ -150,7 +150,7 @@ window['wordfenceAdmin'] = {
|
|
150 |
pointerWidth: 400,
|
151 |
position: {
|
152 |
edge: edge,
|
153 |
-
align: align
|
154 |
}
|
155 |
};
|
156 |
this.currentPointer = jQuery('#' + elemID).pointer(options).pointer('open');
|
@@ -269,10 +269,18 @@ window['wordfenceAdmin'] = {
|
|
269 |
var msg = item.msg.replace('SUM_ENDBAD:', '');
|
270 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
|
271 |
summaryUpdated = true;
|
|
|
|
|
|
|
|
|
272 |
} else if(item.msg.indexOf('SUM_ENDOK') != -1){
|
273 |
var msg = item.msg.replace('SUM_ENDOK:', '');
|
274 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
|
275 |
summaryUpdated = true;
|
|
|
|
|
|
|
|
|
276 |
} else if(item.msg.indexOf('SUM_ENDERR') != -1){
|
277 |
var msg = item.msg.replace('SUM_ENDERR:', '');
|
278 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occurred.');
|
150 |
pointerWidth: 400,
|
151 |
position: {
|
152 |
edge: edge,
|
153 |
+
align: align
|
154 |
}
|
155 |
};
|
156 |
this.currentPointer = jQuery('#' + elemID).pointer(options).pointer('open');
|
269 |
var msg = item.msg.replace('SUM_ENDBAD:', '');
|
270 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
|
271 |
summaryUpdated = true;
|
272 |
+
} else if(item.msg.indexOf('SUM_ENDFAILED') != -1){
|
273 |
+
var msg = item.msg.replace('SUM_ENDFAILED:', '');
|
274 |
+
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Failed.');
|
275 |
+
summaryUpdated = true;
|
276 |
} else if(item.msg.indexOf('SUM_ENDOK') != -1){
|
277 |
var msg = item.msg.replace('SUM_ENDOK:', '');
|
278 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
|
279 |
summaryUpdated = true;
|
280 |
+
} else if(item.msg.indexOf('SUM_ENDSUCCESS') != -1){
|
281 |
+
var msg = item.msg.replace('SUM_ENDSUCCESS:', '');
|
282 |
+
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Success.');
|
283 |
+
summaryUpdated = true;
|
284 |
} else if(item.msg.indexOf('SUM_ENDERR') != -1){
|
285 |
var msg = item.msg.replace('SUM_ENDERR:', '');
|
286 |
jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occurred.');
|
lib/GeoIP.dat
CHANGED
Binary file
|
lib/wfAPI.php
CHANGED
@@ -13,6 +13,9 @@ class wfAPI {
|
|
13 |
$this->APIKey = $apiKey;
|
14 |
$this->wordpressVersion = $wordpressVersion;
|
15 |
}
|
|
|
|
|
|
|
16 |
public function call($action, $getParams = array(), $postParams = array()){
|
17 |
$json = $this->getURL($this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . http_build_query(
|
18 |
array_merge(
|
@@ -45,7 +48,7 @@ class wfAPI {
|
|
45 |
$this->curlDataWritten = 0;
|
46 |
$this->curlContent = "";
|
47 |
$curl = curl_init($url);
|
48 |
-
curl_setopt ($curl, CURLOPT_TIMEOUT,
|
49 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]') );
|
50 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
51 |
curl_setopt ($curl, CURLOPT_HEADER, 0);
|
@@ -106,7 +109,7 @@ class wfAPI {
|
|
106 |
$url = $this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
|
107 |
if(function_exists('curl_init')){
|
108 |
$curl = curl_init($url);
|
109 |
-
curl_setopt ($curl, CURLOPT_TIMEOUT,
|
110 |
//curl_setopt($curl, CURLOPT_VERBOSE, true);
|
111 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
|
112 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
13 |
$this->APIKey = $apiKey;
|
14 |
$this->wordpressVersion = $wordpressVersion;
|
15 |
}
|
16 |
+
public function getStaticURL($url){ // In the form '/something.bin' without quotes
|
17 |
+
return $this->getURL($this->getAPIURL() . $url);
|
18 |
+
}
|
19 |
public function call($action, $getParams = array(), $postParams = array()){
|
20 |
$json = $this->getURL($this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . http_build_query(
|
21 |
array_merge(
|
48 |
$this->curlDataWritten = 0;
|
49 |
$this->curlContent = "";
|
50 |
$curl = curl_init($url);
|
51 |
+
curl_setopt ($curl, CURLOPT_TIMEOUT, 900);
|
52 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]') );
|
53 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
54 |
curl_setopt ($curl, CURLOPT_HEADER, 0);
|
109 |
$url = $this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
|
110 |
if(function_exists('curl_init')){
|
111 |
$curl = curl_init($url);
|
112 |
+
curl_setopt ($curl, CURLOPT_TIMEOUT, 900);
|
113 |
//curl_setopt($curl, CURLOPT_VERBOSE, true);
|
114 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
|
115 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
lib/wfModTracker.php
DELETED
@@ -1,125 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
require_once('wordfenceClass.php');
|
3 |
-
class wfModTracker {
|
4 |
-
private $themeSum = false;
|
5 |
-
private $pluginSum = false;
|
6 |
-
private $coreSum = false;
|
7 |
-
private $db = false;
|
8 |
-
private $changesTable = false;
|
9 |
-
private $anyFilesChangedCached = false;
|
10 |
-
public function __construct(){
|
11 |
-
global $wpdb;
|
12 |
-
$this->changesTable = $wpdb->base_prefix . 'wfFileChanges';
|
13 |
-
$this->status(2, 'info', "Getting file change DB handle");
|
14 |
-
$this->db = new wfDB();
|
15 |
-
$this->status(2, 'info', "Starting theme change check");
|
16 |
-
$this->themeSum = $this->makeSum(get_theme_root());
|
17 |
-
$this->status(2, 'info', "Starting plugin change scan");
|
18 |
-
$this->pluginSum = $this->makeSum(WP_PLUGIN_DIR);
|
19 |
-
$this->status(2, 'info', "Starting core file change scan");
|
20 |
-
$this->coreSum = $this->makeCoreSum();
|
21 |
-
$this->allFilesSum = array();
|
22 |
-
$this->status(2, 'info', "Getting changes in all other files");
|
23 |
-
$this->getAllFilesSum(ABSPATH);
|
24 |
-
$this->status(2, 'info', "Done compiling file changes");
|
25 |
-
}
|
26 |
-
public static function resetChanges(){
|
27 |
-
wfConfig::set('wfmdt_coreSum', '');
|
28 |
-
wfConfig::set('wfmdt_themeSum', '');
|
29 |
-
wfConfig::set('wfmdt_pluginSum', '');
|
30 |
-
$db = new wfDB();
|
31 |
-
global $wpdb;
|
32 |
-
$db->query("delete from " . $wpdb->base_prefix . 'wfFileChanges');
|
33 |
-
}
|
34 |
-
public function filesModifiedInCore(){ if(wfConfig::get('wfmdt_coreSum') != $this->coreSum){ return true; } else { return false; } }
|
35 |
-
public function filesModifiedInThemes(){ if(wfConfig::get('wfmdt_themeSum') != $this->themeSum){ return true; } else { return false; } }
|
36 |
-
public function filesModifiedInPlugins(){ if(wfConfig::get('wfmdt_pluginSum') != $this->pluginSum){ return true; } else { return false; } }
|
37 |
-
public function getChangedFiles($stripPath, $filterOutFiles){
|
38 |
-
$changed = array();
|
39 |
-
foreach($this->allFilesSum as $file => $md5){
|
40 |
-
if(in_array($file, $filterOutFiles)){ continue; }
|
41 |
-
$dbSig = $this->db->querySingle("select md5 from " . $this->changesTable . " where filenameHash='%s'", hash('sha256', $file));
|
42 |
-
if($dbSig != $md5){
|
43 |
-
$changed[] = substr($file, strlen($stripPath) - 1);
|
44 |
-
}
|
45 |
-
}
|
46 |
-
return $changed;
|
47 |
-
}
|
48 |
-
public function anyFilesChanged(){
|
49 |
-
if(! $this->anyFilesChangedCached){
|
50 |
-
$changed = false;
|
51 |
-
$q = $this->db->query("select file, md5 from " . $this->changesTable);
|
52 |
-
$knownDBFiles = array();
|
53 |
-
while($row = mysql_fetch_assoc($q)){
|
54 |
-
$knownDBFiles[$row['file']] = true;
|
55 |
-
if( (! isset($this->allFilesSum[$row['file']])) || $this->allFilesSum[$row['file']] != $row['md5']){
|
56 |
-
$changed = true;
|
57 |
-
//Can't break because we need to populate all of knownDBFiles
|
58 |
-
}
|
59 |
-
}
|
60 |
-
foreach($this->allFilesSum as $file => $md5){
|
61 |
-
if(! isset($knownDBFiles[$file])){
|
62 |
-
//We have a new file the DB doesn't know about
|
63 |
-
$changed = true;
|
64 |
-
break;
|
65 |
-
}
|
66 |
-
}
|
67 |
-
$this->anyFilesChangedCached = $changed ? 'true' : 'false';
|
68 |
-
}
|
69 |
-
return $this->anyFilesChangedCached == 'true' ? true : false;
|
70 |
-
}
|
71 |
-
public function logCurrentState(){
|
72 |
-
wfConfig::set('wfmdt_coreSum', $this->coreSum);
|
73 |
-
wfConfig::set('wfmdt_themeSum', $this->themeSum);
|
74 |
-
wfConfig::set('wfmdt_pluginSum', $this->pluginSum);
|
75 |
-
foreach($this->allFilesSum as $file => $md5){
|
76 |
-
$this->db->query("insert into " . $this->changesTable . " (file, md5, filenameHash) values ('%s', '%s', '%s') ON DUPLICATE KEY UPDATE md5='%s'", $file, $md5, hash('sha256', $file), $md5);
|
77 |
-
}
|
78 |
-
$q = $this->db->query("select file from " . $this->changesTable);
|
79 |
-
while($row = mysql_fetch_assoc($q)){
|
80 |
-
if(! isset($this->allFilesSum[$row['file']])){
|
81 |
-
$this->db->query("delete from " . $this->changesTable . " where filenameHash='%s'", hash('sha256', $row['file']));
|
82 |
-
}
|
83 |
-
}
|
84 |
-
}
|
85 |
-
private function getAllFilesSum($path){
|
86 |
-
$path = rtrim($path, '/');
|
87 |
-
$files = scandir($path);
|
88 |
-
foreach($files as $file){
|
89 |
-
if($file == '.' || $file == '..'){ continue; }
|
90 |
-
$file = $path . '/' . $file;
|
91 |
-
if(is_file($file)){
|
92 |
-
$md5 = @md5_file($file);
|
93 |
-
if($md5){ $this->allFilesSum[$file] = $md5; }
|
94 |
-
} else if(is_dir($file)){
|
95 |
-
$this->getAllFilesSum($file, $this->allFilesSum);
|
96 |
-
}
|
97 |
-
}
|
98 |
-
}
|
99 |
-
private function makeCoreSum(){
|
100 |
-
return md5(
|
101 |
-
$this->makeSum(ABSPATH, true) . //norecurse
|
102 |
-
$this->makeSum(ABSPATH . '/wp-admin/') .
|
103 |
-
$this->makeSum(ABSPATH . '/wp-includes/')
|
104 |
-
);
|
105 |
-
}
|
106 |
-
public function makeSum($dir, $norecurse = false, $str = ''){
|
107 |
-
$dir = rtrim($dir, '/');
|
108 |
-
$files = scandir($dir);
|
109 |
-
foreach($files as $file){
|
110 |
-
if($file == '.' || $file == '..'){ continue; }
|
111 |
-
$file = $dir . '/' . $file;
|
112 |
-
if(is_file($file)){
|
113 |
-
$md5 = @md5_file($file);
|
114 |
-
if($md5){ $str .= $md5; }
|
115 |
-
} else if((! $norecurse) && is_dir($file)){
|
116 |
-
$str .= md5($this->makeSum($file, $norecurse, $str));
|
117 |
-
}
|
118 |
-
}
|
119 |
-
return md5($str);
|
120 |
-
}
|
121 |
-
private function status($level, $type, $msg){
|
122 |
-
wordfence::status($level, $type, $msg);
|
123 |
-
}
|
124 |
-
}
|
125 |
-
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/wfScanEngine.php
CHANGED
@@ -8,7 +8,7 @@ require_once('wfDB.php');
|
|
8 |
require_once('wfUtils.php');
|
9 |
class wfScanEngine {
|
10 |
private static $cronTestFailedURLs = array();
|
11 |
-
|
12 |
private $dictWords = array();
|
13 |
private $forkRequested = false;
|
14 |
|
@@ -22,12 +22,7 @@ class wfScanEngine {
|
|
22 |
private $startTime = 0;
|
23 |
private $scanStep = 0;
|
24 |
private $maxExecTime = 10; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
|
25 |
-
private $malwareScanEnabled = false;
|
26 |
-
private $pluginScanEnabled = false;
|
27 |
-
private $coreScanEnabled = false;
|
28 |
private $publicScanEnabled = false;
|
29 |
-
private $themeScanEnabled = false;
|
30 |
-
private $unknownFiles = "";
|
31 |
private $fileContentsResults = false;
|
32 |
private $scanner = false;
|
33 |
private $scanQueue = array();
|
@@ -42,7 +37,7 @@ class wfScanEngine {
|
|
42 |
private $userPasswdQueue = "";
|
43 |
private $passwdHasIssues = false;
|
44 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
45 |
-
return array('hasher', 'hashes', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'scanStep', 'maxExecTime', 'publicScanEnabled', '
|
46 |
}
|
47 |
public function __construct(){
|
48 |
$this->startTime = time();
|
@@ -55,7 +50,7 @@ class wfScanEngine {
|
|
55 |
include('wfDict.php'); //$dictWords
|
56 |
$this->dictWords = $dictWords;
|
57 |
$this->jobList[] = 'publicSite';
|
58 |
-
|
59 |
foreach(array('fileContents', 'posts', 'comments', 'passwds', 'dns', 'diskSpace', 'oldVersions') as $scanType){
|
60 |
if(wfConfig::get('scansEnabled_' . $scanType)){
|
61 |
if(method_exists($this, 'scan_' . $scanType . '_init')){
|
@@ -153,47 +148,9 @@ class wfScanEngine {
|
|
153 |
sleep(2); //enough time to read the message before it scrolls off.
|
154 |
}
|
155 |
}
|
156 |
-
private function
|
157 |
$this->status(1, 'info', "Contacting Wordfence to initiate scan");
|
158 |
$this->api->call('log_scan', array(), array());
|
159 |
-
if(wfConfig::get('scansEnabled_core')){
|
160 |
-
$this->coreScanEnabled = true;
|
161 |
-
$this->statusIDX['core'] = wordfence::statusStart("Comparing core WordPress files against originals in repository");
|
162 |
-
} else {
|
163 |
-
wordfence::statusDisabled("Skipping core scan");
|
164 |
-
}
|
165 |
-
|
166 |
-
//These are both now available to free customers
|
167 |
-
if(wfConfig::get('scansEnabled_plugins')){
|
168 |
-
$this->pluginScanEnabled = true;
|
169 |
-
$this->statusIDX['plugin'] = wordfence::statusStart("Comparing open source plugins against WordPress.org originals");
|
170 |
-
} else {
|
171 |
-
wordfence::statusDisabled("Skipping comparing plugin files against originals in repository");
|
172 |
-
}
|
173 |
-
|
174 |
-
if(wfConfig::get('scansEnabled_themes')){
|
175 |
-
$this->themeScanEnabled = true;
|
176 |
-
$this->statusIDX['theme'] = wordfence::statusStart("Comparing open source themes against WordPress.org originals");
|
177 |
-
} else {
|
178 |
-
wordfence::statusDisabled("Skipping comparing theme files against originals in repository");
|
179 |
-
}
|
180 |
-
//End new section available to free customers
|
181 |
-
|
182 |
-
if(wfConfig::get('scansEnabled_malware')){
|
183 |
-
$this->statusIDX['unknown'] = wordfence::statusStart("Scanning for known malware files");
|
184 |
-
$this->malwareScanEnabled = true;
|
185 |
-
} else {
|
186 |
-
wordfence::statusDisabled("Skipping malware scan");
|
187 |
-
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
|
188 |
-
}
|
189 |
-
if((! $this->i->summaryUpdateRequired()) && (! ($this->coreScanEnabled || $this->pluginScanEnabled || $this->themeScanEnabled || $this->malwareScanEnabled))){
|
190 |
-
$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.");
|
191 |
-
//Remove main and finish routines because they rely on $this->hasher being created
|
192 |
-
array_shift($this->jobList);
|
193 |
-
array_shift($this->jobList);
|
194 |
-
return array();
|
195 |
-
}
|
196 |
-
//CORE SCAN
|
197 |
$this->hasher = new wordfenceHash(strlen(ABSPATH));
|
198 |
$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');
|
199 |
$baseContents = scandir(ABSPATH);
|
@@ -212,107 +169,61 @@ class wfScanEngine {
|
|
212 |
$includeInScan[] = $file;
|
213 |
}
|
214 |
}
|
215 |
-
$this->status(2, 'info', "Hashing your WordPress files for comparison against originals.");
|
216 |
-
$this->hasher->buildFileQueue(ABSPATH, $includeInScan);
|
217 |
-
}
|
218 |
-
private function scan_knownFiles_main(){
|
219 |
-
$this->hasher->genHashes($this);
|
220 |
-
}
|
221 |
-
private function scan_knownFiles_finish(){
|
222 |
-
$this->status(2, 'info', "Done hash. Updating summary items.");
|
223 |
-
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($this->hasher->totalData));
|
224 |
-
$this->i->updateSummaryItem('totalFiles', $this->hasher->totalFiles);
|
225 |
-
$this->i->updateSummaryItem('totalDirs', $this->hasher->totalDirs);
|
226 |
-
$this->i->updateSummaryItem('linesOfPHP', $this->hasher->linesOfPHP);
|
227 |
-
$this->i->updateSummaryItem('linesOfJCH', $this->hasher->linesOfJCH);
|
228 |
|
229 |
if(! function_exists( 'get_plugins')){
|
230 |
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
231 |
}
|
232 |
$this->status(2, 'info', "Getting plugin list from WordPress");
|
233 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
$this->status(2, 'info', "Found " . sizeof($plugins) . " plugins");
|
235 |
$this->i->updateSummaryItem('totalPlugins', sizeof($plugins));
|
|
|
236 |
if(! function_exists( 'get_themes')){
|
237 |
require_once ABSPATH . '/wp-includes/theme.php';
|
238 |
}
|
239 |
$this->status(2, 'info', "Getting theme list from WordPress");
|
240 |
-
$
|
241 |
-
$
|
242 |
-
$
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
if($cssData){
|
253 |
-
if(preg_match('/Theme URI:\s*([^\r\n]+)/', $cssData, $matches)){ $themes[$themeName]['Theme URI'] = $matches[1]; }
|
254 |
-
if(preg_match('/License:\s*([^\r\n]+)/', $cssData, $matches)){ $themes[$themeName]['License'] = $matches[1]; }
|
255 |
-
if(preg_match('/License URI:\s*([^\r\n]+)/', $cssData, $matches)){ $themes[$themeName]['License URI'] = $matches[1]; }
|
256 |
-
}
|
257 |
-
}
|
258 |
-
$this->status(2, 'info', "Sending request to Wordfence servers to do main scan.");
|
259 |
-
|
260 |
-
$scanData = array(
|
261 |
-
'pluginScanEnabled' => $this->pluginScanEnabled,
|
262 |
-
'themeScanEnabled' => $this->themeScanEnabled,
|
263 |
-
'coreScanEnabled' => $this->coreScanEnabled,
|
264 |
-
'malwareScanEnabled' => $this->malwareScanEnabled,
|
265 |
-
'plugins' => $plugins,
|
266 |
-
'themes' => $themes,
|
267 |
-
'hashStorageID' => $this->hasher->getHashStorageID()
|
268 |
-
);
|
269 |
-
$content = json_encode($scanData);
|
270 |
-
try {
|
271 |
-
$dataArr = $this->api->binCall('main_scan', $content);
|
272 |
-
} catch(Exception $e){
|
273 |
-
wordfence::statusEndErr();
|
274 |
-
throw $e;
|
275 |
-
}
|
276 |
-
if(! is_array($dataArr)){
|
277 |
-
wordfence::statusEndErr();
|
278 |
-
throw new Exception("We received an empty response from the Wordfence server when scanning core, plugin and theme files.");
|
279 |
-
}
|
280 |
-
//Data is an encoded string of <4 bytes of total length including these 4 bytes><2 bytes of filename length><filename>
|
281 |
-
$totalUStrLen = unpack('N', substr($dataArr['data'], 0, 4));
|
282 |
-
$totalUStrLen = $totalUStrLen[1];
|
283 |
-
$this->unknownFiles = substr($dataArr['data'], 4, ($totalUStrLen - 4)); //subtruct the first 4 bytes which is an INT that is the total length of unknown string including the 4 bytes
|
284 |
-
wfConfig::set('lastUnknownFileList', $this->unknownFiles);
|
285 |
-
$resultArr = json_decode(substr($dataArr['data'], $totalUStrLen), true);
|
286 |
-
if(! (is_array($resultArr) && isset($resultArr['results'])) ){
|
287 |
-
wordfence::statusEndErr();
|
288 |
-
throw new Exception("We received an incorrect response from the Wordfence server when scanning core, plugin and theme files.");
|
289 |
-
}
|
290 |
-
|
291 |
-
$this->status(2, 'info', "Processing scan results");
|
292 |
-
$haveIssues = array(
|
293 |
-
'core' => false,
|
294 |
-
'plugin' => false,
|
295 |
-
'theme' => false,
|
296 |
-
'unknown' => false
|
297 |
-
);
|
298 |
-
foreach($resultArr['results'] as $issue){
|
299 |
-
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
300 |
-
if($this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data'])){
|
301 |
-
$haveIssues[$issue['data']['cType']] = true;
|
302 |
-
}
|
303 |
-
}
|
304 |
-
foreach($haveIssues as $type => $have){
|
305 |
-
if($this->statusIDX[$type] !== false){
|
306 |
-
wordfence::statusEnd($this->statusIDX[$type], $have);
|
307 |
}
|
308 |
}
|
|
|
|
|
309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
}
|
311 |
private function scan_fileContents_init(){
|
312 |
$this->statusIDX['infect'] = wordfence::statusStart('Scanning file contents for infections and vulnerabilities');
|
313 |
$this->statusIDX['GSB'] = wordfence::statusStart('Scanning files for URLs in Google\'s Safe Browsing List');
|
314 |
-
$this->scanner = new wordfenceScanner($this->apiKey, $this->wp_version,
|
315 |
-
$this->unknownFiles = false;
|
316 |
$this->status(2, 'info', "Starting scan of file contents");
|
317 |
}
|
318 |
private function scan_fileContents_main(){
|
@@ -918,7 +829,7 @@ class wfScanEngine {
|
|
918 |
public function status($level, $type, $msg){
|
919 |
wordfence::status($level, $type, $msg);
|
920 |
}
|
921 |
-
|
922 |
return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
|
923 |
}
|
924 |
public static function requestKill(){
|
8 |
require_once('wfUtils.php');
|
9 |
class wfScanEngine {
|
10 |
private static $cronTestFailedURLs = array();
|
11 |
+
public $api = false;
|
12 |
private $dictWords = array();
|
13 |
private $forkRequested = false;
|
14 |
|
22 |
private $startTime = 0;
|
23 |
private $scanStep = 0;
|
24 |
private $maxExecTime = 10; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
|
|
|
|
|
|
|
25 |
private $publicScanEnabled = false;
|
|
|
|
|
26 |
private $fileContentsResults = false;
|
27 |
private $scanner = false;
|
28 |
private $scanQueue = array();
|
37 |
private $userPasswdQueue = "";
|
38 |
private $passwdHasIssues = false;
|
39 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
40 |
+
return array('hasher', 'hashes', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'scanStep', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues');
|
41 |
}
|
42 |
public function __construct(){
|
43 |
$this->startTime = time();
|
50 |
include('wfDict.php'); //$dictWords
|
51 |
$this->dictWords = $dictWords;
|
52 |
$this->jobList[] = 'publicSite';
|
53 |
+
$this->jobList[] = 'knownFiles';
|
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')){
|
148 |
sleep(2); //enough time to read the message before it scrolls off.
|
149 |
}
|
150 |
}
|
151 |
+
private function scan_knownFiles(){
|
152 |
$this->status(1, 'info', "Contacting Wordfence to initiate scan");
|
153 |
$this->api->call('log_scan', array(), array());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
$this->hasher = new wordfenceHash(strlen(ABSPATH));
|
155 |
$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');
|
156 |
$baseContents = scandir(ABSPATH);
|
169 |
$includeInScan[] = $file;
|
170 |
}
|
171 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
if(! function_exists( 'get_plugins')){
|
174 |
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
175 |
}
|
176 |
$this->status(2, 'info', "Getting plugin list from WordPress");
|
177 |
+
$pluginData = get_plugins();
|
178 |
+
$plugins = array();
|
179 |
+
foreach($pluginData as $key => $data){
|
180 |
+
if(preg_match('/^([^\/]+)\//', $key, $matches)){
|
181 |
+
$pluginDir = $matches[1];
|
182 |
+
$pluginFullDir = "wp-content/plugins/" . $pluginDir;
|
183 |
+
$plugins[$key] = array(
|
184 |
+
'Name' => $data['Name'],
|
185 |
+
'Version' => $data['Version'],
|
186 |
+
'ShortDir' => $pluginDir,
|
187 |
+
'FullDir' => $pluginFullDir
|
188 |
+
);
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
$this->status(2, 'info', "Found " . sizeof($plugins) . " plugins");
|
193 |
$this->i->updateSummaryItem('totalPlugins', sizeof($plugins));
|
194 |
+
|
195 |
if(! function_exists( 'get_themes')){
|
196 |
require_once ABSPATH . '/wp-includes/theme.php';
|
197 |
}
|
198 |
$this->status(2, 'info', "Getting theme list from WordPress");
|
199 |
+
$themeData = get_themes();
|
200 |
+
$themes = array();
|
201 |
+
foreach($themeData as $themeName => $themeData){
|
202 |
+
if(preg_match('/\/([^\/]+)$/', $themeData['Stylesheet Dir'], $matches)){
|
203 |
+
$shortDir = $matches[1]; //e.g. evo4cms
|
204 |
+
$fullDir = substr($themeData['Stylesheet Dir'], strlen(ABSPATH)); //e.g. wp-content/themes/evo4cms
|
205 |
+
$themes[$themeName] = array(
|
206 |
+
'Name' => $themeData['Name'],
|
207 |
+
'Version' => $themeData['Version'],
|
208 |
+
'ShortDir' => $shortDir,
|
209 |
+
'FullDir' => $fullDir
|
210 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
}
|
212 |
}
|
213 |
+
$this->status(2, 'info', "Found " . sizeof($themes) . " themes");
|
214 |
+
$this->i->updateSummaryItem('totalThemes', sizeof($themes));
|
215 |
|
216 |
+
$this->hasher->run(ABSPATH, $includeInScan, $themes, $plugins, $this); //Include this so we can call addIssue and ->api->
|
217 |
+
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($this->hasher->totalData));
|
218 |
+
$this->i->updateSummaryItem('totalFiles', $this->hasher->totalFiles);
|
219 |
+
$this->i->updateSummaryItem('totalDirs', $this->hasher->totalDirs);
|
220 |
+
$this->i->updateSummaryItem('linesOfPHP', $this->hasher->linesOfPHP);
|
221 |
+
$this->i->updateSummaryItem('linesOfJCH', $this->hasher->linesOfJCH);
|
222 |
}
|
223 |
private function scan_fileContents_init(){
|
224 |
$this->statusIDX['infect'] = wordfence::statusStart('Scanning file contents for infections and vulnerabilities');
|
225 |
$this->statusIDX['GSB'] = wordfence::statusStart('Scanning files for URLs in Google\'s Safe Browsing List');
|
226 |
+
$this->scanner = new wordfenceScanner($this->apiKey, $this->wp_version, ABSPATH);
|
|
|
227 |
$this->status(2, 'info', "Starting scan of file contents");
|
228 |
}
|
229 |
private function scan_fileContents_main(){
|
829 |
public function status($level, $type, $msg){
|
830 |
wordfence::status($level, $type, $msg);
|
831 |
}
|
832 |
+
public function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
|
833 |
return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
|
834 |
}
|
835 |
public static function requestKill(){
|
lib/wfSchema.php
CHANGED
@@ -143,9 +143,12 @@ class wfSchema {
|
|
143 |
hostKey binary(4),
|
144 |
KEY k2(hostKey)
|
145 |
) default charset=utf8",
|
146 |
-
'
|
147 |
-
|
148 |
-
filename
|
|
|
|
|
|
|
149 |
) default charset=utf8"
|
150 |
);
|
151 |
private $db = false;
|
143 |
hostKey binary(4),
|
144 |
KEY k2(hostKey)
|
145 |
) default charset=utf8",
|
146 |
+
'wfFileMods' => "(
|
147 |
+
filenameMD5 binary(16) NOT NULL PRIMARY KEY,
|
148 |
+
filename varchar(1000) NOT NULL,
|
149 |
+
knownFile tinyint UNSIGNED NOT NULL,
|
150 |
+
oldMD5 binary(16) NOT NULL,
|
151 |
+
newMD5 binary(16) NOT NULL
|
152 |
) default charset=utf8"
|
153 |
);
|
154 |
private $db = false;
|
lib/wordfenceClass.php
CHANGED
@@ -229,6 +229,8 @@ class wordfence {
|
|
229 |
$db->queryIgnoreError("alter table $prefix"."wfBlocks modify column blockedTime bigint signed NOT NULL");
|
230 |
//3.2.1 to 3.2.2
|
231 |
$db->queryIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
|
|
|
|
|
232 |
|
233 |
//Must be the final line
|
234 |
}
|
@@ -1289,7 +1291,7 @@ class wordfence {
|
|
1289 |
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
|
1290 |
}
|
1291 |
|
1292 |
-
if(preg_match('/^Wordfence/', @$_GET['page'])){
|
1293 |
wp_enqueue_style('wp-pointer');
|
1294 |
wp_enqueue_script('wp-pointer');
|
1295 |
wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION);
|
@@ -1481,12 +1483,20 @@ class wordfence {
|
|
1481 |
self::status(10, 'info', 'SUM_START:' . $msg);
|
1482 |
return sizeof($statusStartMsgs) - 1;
|
1483 |
}
|
1484 |
-
public static function statusEnd($idx, $haveIssues){
|
1485 |
$statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
|
1486 |
if($haveIssues){
|
1487 |
-
|
|
|
|
|
|
|
|
|
1488 |
} else {
|
1489 |
-
|
|
|
|
|
|
|
|
|
1490 |
}
|
1491 |
$statusStartMsgs[$idx] = '';
|
1492 |
wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
|
229 |
$db->queryIgnoreError("alter table $prefix"."wfBlocks modify column blockedTime bigint signed NOT NULL");
|
230 |
//3.2.1 to 3.2.2
|
231 |
$db->queryIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
|
232 |
+
$db->queryIgnoreError("drop table if exists $prefix"."wfFileQueue");
|
233 |
+
$db->queryIgnoreError("drop table if exists $prefix"."wfFileChanges");
|
234 |
|
235 |
//Must be the final line
|
236 |
}
|
1291 |
add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
|
1292 |
}
|
1293 |
|
1294 |
+
if(isset($_GET['page']) && preg_match('/^Wordfence/', @$_GET['page']) ){
|
1295 |
wp_enqueue_style('wp-pointer');
|
1296 |
wp_enqueue_script('wp-pointer');
|
1297 |
wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION);
|
1483 |
self::status(10, 'info', 'SUM_START:' . $msg);
|
1484 |
return sizeof($statusStartMsgs) - 1;
|
1485 |
}
|
1486 |
+
public static function statusEnd($idx, $haveIssues, $successFailed = false){
|
1487 |
$statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
|
1488 |
if($haveIssues){
|
1489 |
+
if($successFailed){
|
1490 |
+
self::status(10, 'info', 'SUM_ENDFAILED:' . $statusStartMsgs[$idx]);
|
1491 |
+
} else {
|
1492 |
+
self::status(10, 'info', 'SUM_ENDBAD:' . $statusStartMsgs[$idx]);
|
1493 |
+
}
|
1494 |
} else {
|
1495 |
+
if($successFailed){
|
1496 |
+
self::status(10, 'info', 'SUM_ENDSUCCESS:' . $statusStartMsgs[$idx]);
|
1497 |
+
} else {
|
1498 |
+
self::status(10, 'info', 'SUM_ENDOK:' . $statusStartMsgs[$idx]);
|
1499 |
+
}
|
1500 |
}
|
1501 |
$statusStartMsgs[$idx] = '';
|
1502 |
wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
|
lib/wordfenceConstants.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<?php
|
2 |
-
define('WORDFENCE_API_VERSION', '2.
|
3 |
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
|
5 |
define('WORDFENCE_MAX_SCAN_TIME', 600);
|
1 |
<?php
|
2 |
+
define('WORDFENCE_API_VERSION', '2.4');
|
3 |
define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
|
5 |
define('WORDFENCE_MAX_SCAN_TIME', 600);
|
lib/wordfenceHash.php
CHANGED
@@ -1,13 +1,6 @@
|
|
1 |
<?php
|
2 |
require_once('wordfenceClass.php');
|
3 |
class wordfenceHash {
|
4 |
-
private $apiKey = false;
|
5 |
-
private $wp_version = false;
|
6 |
-
private $api = false;
|
7 |
-
private $db = false;
|
8 |
-
private $table = false;
|
9 |
-
private $fileQ = array();
|
10 |
-
|
11 |
//Begin serialized vars
|
12 |
private $whitespace = array("\n","\r","\t"," ");
|
13 |
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}'
|
@@ -16,33 +9,72 @@ class wordfenceHash {
|
|
16 |
public $linesOfPHP = 0;
|
17 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
18 |
public $striplen = 0;
|
19 |
-
private $
|
20 |
-
|
21 |
-
private $
|
22 |
-
private $
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
public function __construct($striplen){
|
30 |
$this->striplen = $striplen;
|
31 |
-
$this->db = new wfDB();
|
32 |
-
$this->table = $this->db->prefix() . 'wfFileQueue';
|
33 |
-
$this->apiKey = wfConfig::get('apiKey');
|
34 |
-
$this->wp_version = wfUtils::getWPVersion();
|
35 |
-
$this->api = new wfAPI($this->apiKey, $this->wp_version);
|
36 |
}
|
37 |
-
public function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
$this->db = new wfDB();
|
39 |
-
|
40 |
-
|
41 |
-
$this->
|
42 |
-
$this->
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
if($path[strlen($path) - 1] != '/'){
|
47 |
$path .= '/';
|
48 |
}
|
@@ -51,6 +83,16 @@ class wordfenceHash {
|
|
51 |
exit();
|
52 |
}
|
53 |
$files = scandir($path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
foreach($files as $file){
|
55 |
if(sizeof($only) > 0 && (! in_array($file, $only))){
|
56 |
continue;
|
@@ -58,44 +100,43 @@ class wordfenceHash {
|
|
58 |
$file = $path . $file;
|
59 |
wordfence::status(4, 'info', "Hashing item in base dir: $file");
|
60 |
$this->_dirHash($file);
|
61 |
-
}
|
62 |
-
$this->
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
if(
|
67 |
-
$
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
}
|
72 |
-
$haveMoreInDB = true;
|
73 |
-
while($haveMoreInDB){
|
74 |
-
$haveMoreInDB = false;
|
75 |
-
//This limit used to be 1000, but we changed it to 5 because forkIfNeeded needs to run frequently, but
|
76 |
-
// we still want to minimize the number of queries we do.
|
77 |
-
// So now we select, process and delete 5 from teh queue and then check forkIfNeeded()
|
78 |
-
// So this assumes that processing 5 files won't take longer than wfScanEngine::$maxExecTime (which was 10 at the time of writing, which is 2 secs per file)
|
79 |
-
$res = $this->db->query("select id, filename from " . $this->table . " limit 5");
|
80 |
-
$ids = array();
|
81 |
-
while($rec = mysql_fetch_row($res)){
|
82 |
-
$this->processFile($rec[1]);
|
83 |
-
array_push($ids, $rec[0]);
|
84 |
-
$haveMoreInDB = true;
|
85 |
}
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
}
|
89 |
-
$forkObj->forkIfNeeded();
|
90 |
}
|
91 |
-
|
92 |
-
$this->sendHashPacket();
|
93 |
-
$this->db->truncate($this->table); //Also resets id autoincrement to 1
|
94 |
-
$this->writeHashingStatus();
|
95 |
-
}
|
96 |
-
private function writeHashingStatus(){
|
97 |
-
$this->lastStatusTime = microtime(true);
|
98 |
-
wordfence::status(2, 'info', "Scanned " . $this->totalFiles . " files at a rate of " . sprintf('%.2f', ($this->totalFiles / (microtime(true) - $this->hashingStartTime))) . " files per second.");
|
99 |
}
|
100 |
private function _dirHash($path){
|
101 |
if(substr($path, -3, 3) == '/..' || substr($path, -2, 2) == '/.'){
|
@@ -112,96 +153,140 @@ class wordfenceHash {
|
|
112 |
if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
|
113 |
$file = $path . $cont[$i];
|
114 |
if(is_file($file)){
|
115 |
-
$this->
|
116 |
} else if(is_dir($file)) {
|
117 |
$this->_dirHash($file);
|
118 |
}
|
119 |
}
|
120 |
} else {
|
121 |
if(is_file($path)){
|
122 |
-
$this->
|
123 |
}
|
124 |
}
|
125 |
}
|
126 |
-
private function
|
127 |
-
$
|
128 |
-
if(
|
129 |
-
$
|
130 |
-
}
|
131 |
-
}
|
132 |
-
private function writeFileQueue(){
|
133 |
-
$sql = "insert into " . $this->table . " (filename) values ";
|
134 |
-
$added = false;
|
135 |
-
foreach($this->fileQ as $val){
|
136 |
-
$added = true;
|
137 |
-
$sql .= "('" . mysql_real_escape_string($val) . "'),";
|
138 |
-
}
|
139 |
-
if($added){
|
140 |
-
$sql = rtrim($sql, ',');
|
141 |
-
$this->db->query($sql);
|
142 |
-
}
|
143 |
-
$this->fileQ = array();
|
144 |
-
}
|
145 |
-
private function processFile($file){
|
146 |
-
if(wfUtils::fileTooBig($file)){
|
147 |
-
wordfence::status(4, 'info', "Skipping file larger than max size: $file");
|
148 |
return;
|
149 |
}
|
150 |
if(function_exists('memory_get_usage')){
|
151 |
-
wordfence::status(4, 'info', "Scanning: $
|
152 |
} else {
|
153 |
-
wordfence::status(4, 'info', "Scanning: $
|
154 |
}
|
155 |
-
$wfHash = $this->wfHash($
|
156 |
if($wfHash){
|
157 |
-
$
|
158 |
-
$
|
159 |
-
|
160 |
-
|
161 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
}
|
|
|
|
|
|
|
|
|
163 |
|
164 |
//Now that we know we can open the file, lets update stats
|
165 |
-
if(preg_match('/\.(?:js|html|htm|css)$/i', $
|
166 |
-
$this->linesOfJCH += sizeof(file($
|
167 |
-
} else if(preg_match('/\.php$/i', $
|
168 |
-
$this->linesOfPHP += sizeof(file($
|
169 |
}
|
170 |
$this->totalFiles++;
|
171 |
-
$this->totalData += filesize($
|
172 |
-
if(
|
173 |
-
$this->
|
174 |
}
|
175 |
} else {
|
176 |
-
wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $
|
177 |
}
|
178 |
}
|
179 |
-
private function sendHashPacket(){
|
180 |
-
wordfence::status(4, 'info', "Sending packet of hash data to Wordfence scanning servers");
|
181 |
-
if(strlen($this->hashPacket) < 1){
|
182 |
-
return;
|
183 |
-
}
|
184 |
-
if($this->hashStorageID){
|
185 |
-
$dataArr = $this->api->binCall('add_hash_chunk', "WFID:" . pack('N', $this->hashStorageID) . $this->hashPacket);
|
186 |
-
$this->hashPacket = "";
|
187 |
-
if(is_array($dataArr) && isset($dataArr['data']) && $dataArr['data'] == $this->hashStorageID){
|
188 |
-
//keep going
|
189 |
-
} else {
|
190 |
-
throw new Exception("Could not store an additional chunk of hash data on Wordfence servers with ID: " . $this->hashStorageID);
|
191 |
-
}
|
192 |
-
} else {
|
193 |
-
$dataArr = $this->api->binCall('add_hash_chunk', "WFST:" . $this->hashPacket);
|
194 |
-
$this->hashPacket = "";
|
195 |
-
if(is_array($dataArr) && isset($dataArr['data']) && preg_match('/^\d+$/', $dataArr['data'])){
|
196 |
-
$this->hashStorageID = $dataArr['data'];
|
197 |
-
} else {
|
198 |
-
throw new Exception("Could not store hash data on Wordfence servers. Got response: " . var_export($dataArr, true));
|
199 |
-
}
|
200 |
-
}
|
201 |
-
}
|
202 |
-
public function getHashStorageID(){
|
203 |
-
return $this->hashStorageID;
|
204 |
-
}
|
205 |
public function wfHash($file){
|
206 |
wfUtils::errorsOff();
|
207 |
$md5 = @md5_file($file, false);
|
@@ -219,5 +304,12 @@ class wordfenceHash {
|
|
219 |
$shac = hash_final($ctx, false);
|
220 |
return array($md5, $shac);
|
221 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
}
|
223 |
?>
|
1 |
<?php
|
2 |
require_once('wordfenceClass.php');
|
3 |
class wordfenceHash {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
//Begin serialized vars
|
5 |
private $whitespace = array("\n","\r","\t"," ");
|
6 |
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 $linesOfPHP = 0;
|
10 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
11 |
public $striplen = 0;
|
12 |
+
private $engine = false;
|
13 |
+
private $db = false;
|
14 |
+
private $coreEnabled = false;
|
15 |
+
private $themesEnabled = false;
|
16 |
+
private $pluginsEnabled = false;
|
17 |
+
private $malwareEnabled = false;
|
18 |
+
private $malwareData = "";
|
19 |
+
private $possibleMalware = array();
|
20 |
+
private $status = array();
|
21 |
+
private $haveIssues = array();
|
22 |
public function __construct($striplen){
|
23 |
$this->striplen = $striplen;
|
|
|
|
|
|
|
|
|
|
|
24 |
}
|
25 |
+
public function run($path, $only, $themes, $plugins, $engine){ //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.
|
26 |
+
$this->engine = $engine;
|
27 |
+
if(wfConfig::get('scansEnabled_core')){
|
28 |
+
$this->coreEnabled = true;
|
29 |
+
}
|
30 |
+
if(wfConfig::get('scansEnabled_plugins')){
|
31 |
+
$this->pluginsEnabled = true;
|
32 |
+
}
|
33 |
+
if(wfConfig::get('scansEnabled_themes')){
|
34 |
+
$this->themesEnabled = true;
|
35 |
+
}
|
36 |
+
if(wfConfig::get('scansEnabled_malware')){
|
37 |
+
$this->malwareEnabled = true;
|
38 |
+
}
|
39 |
$this->db = new wfDB();
|
40 |
+
|
41 |
+
//Doing a delete for now. Later we can optimize this to only scan modified files.
|
42 |
+
//$this->db->query("update " . $this->db->prefix() . "wfFileMods set oldMD5 = newMD5");
|
43 |
+
$this->db->query("delete from " . $this->db->prefix() . "wfFileMods");
|
44 |
+
$fetchCoreHashesStatus = wordfence::statusStart("Fetching core, theme and plugin file signatures from Wordfence");
|
45 |
+
$dataArr = $engine->api->binCall('get_known_files', json_encode(array(
|
46 |
+
'plugins' => $plugins,
|
47 |
+
'themes' => $themes
|
48 |
+
)) );
|
49 |
+
if($dataArr['code'] != 200){
|
50 |
+
wordfence::statusEndErr();
|
51 |
+
throw new Exception("Got error response from Wordfence servers: " . $dataArr['code']);
|
52 |
+
}
|
53 |
+
$this->knownFiles = @json_decode($dataArr['data'], true);
|
54 |
+
if(! is_array($this->knownFiles)){
|
55 |
+
wordfence::statusEndErr();
|
56 |
+
throw new Exception("Invaid response from Wordfence servers.");
|
57 |
+
}
|
58 |
+
wordfence::statusEnd($fetchCoreHashesStatus, false, true);
|
59 |
+
|
60 |
+
if($this->malwareEnabled){
|
61 |
+
$malwarePrefixStatus = wordfence::statusStart("Fetching list of known malware files from Wordfence");
|
62 |
+
$malwareData = $engine->api->getStaticURL('/malwarePrefixes.bin');
|
63 |
+
if(! $malwareData){
|
64 |
+
wordfence::statusEndErr();
|
65 |
+
throw new Exception("Could not fetch malware signatures from Wordfence servers.");
|
66 |
+
}
|
67 |
+
if(strlen($malwareData) % 4 != 0){
|
68 |
+
wordfence::statusEndErr();
|
69 |
+
throw new Exception("Malware data received from Wordfence servers was not valid.");
|
70 |
+
}
|
71 |
+
$this->malwareData = array();
|
72 |
+
for($i = 0; $i < strlen($malwareData); $i += 4){
|
73 |
+
$this->malwareData[substr($malwareData, $i, 4)] = '1';
|
74 |
+
}
|
75 |
+
wordfence::statusEnd($malwarePrefixStatus, false, true);
|
76 |
+
}
|
77 |
+
|
78 |
if($path[strlen($path) - 1] != '/'){
|
79 |
$path .= '/';
|
80 |
}
|
83 |
exit();
|
84 |
}
|
85 |
$files = scandir($path);
|
86 |
+
$this->haveIssues = array(
|
87 |
+
'core' => false,
|
88 |
+
'themes' => false,
|
89 |
+
'plugins' => false,
|
90 |
+
'malware' => false
|
91 |
+
);
|
92 |
+
if($this->coreEnabled){ $this->status['core'] = wordfence::statusStart("Comparing core WordPress files against originals in repository"); } else { wordfence::statusDisabled("Skipping core scan"); }
|
93 |
+
if($this->themesEnabled){ $this->status['themes'] = wordfence::statusStart("Comparing open source themes against WordPress.org originals"); } else { wordfence::statusDisabled("Skipping theme scan"); }
|
94 |
+
if($this->pluginsEnabled){ $this->status['plugins'] = wordfence::statusStart("Comparing plugins against WordPress.org originals"); } else { wordfence::statusDisabled("Skipping plugin scan"); }
|
95 |
+
if($this->malwareEnabled){ $this->status['malware'] = wordfence::statusStart("Scanning for known malware files"); } else { wordfence::statusDisabled("Skipping malware scan"); }
|
96 |
foreach($files as $file){
|
97 |
if(sizeof($only) > 0 && (! in_array($file, $only))){
|
98 |
continue;
|
100 |
$file = $path . $file;
|
101 |
wordfence::status(4, 'info', "Hashing item in base dir: $file");
|
102 |
$this->_dirHash($file);
|
103 |
+
}
|
104 |
+
wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data.");
|
105 |
+
if($this->coreEnabled){ wordfence::statusEnd($this->status['core'], $this->haveIssues['core']); }
|
106 |
+
if($this->themesEnabled){ wordfence::statusEnd($this->status['themes'], $this->haveIssues['themes']); }
|
107 |
+
if($this->pluginsEnabled){ wordfence::statusEnd($this->status['plugins'], $this->haveIssues['plugins']); }
|
108 |
+
if(sizeof($this->possibleMalware) > 0){
|
109 |
+
$malwareResp = $engine->api->binCall('check_possible_malware', json_encode($this->possibleMalware));
|
110 |
+
if($malwareResp['code'] != 200){
|
111 |
+
wordfence::statusEndErr();
|
112 |
+
throw new Exception("Invalid response from Wordfence API during check_possible_malware");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
}
|
114 |
+
$malwareList = json_decode($malwareResp['data'], true);
|
115 |
+
if(is_array($malwareList) && sizeof($malwareList) > 0){
|
116 |
+
for($i = 0; $i < sizeof($malwareList); $i++){
|
117 |
+
$file = $malwareList[$i][0];
|
118 |
+
$md5 = $malwareList[$i][1];
|
119 |
+
$name = $malwareList[$i][2];
|
120 |
+
$this->haveIssues['malware'] = true;
|
121 |
+
$this->engine->addIssue(
|
122 |
+
'file',
|
123 |
+
1,
|
124 |
+
$path . $file,
|
125 |
+
$md5,
|
126 |
+
'This file is suspected malware: ' . $file,
|
127 |
+
"This file's signature matches a known malware file. The title of the malware is '" . $name . "'. Immediately inspect this file using the 'View' option below and consider deleting it from your server.",
|
128 |
+
array(
|
129 |
+
'file' => $file,
|
130 |
+
'cType' => 'unknown',
|
131 |
+
'canDiff' => false,
|
132 |
+
'canFix' => false,
|
133 |
+
'canDelete' => true
|
134 |
+
)
|
135 |
+
);
|
136 |
+
}
|
137 |
}
|
|
|
138 |
}
|
139 |
+
if($this->malwareEnabled){ wordfence::statusEnd($this->status['malware'], $this->haveIssues['malware']); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
}
|
141 |
private function _dirHash($path){
|
142 |
if(substr($path, -3, 3) == '/..' || substr($path, -2, 2) == '/.'){
|
153 |
if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
|
154 |
$file = $path . $cont[$i];
|
155 |
if(is_file($file)){
|
156 |
+
$this->processFile($file);
|
157 |
} else if(is_dir($file)) {
|
158 |
$this->_dirHash($file);
|
159 |
}
|
160 |
}
|
161 |
} else {
|
162 |
if(is_file($path)){
|
163 |
+
$this->processFile($path);
|
164 |
}
|
165 |
}
|
166 |
}
|
167 |
+
private function processFile($realFile){
|
168 |
+
$file = substr($realFile, $this->striplen);
|
169 |
+
if(wfUtils::fileTooBig($realFile)){
|
170 |
+
wordfence::status(4, 'info', "Skipping file larger than max size: $realFile");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
return;
|
172 |
}
|
173 |
if(function_exists('memory_get_usage')){
|
174 |
+
wordfence::status(4, 'info', "Scanning: $realFile (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
|
175 |
} else {
|
176 |
+
wordfence::status(4, 'info', "Scanning: $realFile");
|
177 |
}
|
178 |
+
$wfHash = $this->wfHash($realFile);
|
179 |
if($wfHash){
|
180 |
+
$md5 = strtoupper($wfHash[0]);
|
181 |
+
$shac = strtoupper($wfHash[1]);
|
182 |
+
$knownFile = 0;
|
183 |
+
if($this->malwareEnabled && $this->isMalwarePrefix($md5)){
|
184 |
+
$this->possibleMalware[] = array($file, $md5);
|
185 |
+
}
|
186 |
+
if(isset($this->knownFiles['core'][$file])){
|
187 |
+
if(strtoupper($this->knownFiles['core'][$file]) == $shac){
|
188 |
+
$knownFile = 1;
|
189 |
+
} else {
|
190 |
+
if($this->coreEnabled){
|
191 |
+
$this->haveIssues['core'] = true;
|
192 |
+
$this->engine->addIssue(
|
193 |
+
'file',
|
194 |
+
1,
|
195 |
+
'coreModified' . $file . $md5,
|
196 |
+
'coreModified' . $file,
|
197 |
+
'WordPress core file modified: ' . $file,
|
198 |
+
"This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
|
199 |
+
array(
|
200 |
+
'file' => $file,
|
201 |
+
'cType' => 'core',
|
202 |
+
'canDiff' => true,
|
203 |
+
'canFix' => true,
|
204 |
+
'canDelete' => false
|
205 |
+
)
|
206 |
+
);
|
207 |
+
}
|
208 |
+
}
|
209 |
+
} else if(isset($this->knownFiles['plugins'][$file])){
|
210 |
+
if(in_array($shac, $this->knownFiles['plugins'][$file])){
|
211 |
+
$knownFile = 1;
|
212 |
+
} else {
|
213 |
+
if($this->pluginsEnabled){
|
214 |
+
$itemName = $this->knownFiles['plugins'][$file][0];
|
215 |
+
$itemVersion = $this->knownFiles['plugins'][$file][1];
|
216 |
+
$cKey = $this->knownFiles['plugins'][$file][2];
|
217 |
+
$this->haveIssues['plugins'] = true;
|
218 |
+
$this->engine->addIssue(
|
219 |
+
'file',
|
220 |
+
2,
|
221 |
+
'modifiedplugin' . $file . $md5,
|
222 |
+
'modifiedplugin' . $file,
|
223 |
+
'Modified plugin file: ' . $file,
|
224 |
+
"This file belongs to plugin \"$itemName\" version \"$itemVersion\" and has been modified from the file that is distributed by WordPress.org for this version. Please use the link to see how the file has changed. If you have modified this file yourself, you can safely ignore this warning. If you see a lot of changed files in a plugin that have been made by the author, then try uninstalling and reinstalling the plugin to force an upgrade. Doing this is a workaround for plugin authors who don't manage their code correctly. [See our FAQ on www.wordfence.com for more info]",
|
225 |
+
array(
|
226 |
+
'file' => $file,
|
227 |
+
'cType' => 'plugin',
|
228 |
+
'canDiff' => true,
|
229 |
+
'canFix' => true,
|
230 |
+
'canDelete' => false,
|
231 |
+
'cName' => $itemName,
|
232 |
+
'cVersion' => $itemVersion,
|
233 |
+
'cKey' => $cKey
|
234 |
+
)
|
235 |
+
);
|
236 |
+
}
|
237 |
+
|
238 |
+
}
|
239 |
+
} else if(isset($this->knownFiles['themes'][$file])){
|
240 |
+
if(in_array($shac, $this->knownFiles['themes'][$file])){
|
241 |
+
$knownFile = 1;
|
242 |
+
} else {
|
243 |
+
if($this->themesEnabled){
|
244 |
+
$itemName = $this->knownFiles['themes'][$file][0];
|
245 |
+
$itemVersion = $this->knownFiles['themes'][$file][1];
|
246 |
+
$cKey = $this->knownFiles['themes'][$file][2];
|
247 |
+
$this->haveIssues['themes'] = true;
|
248 |
+
$this->engine->addIssue(
|
249 |
+
'file',
|
250 |
+
2,
|
251 |
+
'modifiedtheme' . $file . $md5,
|
252 |
+
'modifiedtheme' . $file,
|
253 |
+
'Modified theme file: ' . $file,
|
254 |
+
"This file belongs to theme \"$itemName\" version \"$itemVersion\" and has been modified from the original distribution. It is common for site owners to modify their theme files, so if you have modified this file yourself you can safely ignore this warning.",
|
255 |
+
array(
|
256 |
+
'file' => $file,
|
257 |
+
'cType' => 'theme',
|
258 |
+
'canDiff' => true,
|
259 |
+
'canFix' => true,
|
260 |
+
'canDelete' => false,
|
261 |
+
'cName' => $itemName,
|
262 |
+
'cVersion' => $itemVersion,
|
263 |
+
'cKey' => $cKey
|
264 |
+
)
|
265 |
+
);
|
266 |
+
}
|
267 |
+
|
268 |
+
}
|
269 |
}
|
270 |
+
// knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
|
271 |
+
// we could split this into files who's path we recognize and file's who's path we recognize AND who have a valid sig.
|
272 |
+
// But because we want to scan files who's sig we don't recognize, regardless of known path or not, we only need one "knownFile" field.
|
273 |
+
$this->db->query("insert into " . $this->db->prefix() . "wfFileMods (filename, filenameMD5, knownFile, oldMD5, newMD5) values ('%s', unhex(md5('%s')), %d, '', unhex('%s')) ON DUPLICATE KEY UPDATE newMD5=unhex('%s'), knownFile=%d", $file, $file, $knownFile, $md5, $md5, $knownFile);
|
274 |
|
275 |
//Now that we know we can open the file, lets update stats
|
276 |
+
if(preg_match('/\.(?:js|html|htm|css)$/i', $realFile)){
|
277 |
+
$this->linesOfJCH += sizeof(file($realFile));
|
278 |
+
} else if(preg_match('/\.php$/i', $realFile)){
|
279 |
+
$this->linesOfPHP += sizeof(file($realFile));
|
280 |
}
|
281 |
$this->totalFiles++;
|
282 |
+
$this->totalData += filesize($realFile); //We already checked if file overflows int in the fileTooBig routine above
|
283 |
+
if($this->totalFiles % 100 === 0){
|
284 |
+
wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data so far");
|
285 |
}
|
286 |
} else {
|
287 |
+
//wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $realFile");
|
288 |
}
|
289 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
290 |
public function wfHash($file){
|
291 |
wfUtils::errorsOff();
|
292 |
$md5 = @md5_file($file, false);
|
304 |
$shac = hash_final($ctx, false);
|
305 |
return array($md5, $shac);
|
306 |
}
|
307 |
+
private function isMalwarePrefix($hexMD5){
|
308 |
+
$binPrefix = pack("H*", substr($hexMD5, 0, 8));
|
309 |
+
if(isset($this->malwareData[$binPrefix])){
|
310 |
+
return true;
|
311 |
+
}
|
312 |
+
return false;
|
313 |
+
}
|
314 |
}
|
315 |
?>
|
lib/wordfenceScanner.php
CHANGED
@@ -5,7 +5,6 @@ require_once('wordfenceURLHoover.php');
|
|
5 |
class wordfenceScanner {
|
6 |
//serialized:
|
7 |
protected $path = '';
|
8 |
-
protected $fileList = array();
|
9 |
protected $results = array();
|
10 |
public $errorMsg = false;
|
11 |
private $apiKey = false;
|
@@ -15,15 +14,14 @@ class wordfenceScanner {
|
|
15 |
private $lastStatusTime = false;
|
16 |
private $patterns = "";
|
17 |
public function __sleep(){
|
18 |
-
return array('path', '
|
19 |
}
|
20 |
public function __wakeup(){
|
21 |
}
|
22 |
-
public function __construct($apiKey, $wordpressVersion, $
|
23 |
$this->apiKey = $apiKey;
|
24 |
$this->wordpressVersion = $wordpressVersion;
|
25 |
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
|
26 |
-
$this->fileList = $fileList; //A long string of <2 byte network order short showing filename length><filename>
|
27 |
if($path[strlen($path) - 1] != '/'){
|
28 |
$path .= '/';
|
29 |
}
|
@@ -51,16 +49,12 @@ class wordfenceScanner {
|
|
51 |
if(! $this->lastStatusTime){
|
52 |
$this->lastStatusTime = microtime(true);
|
53 |
}
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
}
|
61 |
-
|
62 |
-
$file = substr($this->fileList, 2, $filenameLen);
|
63 |
-
$this->fileList = substr($this->fileList, 2 + $filenameLen);
|
64 |
|
65 |
if(! file_exists($this->path . $file)){
|
66 |
continue;
|
@@ -96,11 +90,6 @@ class wordfenceScanner {
|
|
96 |
}
|
97 |
|
98 |
$stime = microtime(true);
|
99 |
-
$fileSum = @md5_file($this->path . $file);
|
100 |
-
if(! $fileSum){
|
101 |
-
//usually permission denied
|
102 |
-
continue;
|
103 |
-
}
|
104 |
$fh = @fopen($this->path . $file, 'r');
|
105 |
if(! $fh){
|
106 |
continue;
|
@@ -250,7 +239,7 @@ class wordfenceScanner {
|
|
250 |
return $this->results;
|
251 |
}
|
252 |
private function writeScanningStatus(){
|
253 |
-
wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " files at
|
254 |
}
|
255 |
private function addEncIssue($ignoreP, $ignoreC, $encoding, $file){
|
256 |
$this->addResult(array(
|
5 |
class wordfenceScanner {
|
6 |
//serialized:
|
7 |
protected $path = '';
|
|
|
8 |
protected $results = array();
|
9 |
public $errorMsg = false;
|
10 |
private $apiKey = false;
|
14 |
private $lastStatusTime = false;
|
15 |
private $patterns = "";
|
16 |
public function __sleep(){
|
17 |
+
return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned', 'startTime', 'lastStatusTime', 'patterns');
|
18 |
}
|
19 |
public function __wakeup(){
|
20 |
}
|
21 |
+
public function __construct($apiKey, $wordpressVersion, $path){
|
22 |
$this->apiKey = $apiKey;
|
23 |
$this->wordpressVersion = $wordpressVersion;
|
24 |
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
|
|
|
25 |
if($path[strlen($path) - 1] != '/'){
|
26 |
$path .= '/';
|
27 |
}
|
49 |
if(! $this->lastStatusTime){
|
50 |
$this->lastStatusTime = microtime(true);
|
51 |
}
|
52 |
+
$db = new wfDB();
|
53 |
+
$res1 = $db->query("select filename, filenameMD5, hex(newMD5) as newMD5 from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0");
|
54 |
+
while($rec1 = mysql_fetch_assoc($res1)){
|
55 |
+
$db->query("update " . $db->prefix() . "wfFileMods set oldMD5 = newMD5 where filenameMD5='%s'", $rec1['filenameMD5']); //A way to mark as scanned so that if we come back from a sleep we don't rescan this one.
|
56 |
+
$file = $rec1['filename'];
|
57 |
+
$fileSum = $rec1['newMD5'];
|
|
|
|
|
|
|
|
|
58 |
|
59 |
if(! file_exists($this->path . $file)){
|
60 |
continue;
|
90 |
}
|
91 |
|
92 |
$stime = microtime(true);
|
|
|
|
|
|
|
|
|
|
|
93 |
$fh = @fopen($this->path . $file, 'r');
|
94 |
if(! $fh){
|
95 |
continue;
|
239 |
return $this->results;
|
240 |
}
|
241 |
private function writeScanningStatus(){
|
242 |
+
wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " unrecognized files at " . sprintf('%.2f', ($this->totalFilesScanned / (microtime(true) - $this->startTime))) . " per second");
|
243 |
}
|
244 |
private function addEncIssue($ignoreP, $ignoreC, $encoding, $file){
|
245 |
$this->addResult(array(
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
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.4.
|
6 |
-
Stable tag: 3.2
|
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 |
|
@@ -153,6 +153,22 @@ or a theme, because often these have been updated to fix a security hole.
|
|
153 |
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
|
154 |
|
155 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
= 3.2.7 =
|
157 |
* Fixed dates and times in activity log alert emails and other emails to be in site's local timezone.
|
158 |
* Added advanced country blocking options which allow bypass if a special URL is hit.
|
2 |
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.4.2
|
6 |
+
Stable tag: 3.3.2
|
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 |
|
153 |
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
|
154 |
|
155 |
== Changelog ==
|
156 |
+
= 3.3.2 =
|
157 |
+
* A complete rearchitecture of Wordfence scanning to massively improve performance.
|
158 |
+
* Our free customers are now 100% back in business. Apologies for the delay, but this was worth the wait.
|
159 |
+
* Wordfence is now 4X faster for both free and paid customers.
|
160 |
+
* Significantly reduced CPU and memory overhead.
|
161 |
+
* Significantly reduced network througput when communicating with Wordfence scanning servers.
|
162 |
+
* Big performance improvement on our own scanning servers which allows us to continue to provide Wordfence free for the forseeable future.
|
163 |
+
* Upgraded scanning API to version 2.4
|
164 |
+
* Upgraded Geo IP database to October version.
|
165 |
+
* Moved core, theme, plugin and malware scanning into hashing recursive routine for big performance gain.
|
166 |
+
* Removed need for fileQ in hashing routine for reduction in memory usage and reduction in DB write size.
|
167 |
+
* Removed send-packet architecture and now processing files locally by fetching comparison data from scanning server instead.
|
168 |
+
* Removed wfModTracker - old module that is no longer used.
|
169 |
+
* Malware is now scanned by fetching hash prefixes from WF server instead of sending hashes of every file to our server. Much more efficient.
|
170 |
+
* Made status messages in summary console a little more user friendly.
|
171 |
+
|
172 |
= 3.2.7 =
|
173 |
* Fixed dates and times in activity log alert emails and other emails to be in site's local timezone.
|
174 |
* Added advanced country blocking options which allow bypass if a special URL is hit.
|
wordfence.php
CHANGED
@@ -4,10 +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: 3.2
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
-
define('WORDFENCE_VERSION', '3.2
|
11 |
if(! defined('WORDFENCE_VERSIONONLY_MODE')){
|
12 |
if((int) @ini_get('memory_limit') < 64){
|
13 |
@ini_set('memory_limit', '64M'); //Some hosts have ini set at as little as 32 megs. 64 is the min sane amount of memory.
|
4 |
Plugin URI: http://wordfence.com/
|
5 |
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
|
6 |
Author: Mark Maunder
|
7 |
+
Version: 3.3.2
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
+
define('WORDFENCE_VERSION', '3.3.2');
|
11 |
if(! defined('WORDFENCE_VERSIONONLY_MODE')){
|
12 |
if((int) @ini_get('memory_limit') < 64){
|
13 |
@ini_set('memory_limit', '64M'); //Some hosts have ini set at as little as 32 megs. 64 is the min sane amount of memory.
|