Wordfence Security – Firewall & Malware Scan - Version v1.4.1

Version Description

Download this release

Release Info

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

Code changes from version 1.3.3 to v1.4.1

lib/wfAPI.php CHANGED
@@ -56,7 +56,8 @@ class wfAPI {
56
  curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . WORDFENCE_VERSION);
57
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
58
  curl_setopt ($curl, CURLOPT_HEADER, 0);
59
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, TRUE);
 
60
  curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
61
  @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
62
  @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
@@ -94,7 +95,9 @@ class wfAPI {
94
  //curl_setopt($curl, CURLOPT_VERBOSE, true);
95
  curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
96
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
97
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, TRUE);
 
 
98
  @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
99
  @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
100
  curl_setopt($curl, CURLOPT_POST, true);
56
  curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . WORDFENCE_VERSION);
57
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
58
  curl_setopt ($curl, CURLOPT_HEADER, 0);
59
+ curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
60
+ curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
61
  curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
62
  @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
63
  @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
95
  //curl_setopt($curl, CURLOPT_VERBOSE, true);
96
  curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
97
  curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
98
+ curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
99
+ curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
100
+
101
  @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
102
  @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
103
  curl_setopt($curl, CURLOPT_POST, true);
lib/wfIssues.php CHANGED
@@ -29,10 +29,6 @@ class wfIssues {
29
  if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return; }
30
  }
31
 
32
- //This is flagged either during a scan or after a scan. The next scan checks to see if it's been set and will do a full scan if it's set. The next scan also sets this to zero.
33
- //So the logic is: If any new issues have been added in the previous scan or since then, then do a full scan because we delete the 'new' issues at the start of each scan and need to check if they still exist.
34
- wfConfig::set('newIssueAddedLastScan', 1);
35
-
36
  $this->totalIssues++;
37
  if($severity == 1){
38
  $this->totalCriticalIssues++;
@@ -61,14 +57,12 @@ class wfIssues {
61
  );
62
  }
63
  public function deleteIgnored(){
64
- wfConfig::set('ignoreListChanged', 1);
65
  $this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
66
  }
67
  public function deleteNew(){
68
  $this->getDB()->query("delete from " . $this->issuesTable . " where status='new'");
69
  }
70
  public function ignoreAllNew(){
71
- wfConfig::set('ignoreListChanged', 1);
72
  $this->getDB()->query("update " . $this->issuesTable . " set status='ignoreC' where status='new'");
73
  }
74
  public function emailNewIssues(){
@@ -122,10 +116,6 @@ class wfIssues {
122
  }
123
  public function updateIssue($id, $status){ //ignoreC, ignoreP, delete or new
124
  $currentStatus = $this->getDB()->querySingle("select status from " . $this->issuesTable . " where id=%d", $id);
125
- if($currentStatus == 'ignoreC' || $currentStatus == 'ignoreP'){
126
- //We are removing something from the ignore list so we need to flag ignoreListChanged so that the next scan is a full scan
127
- wfConfig::set('ignoreListChanged', 1);
128
- }
129
  if($status == 'delete'){
130
  $this->getDB()->query("delete from " . $this->issuesTable . " where id=%d", $id);
131
  } else if($status == 'ignoreC' || $status == 'ignoreP' || $status == 'new'){
29
  if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return; }
30
  }
31
 
 
 
 
 
32
  $this->totalIssues++;
33
  if($severity == 1){
34
  $this->totalCriticalIssues++;
57
  );
58
  }
59
  public function deleteIgnored(){
 
60
  $this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
61
  }
62
  public function deleteNew(){
63
  $this->getDB()->query("delete from " . $this->issuesTable . " where status='new'");
64
  }
65
  public function ignoreAllNew(){
 
66
  $this->getDB()->query("update " . $this->issuesTable . " set status='ignoreC' where status='new'");
67
  }
68
  public function emailNewIssues(){
116
  }
