public static function alert($subject, $alertMsg, $IP) { wfConfig::inc('totalAlertsSent'); $emails = wfConfig::getAlertEmails(); if (sizeof($emails) < 1) { return; } $IPMsg = ""; if ($IP) { $IPMsg = "User IP: {$IP}\n"; $reverse = wfUtils::reverseLookup($IP); if ($reverse) { $IPMsg .= "User hostname: " . $reverse . "\n"; } $userLoc = wfUtils::getIPGeo($IP); if ($userLoc) { $IPMsg .= "User location: "; if ($userLoc['city']) { $IPMsg .= $userLoc['city'] . ', '; } $IPMsg .= $userLoc['countryName'] . "\n"; } } $content = wfUtils::tmpl('email_genericAlert.php', array('isPaid' => wfConfig::get('isPaid'), 'subject' => $subject, 'blogName' => get_bloginfo('name', 'raw'), 'adminURL' => get_admin_url(), 'alertMsg' => $alertMsg, 'IPMsg' => $IPMsg, 'date' => wfUtils::localHumanDate(), 'myHomeURL' => self::getMyHomeURL(), 'myOptionsURL' => self::getMyOptionsURL())); $shortSiteURL = preg_replace('/^https?:\\/\\//i', '', site_url()); $subject = "[Wordfence Alert] {$shortSiteURL} " . $subject; $sendMax = wfConfig::get('alert_maxHourly', 0); if ($sendMax > 0) { $sendArr = wfConfig::get_ser('alertFreqTrack', array()); if (!is_array($sendArr)) { $sendArr = array(); } $minuteTime = floor(time() / 60); $totalSent = 0; for ($i = $minuteTime; $i > $minuteTime - 60; $i--) { $totalSent += isset($sendArr[$i]) ? $sendArr[$i] : 0; } if ($totalSent >= $sendMax) { return; } $sendArr[$minuteTime] = isset($sendArr[$minuteTime]) ? $sendArr[$minuteTime] + 1 : 1; wfConfig::set_ser('alertFreqTrack', $sendArr); } //Prevent duplicate emails within 1 hour: $hash = md5(implode(',', $emails) . ':' . $subject . ':' . $alertMsg . ':' . $IP); //Hex $lastHash = wfConfig::get('lastEmailHash', false); if ($lastHash) { $lastHashDat = explode(':', $lastHash); //[time, hash] if (time() - $lastHashDat[0] < 3600) { if ($lastHashDat[1] == $hash) { return; //Don't send because this email is identical to the previous email which was sent within the last hour. } } } wfConfig::set('lastEmailHash', time() . ':' . $hash); wp_mail(implode(',', $emails), $subject, $content); }
public function do503($secsToGo, $reason) { wfConfig::inc('total503s'); wfUtils::doNotCache(); header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); if ($secsToGo) { header('Retry-After: ' . $secsToGo); } require_once 'wf503.php'; exit; }
public static function startScan($isFork = false) { if (!$isFork) { //beginning of scan wfConfig::inc('totalScansRun'); wfConfig::set('wfKillRequested', 0); wordfence::status(4, 'info', "Entering start scan routine"); if (wfUtils::isScanRunning()) { return "A scan is already running. Use the kill link if you would like to terminate the current scan."; } } $timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied $testURL = admin_url('admin-ajax.php?action=wordfence_testAjax'); if (!wfConfig::get('startScansRemotely', false)) { $testResult = wp_remote_post($testURL, array('timeout' => $timeout, 'blocking' => true, 'sslverify' => false, 'headers' => array())); wordfence::status(4, 'info', "Test result of scan start URL fetch: " . var_export($testResult, true)); } $cronKey = wfUtils::bigRandomHex(); wfConfig::set('currentCronKey', time() . ',' . $cronKey); if (!wfConfig::get('startScansRemotely', false) && !is_wp_error($testResult) && is_array($testResult) && strstr($testResult['body'], 'WFSCANTESTOK') !== false) { //ajax requests can be sent by the server to itself $cronURL = 'admin-ajax.php?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey; $cronURL = admin_url($cronURL); $headers = array(); wordfence::status(4, 'info', "Starting cron with normal ajax at URL {$cronURL}"); wp_remote_get($cronURL, array('timeout' => $timeout, 'blocking' => true, 'sslverify' => false, 'headers' => $headers)); wordfence::status(4, 'info', "Scan process ended after forking."); } else { $cronURL = admin_url('admin-ajax.php'); $cronURL = preg_replace('/^(https?:\\/\\/)/i', '$1noc1.wordfence.com/scanp/', $cronURL); $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey; $headers = array(); wordfence::status(4, 'info', "Starting cron via proxy at URL {$cronURL}"); wp_remote_get($cronURL, array('timeout' => $timeout, 'blocking' => true, 'sslverify' => false, 'headers' => $headers)); wordfence::status(4, 'info', "Scan process ended after forking."); } return false; //No error }
public static function syncAttackData($exit = true) { global $wpdb; $waf = wfWAF::getInstance(); $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits"); if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) { $attackData = $waf->getStorageEngine()->getNewestAttackDataArray($lastAttackMicroseconds); if ($attackData) { foreach ($attackData as $request) { if (count($request) !== 9 && count($request) !== 10) { continue; } list($logTimeMicroseconds, $requestTime, $ip, $learningMode, $paramKey, $paramValue, $failedRules, $ssl, $requestString, $metadata) = $request; // Skip old entries and hits in learning mode, since they'll get picked up anyways. if ($logTimeMicroseconds <= $lastAttackMicroseconds || $learningMode) { continue; } $hit = new wfRequestModel(); $hit->attackLogTime = $logTimeMicroseconds; $hit->statusCode = 403; $hit->ctime = $requestTime; $hit->IP = wfUtils::inet_pton($ip); if (preg_match('/user\\-agent:(.*?)\\n/i', $requestString, $matches)) { $hit->UA = trim($matches[1]); $hit->isGoogle = wfCrawl::isGoogleCrawler($hit->UA); } if (preg_match('/Referer:(.*?)\\n/i', $requestString, $matches)) { $hit->referer = trim($matches[1]); } if (preg_match('/^[a-z]+\\s+(.*?)\\s+/i', $requestString, $uriMatches) && preg_match('/Host:(.*?)\\n/i', $requestString, $hostMatches)) { $hit->URL = 'http' . ($ssl ? 's' : '') . '://' . trim($hostMatches[1]) . trim($uriMatches[1]); } if (preg_match('/cookie:(.*?)\\n/i', $requestString, $matches)) { $hit->newVisit = strpos($matches[1], 'wfvt_' . crc32(site_url())) !== false ? 1 : 0; $hasVerifiedHumanCookie = strpos($matches[1], 'wordfence_verifiedHuman') !== false; if ($hasVerifiedHumanCookie && preg_match('/wordfence_verifiedHuman=(.*?);/', $matches[1], $cookieMatches)) { $hit->jsRun = (int) wp_verify_nonce($cookieMatches[1], 'wordfence_verifiedHuman' . $hit->UA . $ip); } $hasLoginCookie = strpos($matches[1], $ssl ? SECURE_AUTH_COOKIE : AUTH_COOKIE) !== false; if ($hasLoginCookie && preg_match('/' . ($ssl ? SECURE_AUTH_COOKIE : AUTH_COOKIE) . '=(.*?);/', $matches[1], $cookieMatches)) { $authCookie = rawurldecode($cookieMatches[1]); $authID = $ssl ? wp_validate_auth_cookie($authCookie, 'secure_auth') : wp_validate_auth_cookie($authCookie, 'auth'); if ($authID) { $hit->userID = $authID; } } } $path = '/'; if (preg_match('/^[A-Z]+ (.*?) HTTP\\/1\\.1/', $requestString, $matches)) { if (($pos = strpos($matches[1], '?')) !== false) { $path = substr($matches[1], 0, $pos); } else { $path = $matches[1]; } } $metadata = $metadata != null ? (array) $metadata : array(); if (isset($metadata['finalAction']) && $metadata['finalAction']) { // The request was blocked/redirected because of its IP based on the plugin's blocking settings. WAF blocks should be reported but not shown in live traffic with that as a reason. $action = $metadata['finalAction']['action']; $actionDescription = $action; if (class_exists('wfWAFIPBlocksController')) { if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) { $id = $metadata['finalAction']['id']; $wpdb->query($wpdb->prepare("UPDATE {$wpdb->base_prefix}wfBlocksAdv SET totalBlocked = totalBlocked + 1, lastBlocked = %d WHERE id = %d", $requestTime, $id)); wfActivityReport::logBlockedIP($ip); } else { if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) { $actionDescription .= ' (' . wfConfig::get('cbl_redirURL') . ')'; wfConfig::inc('totalCountryBlocked'); wfActivityReport::logBlockedIP($ip); } else { if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) { wfConfig::inc('totalCountryBlocked'); wfActivityReport::logBlockedIP($ip); } else { if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) { wordfence::wfsnReportBlockedAttempt($ip, 'login'); } } } } } if (strlen($actionDescription) == 0) { $actionDescription = 'Blocked by Wordfence'; } if (empty($failedRules)) { // Just a plugin block $hit->action = 'blocked:wordfence'; if (class_exists('wfWAFIPBlocksController')) { if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) { $hit->action = 'blocked:wfsnrepeat'; } } $hit->actionDescription = $actionDescription; } else { if ($failedRules == 'logged') { $hit->action = 'logged:waf'; } else { // Blocked by the WAF but would've been blocked anyway by the plugin settings so that message takes priority $hit->action = 'blocked:waf-always'; $hit->actionDescription = $actionDescription; } } } else { if ($failedRules == 'logged') { $hit->action = 'logged:waf'; } else { $hit->action = 'blocked:waf'; } } /** @var wfWAFRule $rule */ $ruleIDs = explode('|', $failedRules); $actionData = array('learningMode' => $learningMode, 'failedRules' => $failedRules, 'paramKey' => $paramKey, 'paramValue' => $paramValue, 'path' => $path); if ($ruleIDs && $ruleIDs[0]) { $rule = $waf->getRule($ruleIDs[0]); if ($rule) { if ($hit->action == 'logged:waf' || $hit->action == 'blocked:waf') { $hit->actionDescription = $rule->getDescription(); } $actionData['category'] = $rule->getCategory(); $actionData['ssl'] = $ssl; $actionData['fullRequest'] = base64_encode($requestString); } else { if ($ruleIDs[0] == 'logged') { if ($hit->action == 'logged:waf' || $hit->action == 'blocked:waf') { $hit->actionDescription = 'Watched IP Traffic: ' . $ip; } $actionData['category'] = 'logged'; $actionData['ssl'] = $ssl; $actionData['fullRequest'] = base64_encode($requestString); } } } $hit->actionData = wfRequestModel::serializeActionData($actionData); $hit->save(); self::scheduleSendAttackData(); } } $waf->getStorageEngine()->truncateAttackData(); } update_site_option('wordfence_syncingAttackData', 0); update_site_option('wordfence_syncAttackDataAttempts', 0); update_site_option('wordfence_lastSyncAttackData', time()); if ($exit) { exit; } }
public function do503($secsToGo, $reason) { $this->initLogRequest(); $this->currentRequest->statusCode = 403; if (!$this->currentRequest->action) { $this->currentRequest->action = 'blocked:wordfence'; } if (!$this->currentRequest->actionDescription) { $this->currentRequest->actionDescription = "blocked: " . $reason; } $this->logHit(); wfConfig::inc('total503s'); wfUtils::doNotCache(); header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); if ($secsToGo) { header('Retry-After: ' . $secsToGo); } require_once 'wf503.php'; exit; }