Exemple #1
0
 protected function getURL($url, $postParams = array())
 {
     wordfence::status(4, 'info', "Calling Wordfence API v" . WORDFENCE_API_VERSION . ":" . $url);
     if (!function_exists('wp_remote_post')) {
         require_once ABSPATH . WPINC . 'http.php';
     }
     $ssl_verify = (bool) wfConfig::get('ssl_verify');
     $args = array('timeout' => 900, 'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'), 'body' => $postParams, 'sslverify' => $ssl_verify);
     if (!$ssl_verify) {
         // Some versions of cURL will complain that SSL verification is disabled but the CA bundle was supplied.
         $args['sslcertificates'] = false;
     }
     $response = wp_remote_post($url, $args);
     $this->lastHTTPStatus = (int) wp_remote_retrieve_response_code($response);
     if (is_wp_error($response)) {
         $error_message = $response->get_error_message();
         throw new Exception("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the the Wordfence scanning servers" . ($error_message ? ": {$error_message}" : '.'));
     }
     if (!empty($response['response']['code'])) {
         $this->lastHTTPStatus = (int) $response['response']['code'];
     }
     if (200 != $this->lastHTTPStatus) {
         throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [{$this->lastHTTPStatus}]");
     }
     $this->curlContent = wp_remote_retrieve_body($response);
     return $this->curlContent;
 }
Exemple #2
0
	protected function getURL($url, $postParams = array()){
		if(function_exists('curl_init')){
			$this->curlDataWritten = 0;
			$this->curlContent = "";
			$curl = curl_init($url);
			if(defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT') && wfUtils::hostNotExcludedFromProxy($url) ){
				curl_setopt($curl, CURLOPT_HTTPPROXYTUNNEL, 0);
				curl_setopt($curl, CURLOPT_PROXY, WP_PROXY_HOST . ':' . WP_PROXY_PORT);
				if(defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD')){
					curl_setopt($curl, CURLOPT_PROXYUSERPWD, WP_PROXY_USERNAME . ':' . WP_PROXY_PASSWORD);
				}
			}
			curl_setopt ($curl, CURLOPT_TIMEOUT, 900);
			curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]') );
			curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
			curl_setopt ($curl, CURLOPT_HEADER, 0);
			curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
			curl_setopt($curl, CURLOPT_POST, true);
			curl_setopt($curl, CURLOPT_POSTFIELDS, $postParams);
			wordfence::status(4, 'info', "CURL fetching URL: " . $url);
			curl_exec($curl);

			$httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
			$this->lastCurlErrorNo = curl_errno($curl);
			if($httpStatus == 200){
				curl_close($curl);
				return $this->curlContent;
			} else {
				$cerror = curl_error($curl);
				curl_close($curl);
				throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [$httpStatus] and the curl error number was [" . $this->lastCurlErrorNo . "] " . ($cerror ? (' and the error from CURL was: ' . $cerror) : ''));
			}
		} else {
			wordfence::status(4, 'info', "Fetching URL with file_get: " . $url);
			$data = $this->fileGet($url, $postParams);
			if($data === false){
				$err = error_get_last();
				if($err){
					throw new Exception("We received an error response when trying to contact the Wordfence scanning servers using PHP's file_get_contents function. The error was: " . var_export($err, true));
				} else {
					throw new Exception("We received an empty response when trying to contact the Wordfence scanning servers using PHP's file_get_contents function.");
				}
			}
			return $data;
		}

	}
Exemple #3
0
 protected function getURL($url, $postParams = array())
 {
     wordfence::status(4, 'info', "Calling Wordfence API v" . WORDFENCE_API_VERSION . ":" . $url);
     if (!function_exists('wp_remote_post')) {
         require_once ABSPATH . WPINC . 'http.php';
     }
     $response = wp_remote_post($url, array('timeout' => 900, 'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'), 'body' => $postParams));
     $this->lastHTTPStatus = (int) wp_remote_retrieve_response_code($response);
     if (is_wp_error($response)) {
         $error_message = $response->get_error_message();
         throw new Exception("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the the Wordfence scanning servers" . ($error_message ? ": {$error_message}" : '.'));
     }
     if (!empty($response['response']['code'])) {
         $this->lastHTTPStatus = (int) $response['response']['code'];
     }
     if (200 != $this->lastHTTPStatus) {
         throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [{$this->lastHTTPStatus}]");
     }
     $this->curlContent = wp_remote_retrieve_body($response);
     return $this->curlContent;
 }