117
  public function updateIssue($id, $status){ //ignoreC, ignoreP, delete or new
118
  $currentStatus = $this->getDB()->querySingle("select status from " . $this->issuesTable . " where id=%d", $id);
 
 
 
 
119
  if($status == 'delete'){
120
  $this->getDB()->query("delete from " . $this->issuesTable . " where id=%d", $id);
121
  } else if($status == 'ignoreC' || $status == 'ignoreP' || $status == 'new'){
lib/wfModTracker.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  class wfModTracker {
3
  private $themeSum = false;
4
  private $pluginSum = false;
@@ -9,12 +10,18 @@ class wfModTracker {
9
  public function __construct(){
10
  global $wpdb;
11
  $this->changesTable = $wpdb->prefix . 'wfFileChanges';
 
12
  $this->db = new wfDB();
 
13
  $this->themeSum = $this->makeSum(get_theme_root());
 
14
  $this->pluginSum = $this->makeSum(WP_PLUGIN_DIR);
 
15
  $this->coreSum = $this->makeCoreSum();
16
  $this->allFilesSum = array();
 
17
  $this->getAllFilesSum(ABSPATH);
 
18
  }
19
  public static function resetChanges(){
20
  wfConfig::set('wfmdt_coreSum', '');
@@ -111,5 +118,8 @@ class wfModTracker {
111
  }
112
  return md5($str);
113
  }
 
 
 
114
  }
115
  ?>
1
  <?php
2
+ require_once('wordfenceClass.php');
3
  class wfModTracker {
4
  private $themeSum = false;
5
  private $pluginSum = false;
10
  public function __construct(){
11
  global $wpdb;
12
  $this->changesTable = $wpdb->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', '');
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
@@ -6,7 +6,6 @@ require_once('wordfenceScanner.php');
6
  require_once('wfIssues.php');
7
  require_once('wfDB.php');
8
  require_once('wfUtils.php');
9
- require_once('wfModTracker.php');
10
  class wfScanEngine {
11
  private $i = false;
12
  private $api = false;
@@ -14,7 +13,6 @@ class wfScanEngine {
14
  private $wp_version = false;
15
  private $apiKey = false;
16
  private $errorStopped = false;
17
- private $modTracker = false;
18
  private $dictWords = array();
19
  public function __construct(){
20
  $this->i = new wfIssues();
@@ -26,52 +24,11 @@ class wfScanEngine {
26
  }
27
  public function go(){
28
  $this->status(1, 'info', "Initializing scan");
29
-
30
- /*
31
- The logic for determining if we need to do a full scan or only scan files that have changed is as follows:
32
-
33
- NOTE NOTE: We delete all 'new' issues at the start of every scan.
34
-
35
- If the last scan added any new issues, or if new issues have been added since then, we do a full scan
36
- because at the start of this scan we delete those 'new' issues and we need to check which of those new issues
37
- are still issues.
38
-
39
- If the previous scan didn't detect any new issues, no issues have been added since then and no files have changed
40
- then we don't need to do a full scan UNLESS:
41
-
42
- If the user has deleted 'ignored' issues then we need to do a full scan to see if any of those previously
43
- ignored issues need to show up in the 'new' list.
44
-
45
- So to accomplish this we track if new issues have been added during or since the previous scan and if any ignored issues
46
- have been modified since the previous scan. If any of that is true, then we force a new scan.
47
-
48
- NOTE that we don't have to 'resetChanges()' if the user deletes 'new' issues or moves them to 'ignore' because a full scan
49
- will already occur because the previous scan called addIssue to create those issues (because we delete all new issues at the start of each scan)
50
- and thereby set newIssueAddedLastScan.
51
-
52
- */
53
-
54
- $forceFullScan = false;
55
- if(wfConfig::get('newIssueAddedLastScan', false)){
56
- $forceFullScan = true; //Do a full scan if a new issue was added in the last scan (or since the last scan) because we need to check if that issue has been resolved or still exists.
57
- }
58
- if(wfConfig::get('ignoreListChanged', false)){
59
- $forceFullScan = true; //Do a full scan if the ignore list changed because we need to show that issue in 'new' list if it still exists.
60
- }
61
- wfConfig::set('newIssueAddedLastScan', 0);
62
- wfConfig::set('ignoreListChanged', 0);
63
  $this->i->deleteNew();
64
 
65
  try {
66
- if($forceFullScan){
67
- $this->status(1, 'info', "Getting ready to do a full scan by reinitializing change tracking");
68
- wfModTracker::resetChanges();
69
- }
70
- $this->status(1, 'info', "Compiling list of changed files");
71
- $this->modTracker = new wfModTracker();
72
  $this->doScan();
73
  if(! $this->errorStopped){
74
- $this->modTracker->logCurrentState();
75
  wfConfig::set('lastScanCompleted', 'ok');
76
  }
77
  //updating this scan ID will trigger the scan page to load/reload the results.
@@ -79,7 +36,6 @@ class wfScanEngine {
79
  //scan ID only incremented at end of scan to make UI load new results
80
  $this->emailNewIssues();
81
  } catch(Exception $e){
82
- wfModTracker::resetChanges();
83
  $this->errorStop($e->getMessage());
84
  }
85
  wordfence::scheduleNextScan(true);
@@ -94,15 +50,13 @@ class wfScanEngine {
94
  $this->errorStop($this->api->errorMsg);
95
  return;
96
  }
97
- $knownFiles = $this->scanKnownFiles();
98
  if($this->errorStopped){
99
- wfModTracker::resetChanges();
100
  return;
101
  }
102
  if(wfConfig::get('scansEnabled_fileContents')){
103
- $this->scanFileContents($knownFiles);
104
  if($this->errorStopped){
105
- wfModTracker::resetChanges();
106
  return;
107
  }
108
  }
@@ -138,43 +92,23 @@ class wfScanEngine {
138
  private function scanKnownFiles(){
139
  $malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
140
  if(wfConfig::get('scansEnabled_core')){
141
- if($this->modTracker->filesModifiedInCore()){
142
- $this->status(2, 'info', "Enabling core scan because core files have been modified.");
143
- $coreScanEnabled = true;
144
- } else {
145
- $this->status(2, 'info', "Skipping core scan because no core files were modified.");
146
- }
147
  } else {
148
  $this->status(2, 'info', "Skipping core scan because it's disabled.");
149
  }
150
  if(wfConfig::get('scansEnabled_plugins')){
151
- if($this->modTracker->filesModifiedInPlugins()){
152
- $this->status(2, 'info', "Enabling plugin scan because files in plugins directory have been modified.");
153
- $pluginScanEnabled = true;
154
- } else {
155
- $this->status(2, 'info', "Skipping plugin scan because no files in plugins directory have been modified.");
156
- }
157
  } else {
158
  $this->status(2, 'info', "Skipping plugin scan because it's disabled.");
159
  }
160
  if(wfConfig::get('scansEnabled_themes')){
161
- if($this->modTracker->filesModifiedInThemes()){
162
- $this->status(2, 'info', "Enabling theme scan because files in themes dir have been modified.");
163
- $themeScanEnabled = true;
164
- } else {
165
- $this->status(2, 'info', "Skipping theme scan because no files in themes directory have been modified.");
166
- }
167
  } else {
168
  $this->status(2, 'info', "Skipping themes scan because it's disabled.");
169
  }
170
 
171
  if(wfConfig::get('scansEnabled_malware')){
172
- if($this->modTracker->anyFilesChanged()){
173
- $this->status(2, 'info', "Enabling malware scan because we have modified files in the dir structure.");
174
- $malwareScanEnabled = true;
175
- } else {
176
- $this->status(2, 'info', "Skipping malware scan because no files were modified in the dir structure.");
177
- }
178
  } else {
179
  $this->status(2, 'info', "Skipping malware scan because it's disabled.");
180
  }
@@ -184,11 +118,25 @@ class wfScanEngine {
184
  return array();
185
  }
186
 
187
- $this->status(1, 'info', "Scanning known files.");
188
  //CORE SCAN
189
- $this->status(2, 'info', "Generating a hash of directory structure.");
190
- $hasher = new wordfenceHash();
191
- $hashes = $hasher->dirHash(ABSPATH, strlen(ABSPATH) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  $this->status(2, 'info', "Done hash. Updating summary items.");
193
  $this->i->updateSummaryItem('totalData', wfUtils::formatBytes($hasher->totalData));
194
  $this->i->updateSummaryItem('totalFiles', $hasher->totalFiles);
@@ -256,19 +204,17 @@ class wfScanEngine {
256
  $this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
257
  $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
258
  }
259
- return $result1['knownFiles'];
260
  }
261
- private function scanFileContents($knownFiles){
262
  $this->status(1, 'info', "Scanning file contents.");
263
- if(! is_array($knownFiles)){
264
- $knownFiles = array();
265
  }
266
  $this->status(2, 'info', "Getting list of changed files since last scan.");
267
- $filesToScan = $this->modTracker->getChangedFiles(ABSPATH, $knownFiles); //get changed files since last scan and exclude $knownFiles
268
- $this->status(2, 'info', "Got " . sizeof($filesToScan) . " changed files to scan.");
269
  $scanner = new wordfenceScanner($this->apiKey, $this->wp_version);
270
  $this->status(2, 'info', "Starting scan of file contents");
271
- $result2 = $scanner->scan(ABSPATH, $filesToScan, array($this, 'status'));
272
  $this->status(2, 'info', "Done file contents scan");
273
  if($scanner->errorMsg){
274
  $this->errorStop($scanner->errorMsg);
6
  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;
13
  private $wp_version = false;
14
  private $apiKey = false;
15
  private $errorStopped = false;
 
16
  private $dictWords = array();
17
  public function __construct(){
18
  $this->i = new wfIssues();
24
  }
25
  public function go(){
26
  $this->status(1, 'info', "Initializing scan");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  $this->i->deleteNew();
28
 
29
  try {
 
 
 
 
 
 
30
  $this->doScan();
31
  if(! $this->errorStopped){
 
32
  wfConfig::set('lastScanCompleted', 'ok');
33
  }
34
  //updating this scan ID will trigger the scan page to load/reload the results.
36
  //scan ID only incremented at end of scan to make UI load new results
37
  $this->emailNewIssues();
38
  } catch(Exception $e){
 
39
  $this->errorStop($e->getMessage());
40
  }
41
  wordfence::scheduleNextScan(true);
50
  $this->errorStop($this->api->errorMsg);
51
  return;
52
  }
53
+ $unknownFiles = $this->scanKnownFiles();
54
  if($this->errorStopped){
 
55
  return;
56
  }
57
  if(wfConfig::get('scansEnabled_fileContents')){
58
+ $this->scanFileContents($unknownFiles);
59
  if($this->errorStopped){
 
60
  return;
61
  }
62
  }
92
  private function scanKnownFiles(){
93
  $malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
94
  if(wfConfig::get('scansEnabled_core')){
95
+ $coreScanEnabled = true;
 
 
 
 
 
96
  } else {
97
  $this->status(2, 'info', "Skipping core scan because it's disabled.");
98
  }
99
  if(wfConfig::get('scansEnabled_plugins')){
100
+ $pluginScanEnabled = true;
 
 
 
 
 
101
  } else {
102
  $this->status(2, 'info', "Skipping plugin scan because it's disabled.");
103
  }
104
  if(wfConfig::get('scansEnabled_themes')){
105
+ $themeScanEnabled = true;
 
 
 
 
 
106
  } else {
107
  $this->status(2, 'info', "Skipping themes scan because it's disabled.");
108
  }
109
 
110
  if(wfConfig::get('scansEnabled_malware')){
111
+ $malwareScanEnabled = true;
 
 
 
 
 
112
  } else {
113
  $this->status(2, 'info', "Skipping malware scan because it's disabled.");
114
  }
118
  return array();
119
  }
120
 
 
121
  //CORE SCAN
122
+ $this->status(2, 'info', "Examining files in WordPress base directory.");
123
+ $hasher = new wordfenceHash(strlen(ABSPATH));
124
+ $includeInScan = 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');
125
+ $baseContents = scandir(ABSPATH);
126
+ $includeBase = true;
127
+ if(sizeof($baseContents) > 500){ //If there are more than 500 files in the base dir, then don't scan base dir files other than core WP files.
128
+ $includeBase = false;
129
+ }
130
+ if($includeBase){
131
+ foreach($baseContents as $file){ //Only include base files less than a meg that are files.
132
+ $file = rtrim(ABSPATH, '/') . '/' . $file;
133
+ if(is_file($file) && @filesize(ABSPATH . $file) < 1000000 && (! in_array($file, $includeInScan)) ){
134
+ $includeInScan[] = $file;
135
+ }
136
+ }
137
+ }
138
+ $this->status(2, 'info', "Hashing your WordPress files for comparison against originals.");
139
+ $hashes = $hasher->hashPaths(ABSPATH, $includeInScan);
140
  $this->status(2, 'info', "Done hash. Updating summary items.");
141
  $this->i->updateSummaryItem('totalData', wfUtils::formatBytes($hasher->totalData));
142
  $this->i->updateSummaryItem('totalFiles', $hasher->totalFiles);
204
  $this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
205
  $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
206
  }
207
+ return $result1['unknownFiles'];
208
  }
209
+ private function scanFileContents($unknownFiles){
210
  $this->status(1, 'info', "Scanning file contents.");
211
+ if(! is_array($unknownFiles)){
212
+ $unknownFiles = array();
213
  }
214
  $this->status(2, 'info', "Getting list of changed files since last scan.");
 
 
215
  $scanner = new wordfenceScanner($this->apiKey, $this->wp_version);
216
  $this->status(2, 'info', "Starting scan of file contents");
217
+ $result2 = $scanner->scan(ABSPATH, $unknownFiles, array($this, 'status'));
218
  $this->status(2, 'info', "Done file contents scan");
219
  if($scanner->errorMsg){
220
  $this->errorStop($scanner->errorMsg);
lib/wordfenceConstants.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- define('WORDFENCE_VERSION', 1.1);
3
  define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_MAX_SCAN_TIME', 3600);
5
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
1
  <?php
2
+ define('WORDFENCE_VERSION', 1.2);
3
  define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_MAX_SCAN_TIME', 3600);
5
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
lib/wordfenceHash.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  class wordfenceHash {
3
  private $whitespace = array("\n","\r","\t"," ");
4
  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,63 +7,71 @@ class wordfenceHash {
6
  public $totalDirs = 0;
7
  public $linesOfPHP = 0;
8
  public $linesOfJCH = 0; //lines of HTML, CSS and javascript
9
- public function dirHash($path, $striplen, $filter = array(), $userfunc = false){
10
- $hashes = $this->_dirHash($path, $striplen, $filter, $userfunc);
11
- $hashes['.'] = $this->hashOfHashes($hashes);
12
- return $hashes;
13
  }
14
- private function _dirHash($path, $striplen, $filter, $userfunc = false){
15
  if($path[strlen($path) - 1] != '/'){
16
  $path .= '/';
17
  }
18
- $cont = scandir($path);
19
-
20
- $ret = array();
21
- for($i = 0; $i < sizeof($cont); $i++){
22
- if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
23
- if(in_array($cont[$i], $filter)){ continue; }
24
- $file = $path . $cont[$i];
25
- if($userfunc){ call_user_func($userfunc, "Scanning: $file"); }
26
- if(is_file($file)){
27
- $wfHash = $this->wfHash($file, true);
28
- if($wfHash){
29
- $ret[substr($file, $striplen)] = $wfHash;
30
- //Now that we know we can open the file, lets update stats
31
- if(preg_match('/\.(?:js|html|htm|css)$/i', $file)){
32
- $this->linesOfJCH += sizeof(file($file));
33
- } else if(preg_match('/\.php$/i', $file)){
34
- $this->linesOfPHP += sizeof(file($file));
35
- }
36
- $this->totalFiles++;
37
- $this->totalData += filesize($file);
 
 
 
 
 
 
 
 
 
38
  }
39
-
40
- } else if(is_dir($file)) {
41
- $this->totalDirs++;
42
- $dirHashes = $this->_dirHash($file, $striplen, $filter);
43
- $dirHashes[substr($file, $striplen) . '/'] = $this->hashOfHashes($dirHashes);
44
- $ret = array_merge($ret, $dirHashes);
45
  }
46
  }
47
- return $ret;
48
  }
49
- public function hashOfHashes($dirHashes){
50
- ksort($dirHashes);
51
- $all_md5 = "";
52
- $all_sha = "";
53
- $all_shac = "";
54
- foreach($dirHashes as $key => $val){
55
- $all_md5 .= $val[0];
56
- $all_sha .= $val[1];
57
- $all_shac .= $val[2];
 
 
 
58
  }
59
- return array( md5($all_md5, true), hash('sha256', $all_sha, true), hash('sha256', $all_shac, true) );
60
  }
61
  public function wfHash($file, $binary = true){
62
  $md5 = @md5_file($file, $binary);
63
  if(! $md5){ return false; }
64
- $sha = @hash_file('sha256', $file, $binary);
65
- if(! $sha){ return false; }
66
  $fp = @fopen($file, "rb");
67
  if(! $fp){
68
  return false;
@@ -72,7 +81,8 @@ class wordfenceHash {
72
  hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536)));
73
  }
74
  $shac = hash_final($ctx, $binary);
75
- return array($md5, $sha, $shac, filesize($file) );
 
76
  }
77
  public static function bin2hex($hashes){
78
  function wf_func1($elem){
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}'
7
  public $totalDirs = 0;
8
  public $linesOfPHP = 0;
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 hashPaths($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.
16
  if($path[strlen($path) - 1] != '/'){
17
  $path .= '/';
18
  }
19
+ $files = scandir($path);
20
+ foreach($files as $file){
21
+ if(sizeof($only) > 0 && (! in_array($file, $only))){
22
+ continue;
23
+ }
24
+ $file = $path . $file;
25
+ wordfence::status(2, 'info', "Hashing item in base dir: $file");
26
+ $this->_dirHash($file);
27
+ }
28
+ return $this->hashes;
29
+ }
30
+ private function _dirHash($path){
31
+ if(substr($path, -3, 3) == '/..' || substr($path, -2, 2) == '/.'){
32
+ return;
33
+ }
34
+ if(is_dir($path)){
35
+ $this->totalDirs++;
36
+ if($path[strlen($path) - 1] != '/'){
37
+ $path .= '/';
38
+ }
39
+ $cont = scandir($path);
40
+ for($i = 0; $i < sizeof($cont); $i++){
41
+ if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
42
+ $file = $path . $cont[$i];
43
+ if(is_file($file)){
44
+ $this->processFile($file);
45
+ } else if(is_dir($file)) {
46
+ wordfence::status(2, 'info', "Traversing into dir $file");
47
+ $this->_dirHash($file);
48
  }
49
+ }
50
+ } else {
51
+ if(is_file($path)){
52
+ $this->processFile($path);
 
 
53
  }
54
  }
 
55
  }
56
+ private function processFile($file){
57
+ $wfHash = $this->wfHash($file, true);
58
+ if($wfHash){
59
+ $this->hashes[substr($file, $this->striplen)] = $wfHash;
60
+ //Now that we know we can open the file, lets update stats
61
+ if(preg_match('/\.(?:js|html|htm|css)$/i', $file)){
62
+ $this->linesOfJCH += sizeof(file($file));
63
+ } else if(preg_match('/\.php$/i', $file)){
64
+ $this->linesOfPHP += sizeof(file($file));
65
+ }
66
+ $this->totalFiles++;
67
+ $this->totalData += filesize($file);
68
  }
 
69
  }
70
  public function wfHash($file, $binary = true){
71
  $md5 = @md5_file($file, $binary);
72
  if(! $md5){ return false; }
73
+ //$sha = @hash_file('sha256', $file, $binary);
74
+ //if(! $sha){ return false; }
75
  $fp = @fopen($file, "rb");
76
  if(! $fp){
77
  return false;
81
  hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536)));
82
  }
83
  $shac = hash_final($ctx, $binary);
84
+ //Taking out $sha for now because we don't use it on the scanning server side
85
+ return array($md5, '', $shac, filesize($file) );
86
  }
87
  public static function bin2hex($hashes){
88
  function wf_func1($elem){
readme.txt CHANGED
@@ -1,15 +1,17 @@
1
  === Plugin Name ===
2
  Contributors: mmaunder
3
- Tags: anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence
4
  Requires at least: 3.3.1
5
  Tested up to: 3.3.2
6
- Stable tag: 1.3.3
7
 
8
- Wordfence is an enterprise firewall and anti-virus plugin for WordPress.
9
 
10
  == Description ==
11
 
12
- Wordfence is a free enterprise class firewall and anti-virus plugin for WordPress websites.
 
 
13
 
14
  Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
15
  We also offer a Premium API key that adds additional scanning capabilities. See below for details.
@@ -37,6 +39,8 @@ newest threats to your WordPress website.
37
 
38
  == Installation ==
39
 
 
 
40
  To install Wordfence and start protecting your WordPress website:
41
 
42
  1. Install Wordfence automatically or by uploading the ZIP file.
@@ -51,6 +55,8 @@ To install Wordfence and start protecting your WordPress website:
51
 
52
  == Frequently Asked Questions ==
53
 
 
 
54
  = Why does Wordfence need an API key? =
55
 
56
  Wordfence contacts our servers for a variety of reasons. These include: comparing the hashes of your core, theme and plugin files
@@ -89,6 +95,16 @@ Yes! Simply visit the Options page, click on advanced options and enable or disa
89
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
90
 
91
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
92
  = 1.3.3 =
93
  * Made real-time server polling more efficient.
94
  * Entering your API key now automatically starts your first scan. Was causing some confusion.
1
  === Plugin Name ===
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.3.2
6
+ Stable tag: 1.4.1
7
 
8
+ Wordfence is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
9
 
10
  == Description ==
11
 
12
+ [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
13
+
14
+ Wordfence is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
15
 
16
  Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
17
  We also offer a Premium API key that adds additional scanning capabilities. See below for details.
39
 
40
  == Installation ==
41
 
42
+ [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
43
+
44
  To install Wordfence and start protecting your WordPress website:
45
 
46
  1. Install Wordfence automatically or by uploading the ZIP file.
55
 
56
  == Frequently Asked Questions ==
57
 
58
+ [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
59
+
60
  = Why does Wordfence need an API key? =
61
 
62
  Wordfence contacts our servers for a variety of reasons. These include: comparing the hashes of your core, theme and plugin files
95
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
96
 
97
  == Changelog ==
98
+ = 1.4.1 =
99
+ * This is a major release, please upgrade immediately.
100
+ * Only scan files in the WordPress ABSPATH root directory and known WordPress subdirectories. Prevents potentially massive scans on hosts that have large dirs off their wordpress root.
101
+ * Don't generate plain SHA hashes anymore because we don't currently use them on the server side for scanning. (Still generates md5's and SHAC)
102
+ * No longer do change tracking on files before scans because the change tracking does almost the same amount of work when generating hashes as the actual scan. So just do the scan, which is now faster.
103
+ * Updated internal version to 1.2 to use new code on the server side which sends back a list of unknown files rather than known files, which is usually smaller and more network efficient.
104
+ * Improved logging in activity log.
105
+ * Removed SSL peer verification because some hosts have bad cert config. Connection to our servers is still via SSL.
106
+ * Fixed a few minor issues. Overall you should notice that scans are much faster now.
107
+
108
  = 1.3.3 =
109
  * Made real-time server polling more efficient.
110
  * Entering your API key now automatically starts your first scan. Was causing some confusion.
wordfence.php CHANGED
@@ -2,9 +2,9 @@
2
  /*
3
  Plugin Name: Wordfence
4
  Plugin URI: http://wordfence.com/
5
- Description: Anti-virus and Firewall for WordPress
6
  Author: Mark Maunder
7
- Version: 1.3.3
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');
2
  /*
3
  Plugin Name: Wordfence
4
  Plugin URI: http://wordfence.com/
5
+ Description: WordPress Security - Anti-virus and Firewall for WordPress
6
  Author: Mark Maunder
7
+ Version: 1.4.1
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');