PluginProbe ʕ •ᴥ•ʔ
Wordfence Security – Firewall, Malware Scan, and Login Security / 6.3.2
Wordfence Security – Firewall, Malware Scan, and Login Security v6.3.2
8.2.2 8.2.1 8.2.0 3.7.1 3.7.2 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.9.1 4.0.1 4.0.2 4.0.3 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.0.9 5.1.1 5.1.2 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.1 5.3.10 5.3.11 5.3.12 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 6.0.1 6.0.10 6.0.11 6.0.12 6.0.14 6.0.15 6.0.16 6.0.17 6.0.18 6.0.19 6.0.2 6.0.20 6.0.21 6.0.22 6.0.23 6.0.24 6.0.25 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.0.8 6.0.9 6.1.1 6.1.10 6.1.11 6.1.12 6.1.14 6.1.15 6.1.16 6.1.17 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.10 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10 6.3.11 6.3.12 6.3.14 6.3.15 6.3.16 6.3.17 6.3.18 6.3.19 6.3.2 6.3.20 6.3.21 6.3.22 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.3.8 6.3.9 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1.0 7.1.1 7.1.10 7.1.11 7.1.12 7.1.14 7.1.15 7.1.16 7.1.17 7.1.18 7.1.19 7.1.2 7.1.20 7.1.3 7.1.4 7.1.5 7.1.6 7.1.7 7.1.8 7.1.9 7.10.0 7.10.1 7.10.2 7.10.3 7.10.4 7.10.5 7.10.6 7.10.7 7.11.0 7.11.1 7.11.2 7.11.3 7.11.4 7.11.5 7.11.6 7.11.7 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.4.0 7.4.1 7.4.10 7.4.11 7.4.12 7.4.14 7.4.2 7.4.3 trunk 7.4.4 1.1 7.4.5 1.2 7.4.6 1.3 7.4.7 1.3.1 7.4.8 1.3.2 7.4.9 1.3.3 7.5.0 1.4.2 7.5.1 1.4.3 7.5.10 1.4.4 7.5.11 1.4.5 7.5.2 1.4.6 7.5.3 1.4.7 7.5.4 1.4.8 7.5.5 1.5.1 7.5.6 1.5.2 7.5.7 1.5.3 7.5.8 1.5.4 7.5.9 1.5.5 7.6.0 1.5.6 7.6.1 2.0.1 7.6.2 2.0.2 7.7.0 2.0.3 7.7.1 2.0.5 7.8.0 2.0.6 7.8.1 2.0.7 7.8.2 2.1.0 7.9.0 2.1.1 7.9.1 2.1.2 7.9.2 2.1.3 7.9.3 2.1.4 8.0.0 2.1.5 8.0.1 3.0.2 8.0.2 3.0.3 8.0.3 3.0.4 8.0.4 3.0.5 8.0.5 3.0.6 8.1.0 3.0.7 8.1.1 3.0.8 8.1.2 3.0.9 8.1.3 3.1.0 8.1.4 3.1.1 v1.4.1 3.1.2 3.1.4 3.1.6 3.2.1 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.1 3.4.4 3.4.5 3.5.1 3.5.2 3.6.1 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9
wordfence / lib / wordfenceScanner.php
wordfence / lib Last commit date
Diff 9 years ago dashboard 9 years ago .htaccess 14 years ago Diff.php 14 years ago GeoIP.dat 9 years ago GeoIPv6.dat 9 years ago IPTraf.php 9 years ago compat.php 10 years ago conntest.php 11 years ago cronview.php 10 years ago dashboard.php 9 years ago dbview.php 11 years ago diffResult.php 11 years ago email_genericAlert.php 9 years ago email_newIssues.php 9 years ago email_passwdChanged.php 10 years ago email_pleaseChangePasswd.php 10 years ago email_unlockRequest.php 11 years ago live_activity.php 9 years ago menu_activity.php 9 years ago menu_blocking.php 9 years ago menu_blocking_advancedBlocking.php 9 years ago menu_blocking_blockedIPs.php 9 years ago menu_blocking_countryBlocking.php 9 years ago menu_dashboard.php 9 years ago menu_firewall.php 9 years ago menu_firewall_bruteForce.php 9 years ago menu_firewall_rateLimiting.php 9 years ago menu_firewall_waf.php 9 years ago menu_options.php 9 years ago menu_scan.php 9 years ago menu_scan_options.php 9 years ago menu_scan_scan.php 9 years ago menu_scan_schedule.php 9 years ago menu_tools.php 9 years ago menu_tools_diagnostic.php 9 years ago menu_tools_passwd.php 9 years ago menu_tools_twoFactor.php 9 years ago menu_tools_whois.php 9 years ago pageTitle.php 9 years ago schedWeekEntry.php 12 years ago sysinfo.php 10 years ago unknownFiles.php 11 years ago viewFullActivityLog.php 9 years ago wf503.php 9 years ago wfAPI.php 9 years ago wfAction.php 14 years ago wfActivityReport.php 9 years ago wfArray.php 13 years ago wfBrowscap.php 12 years ago wfBrowscapCache.php 9 years ago wfBulkCountries.php 13 years ago wfCache.php 9 years ago wfConfig.php 9 years ago wfCountryMap.php 13 years ago wfCrawl.php 10 years ago wfCrypt.php 11 years ago wfDB.php 9 years ago wfDashboard.php 9 years ago wfDiagnostic.php 9 years ago wfDict.php 14 years ago wfDirectoryIterator.php 11 years ago wfGeoIP.php 9 years ago wfHelperBin.php 11 years ago wfHelperString.php 11 years ago wfIPWhitelist.php 9 years ago wfIssues.php 9 years ago wfLockedOut.php 9 years ago wfLog.php 9 years ago wfMD5BloomFilter.php 9 years ago wfNotification.php 9 years ago wfRESTAPI.php 9 years ago wfRate.php 10 years ago wfScan.php 9 years ago wfScanEngine.php 9 years ago wfSchema.php 9 years ago wfStyle.php 9 years ago wfUnlockMsg.php 10 years ago wfUpdateCheck.php 9 years ago wfUtils.php 9 years ago wfView.php 10 years ago wfViewResult.php 11 years ago wordfenceClass.php 9 years ago wordfenceConstants.php 9 years ago wordfenceHash.php 9 years ago wordfenceScanner.php 9 years ago wordfenceURLHoover.php 9 years ago
wordfenceScanner.php
545 lines
1 <?php
2 require_once('wordfenceConstants.php');
3 require_once('wordfenceClass.php');
4 require_once('wordfenceURLHoover.php');
5 class wordfenceScanner {
6 /*
7 * Mask to return all patterns in the exclusion list.
8 * @var int
9 */
10 const EXCLUSION_PATTERNS_ALL = PHP_INT_MAX;
11 /*
12 * Mask for patterns that the user has added.
13 */
14 const EXCLUSION_PATTERNS_USER = 0x1;
15 /*
16 * Mask for patterns that should be excluded from the known files scan.
17 */
18 const EXCLUSION_PATTERNS_KNOWN_FILES = 0x2;
19 /*
20 * Mask for patterns that should be excluded from the malware scan.
21 */
22 const EXCLUSION_PATTERNS_MALWARE = 0x4;
23
24 //serialized:
25 protected $path = '';
26 protected $results = array();
27 public $errorMsg = false;
28 protected $apiKey = false;
29 protected $wordpressVersion = '';
30 protected $totalFilesScanned = 0;
31 protected $startTime = false;
32 protected $lastStatusTime = false;
33 protected $patterns = "";
34 protected $api = false;
35 protected static $excludePatterns = array();
36 protected static $builtinExclusions = array(
37 array('pattern' => 'wp\-includes\/version\.php', 'include' => self::EXCLUSION_PATTERNS_KNOWN_FILES), //Excluded from the known files scan because non-en_US installations will have extra content that fails the check, still in malware scan
38 array('pattern' => '(?:wp\-includes|wp\-admin)\/(?:[^\/]+\/+)*(?:\.htaccess|\.htpasswd|php_errorlog|error_log|[^\/]+?\.log|\._|\.DS_Store|\.listing|dwsync\.xml)', 'include' => self::EXCLUSION_PATTERNS_KNOWN_FILES),
39 );
40 /** @var wfScanEngine */
41 protected $scanEngine;
42
43 public function __sleep(){
44 return array('path', 'results', 'errorMsg', 'apiKey', 'wordpressVersion', 'urlHoover', 'totalFilesScanned',
45 'startTime', 'lastStatusTime', 'patterns', 'scanEngine');
46 }
47 public function __wakeup(){
48 }
49 public function __construct($apiKey, $wordpressVersion, $path){
50 $this->apiKey = $apiKey;
51 $this->wordpressVersion = $wordpressVersion;
52 $this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
53 if($path[strlen($path) - 1] != '/'){
54 $path .= '/';
55 }
56 $this->path = $path;
57
58
59 $this->results = array();
60 $this->errorMsg = false;
61 //First extract hosts or IP's and their URL's into $this->hostsFound and URL's into $this->urlsFound
62 $this->urlHoover = new wordfenceURLHoover($this->apiKey, $this->wordpressVersion);
63 $this->setupSigs();
64 }
65
66 /**
67 * Get scan regexes from noc1 and add any user defined regexes, including descriptions, ID's and time added.
68 * @todo add caching to this.
69 * @throws Exception
70 */
71 protected function setupSigs() {
72 $this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
73 $sigData = $this->api->call('get_patterns', array(), array());
74 if(! (is_array($sigData) && isset($sigData['rules'])) ){
75 throw new Exception("Wordfence could not get the attack signature patterns from the scanning server.");
76 }
77
78 if (wfWAF::getInstance() && method_exists(wfWAF::getInstance(), 'setMalwareSignatures')) {
79 try { wfWAF::getInstance()->setMalwareSignatures(array()); } catch (Exception $e) { /* Ignore */ }
80 }
81
82 if (is_array($sigData['rules'])) {
83 $wafPatterns = array();
84 foreach ($sigData['rules'] as $key => $signatureRow) {
85 list(, , $pattern) = $signatureRow;
86 $logOnly = (isset($signatureRow[5]) && !empty($signatureRow[5])) ? $signatureRow[5] : false;
87 if (@preg_match('/' . $pattern . '/i', null) === false) {
88 wordfence::status(1, 'error', "A regex Wordfence received from it's servers is invalid. The pattern is: " . esc_html($pattern));
89 unset($sigData['rules'][$key]);
90 }
91 else if (!$logOnly) {
92 $wafPatterns[] = $pattern;
93 }
94 }
95
96 if (wfWAF::getInstance() && method_exists(wfWAF::getInstance(), 'setMalwareSignatures')) {
97 try { wfWAF::getInstance()->setMalwareSignatures($wafPatterns); } catch (Exception $e) { /* Ignore */ }
98 }
99 }
100
101 $extra = wfConfig::get('scan_include_extra');
102 if (!empty($extra)) {
103 $regexs = explode("\n", $extra);
104 $id = 1000001;
105 foreach($regexs as $r){
106 $r = rtrim($r, "\r");
107 try {
108 preg_match('/' . $r . '/i', "");
109 } catch(Exception $e){
110 throw new Exception("The following user defined scan pattern has an error: $r");
111 }
112 $sigData['rules'][] = array($id++, time(), $r, "User defined scan pattern");
113 }
114 }
115
116 $this->patterns = $sigData;
117 if (isset($this->patterns['signatureUpdateTime'])) {
118 wfConfig::set('signatureUpdateTime', $this->patterns['signatureUpdateTime']);
119 }
120 }
121
122 /**
123 * Return regular expression to exclude files or false if
124 * there is no pattern
125 *
126 * @param $whichPatterns int Bitmask indicating which patterns to include.
127 * @return string|boolean
128 */
129 public static function getExcludeFilePattern($whichPatterns = self::EXCLUSION_PATTERNS_USER) {
130 if (isset(self::$excludePatterns[$whichPatterns])) {
131 return self::$excludePatterns[$whichPatterns];
132 }
133
134 $exParts = array();
135 if (($whichPatterns & self::EXCLUSION_PATTERNS_USER) > 0)
136 {
137 if (wfConfig::get('scan_exclude', false))
138 {
139 $exParts = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('scan_exclude')));
140 }
141 }
142
143 foreach ($exParts as &$exPart) {
144 $exPart = preg_quote(trim($exPart), '/');
145 $exPart = preg_replace('/\\\\\*/', '.*', $exPart);
146 }
147
148 foreach (self::$builtinExclusions as $pattern) {
149 if (($pattern['include'] & $whichPatterns) > 0) {
150 $exParts[] = $pattern['pattern'];
151 }
152 }
153
154 if (!empty($exParts)) {
155 //self::$excludePattern = '/^(?:' . implode('|', array_filter($exParts)) . ')$/i';
156 self::$excludePatterns[$whichPatterns] = '/(?:' . implode('|', array_filter($exParts)) . ')$/i';
157 }
158 else {
159 self::$excludePatterns[$whichPatterns]= false;
160 }
161
162 return self::$excludePatterns[$whichPatterns];
163 }
164
165 /**
166 * @param wfScanEngine $forkObj
167 * @return array
168 */
169 public function scan($forkObj){
170 $this->scanEngine = $forkObj;
171 $loader = $this->scanEngine->getKnownFilesLoader();
172 if(! $this->startTime){
173 $this->startTime = microtime(true);
174 }
175 if(! $this->lastStatusTime){
176 $this->lastStatusTime = microtime(true);
177 }
178 $db = new wfDB();
179 $lastCount = 'whatever';
180 $excludePattern = self::getExcludeFilePattern(self::EXCLUSION_PATTERNS_USER | self::EXCLUSION_PATTERNS_MALWARE);
181 while(true){
182 $thisCount = $db->querySingle("select count(*) from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0");
183 if($thisCount == $lastCount){
184 //count should always be decreasing. If not, we're in an infinite loop so lets catch it early
185 break;
186 }
187 $lastCount = $thisCount;
188 $res1 = $db->querySelect("select filename, filenameMD5, hex(newMD5) as newMD5 from " . $db->prefix() . "wfFileMods where oldMD5 != newMD5 and knownFile=0 limit 500");
189 if(sizeof($res1) < 1){
190 break;
191 }
192 foreach($res1 as $rec1){
193 $db->queryWrite("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.
194 $file = $rec1['filename'];
195 if($excludePattern && preg_match($excludePattern, $file)){
196 continue;
197 }
198 if (!file_exists($this->path . $file)) {
199 continue;
200 }
201 $fileSum = $rec1['newMD5'];
202
203 $fileExt = '';
204 if(preg_match('/\.([a-zA-Z\d\-]{1,7})$/', $file, $matches)){
205 $fileExt = strtolower($matches[1]);
206 }
207 $isPHP = false;
208 if(preg_match('/\.(?:php(?:\d+)?|phtml)(\.|$)/i', $file)) {
209 $isPHP = true;
210 }
211 $isHTML = false;
212 if(preg_match('/\.(?:html?)(\.|$)/i', $file)) {
213 $isHTML = true;
214 }
215 $isJS = false;
216 if(preg_match('/\.(?:js)(\.|$)/i', $file)) {
217 $isJS = true;
218 }
219 $dontScanForURLs = false;
220 if( (! wfConfig::get('scansEnabled_highSense')) && (preg_match('/^(?:\.htaccess|wp\-config\.php)$/', $file) || $file === ini_get('user_ini.filename'))) {
221 $dontScanForURLs = true;
222 }
223
224 $isScanImagesFile = false;
225 if (!$isPHP && preg_match('/^(?:jpg|jpeg|mp3|avi|m4v|mov|mp4|gif|png|tiff?|svg|sql|js|tbz2?|bz2?|xz|zip|tgz|gz|tar|log|err\d+)$/', $fileExt)) {
226 if (wfConfig::get('scansEnabled_scanImages')) {
227 $isScanImagesFile = true;
228 }
229 else if (!$isJS) {
230 continue;
231 }
232 }
233 $isHighSensitivityFile = false;
234 if (strtolower($fileExt) == 'sql') {
235 if (wfConfig::get('scansEnabled_highSense')) {
236 $isHighSensitivityFile = true;
237 }
238 else {
239 continue;
240 }
241 }
242 if(wfUtils::fileTooBig($this->path . $file)){ //We can't use filesize on 32 bit systems for files > 2 gigs
243 //We should not need this check because files > 2 gigs are not hashed and therefore won't be received back as unknowns from the API server
244 //But we do it anyway to be safe.
245 wordfence::status(2, 'error', "Encountered file that is too large: $file - Skipping.");
246 continue;
247 }
248 wfUtils::beginProcessingFile($file);
249
250 $fsize = @filesize($this->path . $file); //Checked if too big above
251 if($fsize > 1000000){
252 $fsize = sprintf('%.2f', ($fsize / 1000000)) . "M";
253 } else {
254 $fsize = $fsize . "B";
255 }
256 if (function_exists('memory_get_usage')) {
257 wordfence::status(4, 'info', "Scanning contents: $file (Size:$fsize Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
258 } else {
259 wordfence::status(4, 'info', "Scanning contents: $file (Size: $fsize)");
260 }
261
262 $stime = microtime(true);
263 $fh = @fopen($this->path . $file, 'r');
264 if(! $fh){
265 continue;
266 }
267 $totalRead = 0;
268
269 $dataForFile = $this->dataForFile($file);
270
271 while(! feof($fh)){
272 $data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk
273 $totalRead += strlen($data);
274 if($totalRead < 1){
275 break;
276 }
277
278 $extraMsg = '';
279 if ($isScanImagesFile) {
280 $extraMsg = ' This file was detected because you have enabled "Scan images, binary, and other files as if they were executable", which treats non-PHP files as if they were PHP code. This option is more aggressive than the usual scans, and may cause false positives.';
281 }
282 else if ($isHighSensitivityFile) {
283 $extraMsg = ' This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.';
284 }
285
286 $treatAsBinary = ($isPHP || $isHTML || wfConfig::get('scansEnabled_scanImages'));
287 if ($treatAsBinary && strpos($data, '$allowed'.'Sites') !== false && strpos($data, "define ('VER"."SION', '1.") !== false && strpos($data, "TimThum"."b script created by") !== false) {
288 if(! $this->isSafeFile($this->path . $file)){
289 $this->addResult(array(
290 'type' => 'file',
291 'severity' => 1,
292 'ignoreP' => $this->path . $file,
293 'ignoreC' => $fileSum,
294 'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
295 'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it." . $extraMsg,
296 'data' => array_merge(array(
297 'file' => $file,
298 ), $dataForFile),
299 ));
300 break;
301 }
302 }
303 else {
304 $regexMatched = false;
305 foreach ($this->patterns['rules'] as $rule) {
306 $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
307 $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
308 if ($type == 'server' && !$treatAsBinary) { continue; }
309 else if (($type == 'both' || $type == 'browser') && $fileExt == 'js') { $extraMsg = ''; }
310 else if (($type == 'both' || $type == 'browser') && !$treatAsBinary) { continue; }
311
312 if (preg_match('/(' . $rule[2] . ')/i', $data, $matches, PREG_OFFSET_CAPTURE)) {
313 if (!$this->isSafeFile($this->path . $file)) {
314 $matchString = $matches[1][0];
315 $matchOffset = $matches[1][1];
316 $beforeString = substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
317 $afterString = substr($data, $matchOffset + strlen($matchString), 100);
318 if (!$logOnly) {
319 $this->addResult(array(
320 'type' => 'file',
321 'severity' => 1,
322 'ignoreP' => $this->path . $file,
323 'ignoreC' => $fileSum,
324 'shortMsg' => "File appears to be malicious: " . esc_html($file),
325 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\">\"" . wfUtils::potentialBinaryStringToHTML((strlen($matchString) > 200 ? substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>.' . $extraMsg,
326 'data' => array_merge(array(
327 'file' => $file,
328 ), $dataForFile),
329 ));
330 }
331 $regexMatched = true;
332 $this->scanEngine->recordMetric('malwareSignature', $rule[0], array('file' => $file, 'match' => $matchString, 'before' => $beforeString, 'after' => $afterString), false);
333 break;
334 }
335 }
336 }
337 if ($regexMatched) { break; }
338 }
339 if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
340 $badStringFound = false;
341 if (strpos($data, $this->patterns['badstrings'][0]) !== false) {
342 for ($i = 1; $i < sizeof($this->patterns['badstrings']); $i++) {
343 if (strpos($data, $this->patterns['badstrings'][$i]) !== false) {
344 $badStringFound = $this->patterns['badstrings'][$i];
345 break;
346 }
347 }
348 }
349 if ($badStringFound) {
350 if (!$this->isSafeFile($this->path . $file)) {
351 $this->addResult(array(
352 'type' => 'file',
353 'severity' => 1,
354 'ignoreP' => $this->path . $file,
355 'ignoreC' => $fileSum,
356 'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
357 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '" . esc_html($badStringFound) . "' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans. This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.",
358 'data' => array_merge(array(
359 'file' => $file,
360 ), $dataForFile),
361 ));
362 break;
363 }
364 }
365 }
366
367 if (!$dontScanForURLs) {
368 $this->urlHoover->hoover($file, $data);
369 }
370
371 if($totalRead > 2 * 1024 * 1024){
372 break;
373 }
374 }
375 fclose($fh);
376 $this->totalFilesScanned++;
377 if(microtime(true) - $this->lastStatusTime > 1){
378 $this->lastStatusTime = microtime(true);
379 $this->writeScanningStatus();
380 }
381 $forkObj->forkIfNeeded();
382 }
383 }
384 $this->writeScanningStatus();
385 wordfence::status(2, 'info', "Asking Wordfence to check URL's against malware list.");
386 $hooverResults = $this->urlHoover->getBaddies();
387 if($this->urlHoover->errorMsg){
388 $this->errorMsg = $this->urlHoover->errorMsg;
389 return false;
390 }
391 $this->urlHoover->cleanup();
392 $siteURL = get_site_url();
393 $siteHost = parse_url($siteURL, PHP_URL_HOST);
394 foreach($hooverResults as $file => $hresults){
395 $dataForFile = $this->dataForFile($file, $this->path . $file);
396
397 foreach($hresults as $result){
398 if(preg_match('/wfBrowscapCache\.php$/', $file)){
399 continue;
400 }
401
402 if (empty($result['URL'])) {
403 continue;
404 }
405 $url = $result['URL'];
406 $urlHost = parse_url($url, PHP_URL_HOST);
407 if (strcasecmp($siteHost, $urlHost) === 0) {
408 continue;
409 }
410
411 if($result['badList'] == 'goog-malware-shavar'){
412 if(! $this->isSafeFile($this->path . $file)){
413 $this->addResult(array(
414 'type' => 'file',
415 'severity' => 1,
416 'ignoreP' => $this->path . $file,
417 'ignoreC' => md5_file($this->path . $file),
418 'shortMsg' => "File contains suspected malware URL: " . esc_html($this->path . $file),
419 'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . esc_html($this->patterns['word3']) . " when scanning files so the URL may not be visible if you view this file. The URL is: " . esc_html($result['URL']) . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
420 'data' => array_merge(array(
421 'file' => $file,
422 'badURL' => $result['URL'],
423 'gsb' => 'goog-malware-shavar'
424 ), $dataForFile),
425 ));
426 }
427 } else if($result['badList'] == 'googpub-phish-shavar'){
428 if(! $this->isSafeFile($this->path . $file)){
429 $this->addResult(array(
430 'type' => 'file',
431 'severity' => 1,
432 'ignoreP' => $this->path . $file,
433 'ignoreC' => md5_file($this->path . $file),
434 'shortMsg' => "File contains suspected phishing URL: " . esc_html($this->path . $file),
435 'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . esc_html($result['URL']),
436 'data' => array_merge(array(
437 'file' => $file,
438 'badURL' => $result['URL'],
439 'gsb' => 'googpub-phish-shavar'
440 ), $dataForFile),
441 ));
442 }
443 }
444 }
445 }
446 wfUtils::endProcessingFile();
447
448 return $this->results;
449 }
450
451 protected function writeScanningStatus() {
452 wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " additional files at " . sprintf('%.2f', ($this->totalFilesScanned / (microtime(true) - $this->startTime))) . " per second");
453 }
454
455 protected function addResult($result) {
456 for ($i = 0; $i < sizeof($this->results); $i++) {
457 if ($this->results[$i]['type'] == 'file' && $this->results[$i]['data']['file'] == $result['data']['file']) {
458 if ($this->results[$i]['severity'] > $result['severity']) {
459 $this->results[$i] = $result; //Overwrite with more severe results
460 }
461 return;
462 }
463 }
464 //We don't have a results for this file so append
465 $this->results[] = $result;
466 }
467 private function isSafeFile($file){
468 if(! $this->api){
469 $this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
470 }
471
472 $wfHash = wordfenceHash::wfHash($file);
473 $result = $this->api->call('is_safe_file', array(), array('shac' => strtoupper($wfHash[1])));
474 if(isset($result['isSafe']) && $result['isSafe'] == 1){
475 return true;
476 }
477 return false;
478 }
479
480 /**
481 * @param string $file
482 * @return array
483 */
484 private function dataForFile($file, $fullPath = null) {
485 $loader = $this->scanEngine->getKnownFilesLoader();
486 $data = array();
487 if ($isKnownFile = $loader->isKnownFile($file)) {
488 if ($loader->isKnownCoreFile($file)) {
489 $data['cType'] = 'core';
490
491 } else if ($loader->isKnownPluginFile($file)) {
492 $data['cType'] = 'plugin';
493 list($itemName, $itemVersion, $cKey) = $loader->getKnownPluginData($file);
494 $data = array_merge($data, array(
495 'cName' => $itemName,
496 'cVersion' => $itemVersion,
497 'cKey' => $cKey
498 ));
499
500 } else if ($loader->isKnownThemeFile($file)) {
501 $data['cType'] = 'theme';
502 list($itemName, $itemVersion, $cKey) = $loader->getKnownThemeData($file);
503 $data = array_merge($data, array(
504 'cName' => $itemName,
505 'cVersion' => $itemVersion,
506 'cKey' => $cKey
507 ));
508 }
509 }
510
511 $suppressDelete = false;
512 $canRegenerate = false;
513 if ($fullPath !== null) {
514 $bootstrapPath = wordfence::getWAFBootstrapPath();
515 $htaccessPath = get_home_path() . '.htaccess';
516 $userIni = ini_get('user_ini.filename');
517 $userIniPath = false;
518 if ($userIni) {
519 $userIniPath = get_home_path() . $userIni;
520 }
521
522 if ($fullPath == $htaccessPath) {
523 $suppressDelete = true;
524 }
525 else if ($userIniPath !== false && $fullPath == $userIniPath) {
526 $suppressDelete = true;
527 }
528 else if ($fullPath == $bootstrapPath) {
529 $suppressDelete = true;
530 $canRegenerate = true;
531 }
532 }
533
534 $data['canDiff'] = $isKnownFile;
535 $data['canFix'] = $isKnownFile;
536 $data['canDelete'] = !$isKnownFile && !$canRegenerate && !$suppressDelete;
537 $data['canRegenerate'] = $canRegenerate;
538
539 return $data;
540 }
541 }
542
543
544 ?>
545