Exemple #4
0
 public static function getIPsGeo($IPs)
 {
     //works with int or dotted. Outputs same format it receives.
     $IPs = array_unique($IPs);
     $toResolve = array();
     $db = new wfDB();
     global $wpdb;
     $locsTable = $wpdb->base_prefix . 'wfLocs';
     $IPLocs = array();
     foreach ($IPs as $IP) {
         $isBinaryIP = !self::isValidIP($IP);
         if ($isBinaryIP) {
             $ip_printable = wfUtils::inet_ntop($IP);
             $ip_bin = $IP;
         } else {
             $ip_printable = $IP;
             $ip_bin = wfUtils::inet_pton($IP);
         }
         $row = $db->querySingleRec("select IP, ctime, failed, city, region, countryName, countryCode, lat, lon, unix_timestamp() - ctime as age from " . $locsTable . " where IP=%s", $ip_bin);
         if ($row) {
             if ($row['age'] > WORDFENCE_MAX_IPLOC_AGE) {
                 $db->queryWrite("delete from " . $locsTable . " where IP=%s", $row['IP']);
             } else {
                 if ($row['failed'] == 1) {
                     $IPLocs[$ip_printable] = false;
                 } else {
                     $row['IP'] = self::inet_ntop($row['IP']);
                     $IPLocs[$ip_printable] = $row;
                 }
             }
         }
         if (!isset($IPLocs[$ip_printable])) {
             $toResolve[] = $ip_printable;
         }
     }
     if (sizeof($toResolve) > 0) {
         $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
         try {
             $freshIPs = $api->call('resolve_ips', array(), array('ips' => implode(',', $toResolve)));
             if (is_array($freshIPs)) {
                 foreach ($freshIPs as $IP => $value) {
                     $IP_bin = wfUtils::inet_pton($IP);
                     if ($value == 'failed') {
                         $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", $IP_bin);
                         $IPLocs[$IP] = false;
                     } else {
                         if (is_array($value)) {
                             for ($i = 0; $i <= 5; $i++) {
                                 //Prevent warnings in debug mode about uninitialized values
                                 if (!isset($value[$i])) {
                                     $value[$i] = '';
                                 }
                             }
                             $db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)", $IP_bin, $value[3], $value[2], $value[1], $value[0], $value[4], $value[5]);
                             $IPLocs[$IP] = array('IP' => $IP, 'city' => $value[3], 'region' => $value[2], 'countryName' => $value[1], 'countryCode' => $value[0], 'lat' => $value[4], 'lon' => $value[5]);
                         }
                     }
                 }
             }
         } catch (Exception $e) {
             wordfence::status(2, 'error', "Call to Wordfence API to resolve IPs failed: " . $e->getMessage());
             return array();
         }
     }
     return $IPLocs;
 }
 private function writeScanningStatus()
 {
     wordfence::status(2, 'info', "Scanned contents of " . $this->totalFilesScanned . " additional files at " . sprintf('%.2f', $this->totalFilesScanned / (microtime(true) - $this->startTime)) . " per second");
 }
Exemple #6
0
 public static function statusPrep()
 {
     wfConfig::set_ser('wfStatusStartMsgs', array());
     wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
 }
 private function takeBlockingAction($configVar, $reason)
 {
     if ($this->googleSafetyCheckOK()) {
         $action = wfConfig::get($configVar . '_action');
         if (!$action) {
             //error_log("Wordfence action missing for configVar: $configVar");
             return;
         }
         $secsToGo = 0;
         if ($action == 'block') {
             $IP = wfUtils::getIP();
             $this->blockIP($IP, $reason);
             $secsToGo = wfConfig::get('blockedTime');
             //Moved the following code AFTER the block to prevent multiple emails.
             if (wfConfig::get('alertOn_block')) {
                 wordfence::alert("Blocking IP {$IP}", "Wordfence has blocked IP address {$IP}.\nThe reason is: \"{$reason}\".", $IP);
             }
             wordfence::status(2, 'info', "Blocking IP {$IP}. {$reason}");
         } else {
             if ($action == 'throttle') {
                 $IP = wfUtils::getIP();
                 $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_pton($IP), $reason, $reason);
                 wordfence::status(2, 'info', "Throttling IP {$IP}. {$reason}");
                 wfConfig::inc('totalIPsThrottled');
                 $secsToGo = 60;
             }
         }
         $this->do503($secsToGo, $reason);
     } else {
         return;
     }
 }
 private function processFile($realFile)
 {
     $file = substr($realFile, $this->striplen);
     if (!$this->stoppedOnFile && microtime(true) - $this->startTime > $this->engine->maxExecTime) {
         //max X seconds but don't allow fork if we're looking for the file we stopped on. Search mode is VERY fast.
         $this->stoppedOnFile = $file;
         wordfence::status(4, 'info', "Calling fork() from wordfenceHash::processFile with maxExecTime: " . $this->engine->maxExecTime);
         $this->engine->fork();
         //exits
     }
     //Put this after the fork, that way we will at least scan one more file after we fork if it takes us more than 10 seconds to search for the stoppedOnFile
     if ($this->stoppedOnFile && $file != $this->stoppedOnFile) {
         return;
     } else {
         if ($this->stoppedOnFile && $file == $this->stoppedOnFile) {
             $this->stoppedOnFile = false;
             //Continue scanning
         }
     }
     if (wfUtils::fileTooBig($realFile)) {
         wordfence::status(4, 'info', "Skipping file larger than max size: {$realFile}");
         return;
     }
     if (function_exists('memory_get_usage')) {
         wordfence::status(4, 'info', "Scanning: {$realFile} (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
     } else {
         wordfence::status(4, 'info', "Scanning: {$realFile}");
     }
     $wfHash = self::wfHash($realFile);
     if ($wfHash) {
         $md5 = strtoupper($wfHash[0]);
         $shac = strtoupper($wfHash[1]);
         $knownFile = 0;
         if ($this->malwareEnabled && $this->isMalwarePrefix($md5)) {
             $this->possibleMalware[] = array($file, $md5);
         }
         if (isset($this->knownFiles['core'][$file])) {
             if (strtoupper($this->knownFiles['core'][$file]) == $shac) {
                 $knownFile = 1;
             } else {
                 if ($this->coreEnabled) {
                     $localFile = ABSPATH . '/' . preg_replace('/^[\\.\\/]+/', '', $file);
                     $fileContents = @file_get_contents($localFile);
                     if ($fileContents && !preg_match('/<\\?' . 'php[\\r\\n\\s\\t]*\\/\\/[\\r\\n\\s\\t]*Silence is golden\\.[\\r\\n\\s\\t]*(?:\\?>)?[\\r\\n\\s\\t]*$/s', $fileContents)) {
                         //<?php
                         if (!$this->isSafeFile($shac)) {
                             $this->haveIssues['core'] = true;
                             $this->engine->addIssue('file', 1, 'coreModified' . $file . $md5, 'coreModified' . $file, 'WordPress core file modified: ' . $file, "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.", array('file' => $file, 'cType' => 'core', 'canDiff' => true, 'canFix' => true, 'canDelete' => false));
                         }
                     }
                 }
             }
         } else {
             if (isset($this->knownFiles['plugins'][$file])) {
                 if (in_array($shac, $this->knownFiles['plugins'][$file])) {
                     $knownFile = 1;
                 } else {
                     if ($this->pluginsEnabled) {
                         if (!$this->isSafeFile($shac)) {
                             $itemName = $this->knownFiles['plugins'][$file][0];
                             $itemVersion = $this->knownFiles['plugins'][$file][1];
                             $cKey = $this->knownFiles['plugins'][$file][2];
                             $this->haveIssues['plugins'] = true;
                             $this->engine->addIssue('file', 2, 'modifiedplugin' . $file . $md5, 'modifiedplugin' . $file, 'Modified plugin file: ' . $file, "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]", array('file' => $file, 'cType' => 'plugin', 'canDiff' => true, 'canFix' => true, 'canDelete' => false, 'cName' => $itemName, 'cVersion' => $itemVersion, 'cKey' => $cKey));
                         }
                     }
                 }
             } else {
                 if (isset($this->knownFiles['themes'][$file])) {
                     if (in_array($shac, $this->knownFiles['themes'][$file])) {
                         $knownFile = 1;
                     } else {
                         if ($this->themesEnabled) {
                             if (!$this->isSafeFile($shac)) {
                                 $itemName = $this->knownFiles['themes'][$file][0];
                                 $itemVersion = $this->knownFiles['themes'][$file][1];
                                 $cKey = $this->knownFiles['themes'][$file][2];
                                 $this->haveIssues['themes'] = true;
                                 $this->engine->addIssue('file', 2, 'modifiedtheme' . $file . $md5, 'modifiedtheme' . $file, 'Modified theme file: ' . $file, "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.", array('file' => $file, 'cType' => 'theme', 'canDiff' => true, 'canFix' => true, 'canDelete' => false, 'cName' => $itemName, 'cVersion' => $itemVersion, 'cKey' => $cKey));
                             }
                         }
                     }
                 }
             }
         }
         // knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
         // 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.
         // 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.
         $this->db->queryWrite("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);
         //Now that we know we can open the file, lets update stats
         if (preg_match('/\\.(?:js|html|htm|css)$/i', $realFile)) {
             $this->linesOfJCH += sizeof(file($realFile));
         } else {
             if (preg_match('/\\.php$/i', $realFile)) {
                 $this->linesOfPHP += sizeof(file($realFile));
             }
         }
         $this->totalFiles++;
         $this->totalData += filesize($realFile);
         //We already checked if file overflows int in the fileTooBig routine above
         if ($this->totalFiles % 100 === 0) {
             wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data so far");
         }
     } else {
         //wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $realFile");
     }
 }
 public static function set_ser($key, $val, $canUseDisk = false)
 {
     //We serialize some very big values so this is memory efficient. We don't make any copies of $val and don't use ON DUPLICATE KEY UPDATE
     // because we would have to concatenate $val twice into the query which could also exceed max packet for the mysql server
     $serialized = serialize($val);
     $val = '';
     $tempFilename = 'wordfence_tmpfile_' . $key . '.php';
     if (strlen($serialized) * 1.1 > self::getDB()->getMaxAllowedPacketBytes()) {
         //If it's greater than max_allowed_packet + 10% for escaping and SQL
         if ($canUseDisk) {
             $dir = self::getTempDir();
             $potentialDirs = self::getPotentialTempDirs();
             if ($dir) {
                 $fh = false;
                 $fullFile = $dir . $tempFilename;
                 self::deleteOldTempFile($fullFile);
                 $fh = fopen($fullFile, 'w');
                 if ($fh) {
                     wordfence::status(4, 'info', "Serialized data for {$key} is " . strlen($serialized) . " bytes and is greater than max_allowed packet so writing it to disk file: " . $fullFile);
                 } else {
                     wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $potentialDirs));
                     return false;
                 }
                 fwrite($fh, self::$tmpFileHeader);
                 fwrite($fh, $serialized);
                 fclose($fh);
                 return true;
             } else {
                 wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $potentialDirs));
                 return false;
             }
         } else {
             wordfence::status(1, 'error', "Wordfence tried to save a variable with name '{$key}' and your database max_allowed_packet is set to be too small. This particular variable can't be saved to disk. Please ask your administrator to increase max_allowed_packet. Thanks.");
             return false;
         }
     } else {
         //Delete temp files on disk or else the DB will be written to but get_ser will see files on disk and read them instead
         $tempDir = self::getTempDir();
         if ($tempDir) {
             self::deleteOldTempFile($tempDir . $tempFilename);
         }
         $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
         if ($exists) {
             self::getDB()->queryWrite("update " . self::table() . " set val=%s where name=%s", $serialized, $key);
         } else {
             self::getDB()->queryWrite("insert IGNORE into " . self::table() . " (name, val) values (%s, %s)", $key, $serialized);
         }
     }
     self::getDB()->flush();
     return true;
 }
Exemple #10
0
 public static function getMaxExecutionTime()
 {
     $config = wfConfig::get('maxExecutionTime');
     wordfence::status(4, 'info', "Got value from wf config maxExecutionTime: {$config}");
     if (is_numeric($config) && $config >= 10) {
         wordfence::status(4, 'info', "getMaxExecutionTime() returning config value: {$config}");
         return $config;
     }
     $ini = @ini_get('max_execution_time');
     wordfence::status(4, 'info', "Got max_execution_time value from ini: {$ini}");
     if (is_numeric($ini) && $ini >= 10) {
         $ini = floor($ini / 2);
         wordfence::status(4, 'info', "getMaxExecutionTime() returning half ini value: {$ini}");
         return $ini;
     }
     wordfence::status(4, 'info', "getMaxExecutionTime() returning default of: 15");
     return 15;
 }
 private function processFile($realFile)
 {
     $file = substr($realFile, $this->striplen);
     if (wfUtils::fileTooBig($realFile)) {
         wordfence::status(4, 'info', "Skipping file larger than max size: {$realFile}");
         return;
     }
     if (function_exists('memory_get_usage')) {
         wordfence::status(4, 'info', "Scanning: {$realFile} (Mem:" . sprintf('%.1f', memory_get_usage(true) / (1024 * 1024)) . "M)");
     } else {
         wordfence::status(4, 'info', "Scanning: {$realFile}");
     }
     wfUtils::beginProcessingFile($file);
     $wfHash = self::wfHash($realFile);
     if ($wfHash) {
         $md5 = strtoupper($wfHash[0]);
         $shac = strtoupper($wfHash[1]);
         $knownFile = 0;
         if ($this->malwareEnabled && $this->isMalwarePrefix($md5)) {
             $this->possibleMalware[] = array($file, $md5);
         }
         $knownFileExclude = wordfenceScanner::getExcludeFilePattern(wordfenceScanner::EXCLUSION_PATTERNS_KNOWN_FILES);
         $allowKnownFileScan = true;
         if ($knownFileExclude) {
             $allowKnownFileScan = !preg_match($knownFileExclude, $realFile);
         }
         if ($allowKnownFileScan) {
             if ($this->coreUnknownEnabled && !$this->alertedOnUnknownWordPressVersion && empty($this->knownFiles['core'])) {
                 require ABSPATH . 'wp-includes/version.php';
                 //defines $wp_version
                 $this->alertedOnUnknownWordPressVersion = true;
                 $this->haveIssues['coreUnknown'] = true;
                 $this->engine->addIssue('coreUnknown', 2, 'coreUnknown' . $wp_version, 'coreUnknown' . $wp_version, 'Unknown WordPress core version: ' . $wp_version, "The core files scan will not be run because this version of WordPress is not currently indexed by Wordfence. This may be due to using a prerelease version or because the servers are still indexing a new release. If you are using an official WordPress release, this issue will automatically dismiss once the version is indexed and another scan is run.", array());
             }
             if (isset($this->knownFiles['core'][$file])) {
                 if (strtoupper($this->knownFiles['core'][$file]) == $shac) {
                     $knownFile = 1;
                 } else {
                     if ($this->coreEnabled) {
                         $localFile = ABSPATH . '/' . preg_replace('/^[\\.\\/]+/', '', $file);
                         $fileContents = @file_get_contents($localFile);
                         if ($fileContents && !preg_match('/<\\?' . 'php[\\r\\n\\s\\t]*\\/\\/[\\r\\n\\s\\t]*Silence is golden\\.[\\r\\n\\s\\t]*(?:\\?>)?[\\r\\n\\s\\t]*$/s', $fileContents)) {
                             //<?php
                             if (!$this->isSafeFile($shac)) {
                                 $this->haveIssues['core'] = true;
                                 $this->engine->addIssue('knownfile', 1, 'coreModified' . $file . $md5, 'coreModified' . $file, 'WordPress core file modified: ' . $file, "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.", array('file' => $file, 'cType' => 'core', 'canDiff' => true, 'canFix' => true, 'canDelete' => false));
                             }
                         }
                     }
                 }
             } else {
                 if (isset($this->knownFiles['plugins'][$file])) {
                     if (in_array($shac, $this->knownFiles['plugins'][$file])) {
                         $knownFile = 1;
                     } else {
                         if ($this->pluginsEnabled) {
                             if (!$this->isSafeFile($shac)) {
                                 $itemName = $this->knownFiles['plugins'][$file][0];
                                 $itemVersion = $this->knownFiles['plugins'][$file][1];
                                 $cKey = $this->knownFiles['plugins'][$file][2];
                                 $this->haveIssues['plugins'] = true;
                                 $this->engine->addIssue('knownfile', 2, 'modifiedplugin' . $file . $md5, 'modifiedplugin' . $file, 'Modified plugin file: ' . $file, "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]", array('file' => $file, 'cType' => 'plugin', 'canDiff' => true, 'canFix' => true, 'canDelete' => false, 'cName' => $itemName, 'cVersion' => $itemVersion, 'cKey' => $cKey));
                             }
                         }
                     }
                 } else {
                     if (isset($this->knownFiles['themes'][$file])) {
                         if (in_array($shac, $this->knownFiles['themes'][$file])) {
                             $knownFile = 1;
                         } else {
                             if ($this->themesEnabled) {
                                 if (!$this->isSafeFile($shac)) {
                                     $itemName = $this->knownFiles['themes'][$file][0];
                                     $itemVersion = $this->knownFiles['themes'][$file][1];
                                     $cKey = $this->knownFiles['themes'][$file][2];
                                     $this->haveIssues['themes'] = true;
                                     $this->engine->addIssue('knownfile', 2, 'modifiedtheme' . $file . $md5, 'modifiedtheme' . $file, 'Modified theme file: ' . $file, "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.", array('file' => $file, 'cType' => 'theme', 'canDiff' => true, 'canFix' => true, 'canDelete' => false, 'cName' => $itemName, 'cVersion' => $itemVersion, 'cKey' => $cKey));
                                 }
                             }
                         }
                     } else {
                         if ($this->coreUnknownEnabled && !$this->alertedOnUnknownWordPressVersion) {
                             //Check for unknown files in system directories
                             $restrictedWordPressFolders = array(ABSPATH . 'wp-admin/', ABSPATH . WPINC . '/');
                             foreach ($restrictedWordPressFolders as $path) {
                                 if (strpos($realFile, $path) === 0) {
                                     $this->haveIssues['coreUnknown'] = true;
                                     $this->engine->addIssue('knownfile', 2, 'coreUnknown' . $file . $md5, 'coreUnknown' . $file, 'Unknown file in WordPress core: ' . $file, "This file is in a WordPress core location but is not distributed with this version of WordPress. This is usually due to it being left over from a previous WordPress update, but it may also have been added by another plugin or a malicious file added by an attacker.", array('file' => $file, 'cType' => 'core', 'canDiff' => false, 'canFix' => false, 'canDelete' => true));
                                 }
                             }
                         }
                     }
                 }
             }
         }
         // knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
         // 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.
         // 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.
         $this->db->queryWrite("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);
         $this->totalFiles++;
         $this->totalData += @filesize($realFile);
         //We already checked if file overflows int in the fileTooBig routine above
         if ($this->totalFiles % 100 === 0) {
             wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data so far");
         }
     } else {
         //wordfence::status(2, 'error', "Could not gen hash for file (probably because we don't have permission to access the file): $realFile");
     }
     wfUtils::endProcessingFile();
 }
 public function getBaddies()
 {
     $allHostKeys = array();
     $stime = microtime(true);
     $allHostKeys = array();
     if ($this->useDB) {
         $q1 = $this->db->querySelect("select distinct hostKey as hostKey from {$this->table}");
         foreach ($q1 as $hRec) {
             $allHostKeys[] = $hRec['hostKey'];
         }
     } else {
         $allHostKeys = $this->hostKeys;
     }
     //Now call API and check if any hostkeys are bad.
     //This is a shortcut, because if no hostkeys are bad it saves us having to check URLs
     if (sizeof($allHostKeys) > 0) {
         //If we don't have any hostkeys, then we won't have any URL's to check either.
         //Hostkeys are 4 byte sha256 prefixes
         //Returned value is 2 byte shorts which are array indexes for bad keys that were passed in the original list
         $this->dbg("Checking " . sizeof($allHostKeys) . " hostkeys");
         if ($this->debug) {
             foreach ($allHostKeys as $key) {
                 $this->dbg("Checking hostkey: " . bin2hex($key));
             }
         }
         wordfence::status(2, 'info', "Checking " . sizeof($allHostKeys) . " host keys against Wordfence scanning servers.");
         $resp = $this->api->binCall('check_host_keys', implode('', $allHostKeys));
         wordfence::status(2, 'info', "Done host key check.");
         $this->dbg("Done hostkey check");
         $badHostKeys = array();
         if ($resp['code'] == 200) {
             if (strlen($resp['data']) > 0) {
                 $dataLen = strlen($resp['data']);
                 if ($dataLen % 2 != 0) {
                     $this->errorMsg = "Invalid data length received from Wordfence server: " . $dataLen;
                     return false;
                 }
                 for ($i = 0; $i < $dataLen; $i += 2) {
                     $idxArr = unpack('n', substr($resp['data'], $i, 2));
                     $idx = $idxArr[1];
                     if (isset($allHostKeys[$idx])) {
                         $badHostKeys[] = $allHostKeys[$idx];
                         $this->dbg("Got bad hostkey for record: " . var_export($allHostKeys[$idx], true));
                     } else {
                         $this->dbg("Bad allHostKeys index: {$idx}");
                         $this->errorMsg = "Bad allHostKeys index: {$idx}";
                         return false;
                     }
                 }
             }
         } else {
             $this->errorMsg = "Wordfence server responded with an error. HTTP code " . $resp['code'] . " and data: " . $resp['data'];
             return false;
         }
         if (sizeof($badHostKeys) > 0) {
             $urlsToCheck = array();
             $totalURLs = 0;
             //need to figure out which id's have bad hostkeys
             //need to feed in all URL's from those id's where the hostkey matches a URL
             foreach ($badHostKeys as $badHostKey) {
                 if ($this->useDB) {
                     //Putting a 10000 limit in here for sites that have a huge number of items with the same URL that repeats.
                     // This is an edge case. But if the URLs are malicious then presumably the admin will fix the malicious URLs
                     // and on subsequent scans the items (owners) that are above the 10000 limit will appear.
                     $q1 = $this->db->querySelect("select owner, host, path from {$this->table} where hostKey='%s' limit 10000", $badHostKey);
                     foreach ($q1 as $rec) {
                         $url = 'http://' . $rec['host'] . $rec['path'];
                         if (!isset($urlsToCheck[$rec['owner']])) {
                             $urlsToCheck[$rec['owner']] = array();
                         }
                         if (!in_array($url, $urlsToCheck[$rec['owner']])) {
                             $urlsToCheck[$rec['owner']][] = $url;
                             $totalURLs++;
                         }
                     }
                 } else {
                     foreach ($this->hostList as $rec) {
                         if ($rec['hostKey'] == $badHostKey) {
                             $url = 'http://' . $rec['host'] . $rec['path'];
                             if (!isset($urlsToCheck[$rec['owner']])) {
                                 $urlsToCheck[$rec['owner']] = array();
                             }
                             if (!in_array($url, $urlsToCheck[$rec['owner']])) {
                                 $urlsToCheck[$rec['owner']][] = $url;
                                 $totalURLs++;
                             }
                         }
                     }
                 }
             }
             if (sizeof($urlsToCheck) > 0) {
                 wordfence::status(2, 'info', "Checking " . $totalURLs . " URLs from " . sizeof($urlsToCheck) . " sources.");
                 $badURLs = $this->api->call('check_bad_urls', array(), array('toCheck' => json_encode($urlsToCheck)));
                 wordfence::status(2, 'info', "Done URL check.");
                 $this->dbg("Done URL check");
                 if (is_array($badURLs) && sizeof($badURLs) > 0) {
                     $finalResults = array();
                     foreach ($badURLs as $file => $badSiteList) {
                         if (!isset($finalResults[$file])) {
                             $finalResults[$file] = array();
                         }
                         foreach ($badSiteList as $badSite) {
                             $finalResults[$file][] = array('URL' => $badSite[0], 'badList' => $badSite[1]);
                         }
                     }
                     return $finalResults;
                 } else {
                     return array();
                 }
             } else {
                 return array();
             }
         } else {
             return array();
         }
     } else {
         return array();
     }
 }
 public static function set_ser($key, $val, $allowCompression = false)
 {
     /*
      * Because of the small default value for `max_allowed_packet` and `max_long_data_size`, we're stuck splitting
      * large values into multiple chunks. To minimize memory use, the MySQLi driver is used directly when possible.
      */
     global $wpdb;
     $dbh = $wpdb->dbh;
     self::delete_ser_chunked($key);
     //Ensure any old values for a chunked value are deleted first
     if (self::canCompressValue() && $allowCompression) {
         $data = gzencode(serialize($val));
     } else {
         $data = serialize($val);
     }
     if (!$wpdb->use_mysqli) {
         $data = bin2hex($data);
     }
     $dataLength = strlen($data);
     $chunkSize = intval((self::getDB()->getMaxAllowedPacketBytes() - 50) / 1.2);
     //Based on max_allowed_packet + 20% for escaping and SQL
     $chunkSize = $chunkSize - $chunkSize % 2;
     //Ensure it's even
     $chunkedValueKey = self::ser_chunked_key($key);
     if ($dataLength > $chunkSize) {
         $chunks = 0;
         while ($chunks * $chunkSize < $dataLength) {
             $dataChunk = substr($data, $chunks * $chunkSize, $chunkSize);
             if ($wpdb->use_mysqli) {
                 $chunkKey = $chunkedValueKey . $chunks;
                 $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (name, val) VALUES (?, ?)");
                 $null = NULL;
                 $stmt->bind_param("sb", $chunkKey, $null);
                 if (!$stmt->send_long_data(1, $dataChunk)) {
                     wordfence::status(2, 'error', "Error writing value chunk for {$key} (error: {$dbh->error})");
                     return false;
                 }
                 if (!$stmt->execute()) {
                     wordfence::status(2, 'error', "Error finishing writing value for {$key} (error: {$dbh->error})");
                     return false;
                 }
             } else {
                 if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val) values (%%s, X'%s')", $dataChunk), $chunkedValueKey . $chunks)) {
                     wordfence::status(2, 'error', "Error writing value chunk for {$key} (error: {$wpdb->last_error})");
                     return false;
                 }
             }
             $chunks++;
         }
         if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val) values (%%s, X'%s')", bin2hex(serialize(array('count' => $chunks)))), $chunkedValueKey . 'header')) {
             wordfence::status(2, 'error', "Error writing value header for {$key}");
             return false;
         }
     } else {
         $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
         if ($wpdb->use_mysqli) {
             if ($exists) {
                 $stmt = $dbh->prepare("UPDATE " . self::table() . " SET val=? WHERE name=?");
             } else {
                 $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (val, name) VALUES (?, ?)");
             }
             $null = NULL;
             $stmt->bind_param("bs", $null, $key);
             if (!$stmt->send_long_data(0, $data)) {
                 wordfence::status(2, 'error', "Error writing value chunk for {$key} (error: {$dbh->error})");
                 return false;
             }
             if (!$stmt->execute()) {
                 wordfence::status(2, 'error', "Error finishing writing value for {$key} (error: {$dbh->error})");
                 return false;
             }
         } else {
             if ($exists) {
                 self::getDB()->queryWrite(sprintf("update " . self::table() . " set val=X'%s' where name=%%s", $data), $key);
             } else {
                 self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val) values (%%s, X'%s')", $data), $key);
             }
         }
     }
     self::getDB()->flush();
     return true;
 }
Exemple #14
0
 private static function status($level, $type, $msg)
 {
     wordfence::status($level, $type, $msg);
 }