public static function veryFirstAction() { /** @var wpdb $wpdb ; */ global $wpdb; $wfFunc = isset($_GET['_wfsf']) ? @$_GET['_wfsf'] : false; if ($wfFunc == 'unlockEmail') { if (!wp_verify_nonce(@$_POST['nonce'], 'wf-form')) { die("Sorry but your browser sent an invalid security token when trying to use this form."); } $numTries = get_transient('wordfenceUnlockTries'); if ($numTries > 10) { echo "<html><body><h1>Please wait 3 minutes and try again</h1><p>You have used this form too much. Please wait 3 minutes and try again.</p></body></html>"; exit; } if (!$numTries) { $numTries = 1; } else { $numTries = $numTries + 1; } set_transient('wordfenceUnlockTries', $numTries, 180); $email = trim($_POST['email']); global $wpdb; $ws = $wpdb->get_results($wpdb->prepare("SELECT ID, user_login FROM {$wpdb->users} WHERE user_email = %s", $email)); foreach ($ws as $user) { $userDat = get_userdata($user->ID); if (wfUtils::isAdmin($userDat)) { if ($email == $userDat->user_email) { $found = true; break; } } } if (!$found) { foreach (wfConfig::getAlertEmails() as $alertEmail) { if ($alertEmail == $email) { $found = true; break; } } } if ($found) { $key = wfUtils::bigRandomHex(); $IP = wfUtils::getIP(); set_transient('wfunlock_' . $key, $IP, 1800); $content = wfUtils::tmpl('email_unlockRequest.php', array('siteName' => get_bloginfo('name', 'raw'), 'siteURL' => wfUtils::getSiteBaseURL(), 'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key, 'key' => $key, 'IP' => $IP)); wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html"); } echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . wp_kses($email, array()) . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>"; exit; } else { if ($wfFunc == 'unlockAccess') { if (!preg_match('/^\\d+\\.\\d+\\.\\d+\\.\\d+$/', get_transient('wfunlock_' . $_GET['key']))) { echo "Invalid key provided for authentication."; exit; } /* You can enable this for paranoid security leve. if(get_transient('wfunlock_' . $_GET['key']) != wfUtils::getIP()){ echo "You can only use this link from the IP address you used to generate the unlock email."; exit(); } */ $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); if ($_GET['func'] == 'unlockMyIP') { $wfLog->unblockIP(wfUtils::getIP()); $wfLog->unlockOutIP(wfUtils::getIP()); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter header('Location: ' . wp_login_url()); exit; } else { if ($_GET['func'] == 'unlockAllIPs') { wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's."); $wfLog->unblockAllIPs(); $wfLog->unlockAllIPs(); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter header('Location: ' . wp_login_url()); exit; } else { if ($_GET['func'] == 'disableRules') { wfConfig::set('firewallEnabled', 0); wfConfig::set('loginSecurityEnabled', 0); wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's via disabling firewall rules."); $wfLog->unblockAllIPs(); $wfLog->unlockAllIPs(); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter wfConfig::set('cbl_countries', ''); //unblock all countries header('Location: ' . wp_login_url()); exit; } else { echo "Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader."; exit; } } } } } // Sync the WAF data with the database. if (!WFWAF_SUBDIRECTORY_INSTALL && ($waf = wfWAF::getInstance())) { try { $configDefaults = array('apiKey' => wfConfig::get('apiKey'), 'isPaid' => wfConfig::get('isPaid'), 'siteURL' => site_url(), 'homeURL' => home_url(), 'whitelistedIPs' => (string) wfConfig::get('whitelisted'), 'howGetIPs' => (string) wfConfig::get('howGetIPs')); foreach ($configDefaults as $key => $value) { $waf->getStorageEngine()->setConfig($key, $value); } if (empty($_GET['wordfence_syncAttackData'])) { $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits"); if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) { if (get_site_option('wordfence_syncingAttackData') <= time() - 60) { // Could be the request to itself is not completing, add ajax to the head as a workaround $attempts = get_site_option('wordfence_syncAttackDataAttempts', 0); if ($attempts > 10) { add_action('wp_head', 'wordfence::addSyncAttackDataAjax'); add_action('login_head', 'wordfence::addSyncAttackDataAjax'); add_action('admin_head', 'wordfence::addSyncAttackDataAjax'); } else { update_site_option('wordfence_syncAttackDataAttempts', ++$attempts); wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array('timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters('https_local_ssl_verify', false))); } } } } if ($waf instanceof wfWAFWordPress && ($learningModeAttackException = $waf->getLearningModeAttackException())) { $log = self::getLog(); $log->initLogRequest(); $request = $log->getCurrentRequest(); $request->action = 'learned:waf'; $request->attackLogTime = microtime(true); $ruleIDs = array(); /** @var wfWAFRule $failedRule */ foreach ($learningModeAttackException->getFailedRules() as $failedRule) { $ruleIDs[] = $failedRule->getRuleID(); } $actionData = array('learningMode' => 1, 'failedRules' => $ruleIDs, 'paramKey' => $learningModeAttackException->getParamKey(), 'paramValue' => $learningModeAttackException->getParamValue()); if ($ruleIDs && $ruleIDs[0]) { $rule = $waf->getRule($ruleIDs[0]); if ($rule) { $request->actionDescription = $rule->getDescription(); $actionData['category'] = $rule->getCategory(); $actionData['ssl'] = $waf->getRequest()->getProtocol() === 'https'; $actionData['fullRequest'] = base64_encode($waf->getRequest()); } } $request->actionData = wfRequestModel::serializeActionData($actionData); register_shutdown_function(array($request, 'save')); self::scheduleSendAttackData(); } } catch (wfWAFStorageFileException $e) { // We don't have anywhere to write files in this scenario. } } if (wfConfig::get('firewallEnabled')) { $wfLog = self::getLog(); $wfLog->firewallBadIPs(); $IP = wfUtils::getIP(); if ($wfLog->isWhitelisted($IP)) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) { return; } if (wfConfig::get('blockFakeBots')) { if (wfCrawl::isGooglebot() && !wfCrawl::isVerifiedGoogleCrawler()) { $wfLog->blockIP($IP, "Fake Google crawler automatically blocked"); wordfence::status(2, 'info', "Blocking fake Googlebot at IP {$IP}"); $wfLog->do503(3600, "Fake Google crawler automatically blocked."); } } if (wfConfig::get('bannedURLs', false)) { $URLs = explode(',', wfConfig::get('bannedURLs')); foreach ($URLs as $URL) { if (preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])) { $wfLog->blockIP($IP, "Accessed a banned URL."); $wfLog->do503(3600, "Accessed a banned URL."); //exits } } } if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) { $wfLog->blockIP($IP, "POST received with blank user-agent and referer"); $wfLog->do503(3600, "POST received with blank user-agent and referer"); //exits } } }
public static function veryFirstAction() { $wfFunc = isset($_GET['_wfsf']) ? @$_GET['_wfsf'] : false; if ($wfFunc == 'unlockEmail') { if (!wp_verify_nonce(@$_POST['nonce'], 'wf-form')) { die("Sorry but your browser sent an invalid security token when trying to use this form."); } $numTries = get_transient('wordfenceUnlockTries'); if ($numTries > 10) { echo "<html><body><h1>Please wait 3 minutes and try again</h1><p>You have used this form too much. Please wait 3 minutes and try again.</p></body></html>"; exit; } if (!$numTries) { $numTries = 1; } else { $numTries = $numTries + 1; } set_transient('wordfenceUnlockTries', $numTries, 180); $email = trim($_POST['email']); global $wpdb; $ws = $wpdb->get_results($wpdb->prepare("SELECT ID, user_login FROM {$wpdb->users} WHERE user_email = %s", $email)); foreach ($ws as $user) { $userDat = get_userdata($user->ID); if (wfUtils::isAdmin($userDat)) { if ($email == $userDat->user_email) { $found = true; break; } } } if (!$found) { foreach (wfConfig::getAlertEmails() as $alertEmail) { if ($alertEmail == $email) { $found = true; break; } } } if ($found) { $key = wfUtils::bigRandomHex(); $IP = wfUtils::getIP(); set_transient('wfunlock_' . $key, $IP, 1800); $content = wfUtils::tmpl('email_unlockRequest.php', array('siteName' => get_bloginfo('name', 'raw'), 'siteURL' => wfUtils::getSiteBaseURL(), 'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key, 'key' => $key, 'IP' => $IP)); wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html"); } echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . wp_kses($email, array()) . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>"; exit; } else { if ($wfFunc == 'unlockAccess') { if (!preg_match('/^\\d+\\.\\d+\\.\\d+\\.\\d+$/', get_transient('wfunlock_' . $_GET['key']))) { echo "Invalid key provided for authentication."; exit; } /* You can enable this for paranoid security leve. if(get_transient('wfunlock_' . $_GET['key']) != wfUtils::getIP()){ echo "You can only use this link from the IP address you used to generate the unlock email."; exit(); } */ $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); if ($_GET['func'] == 'unlockMyIP') { $wfLog->unblockIP(wfUtils::getIP()); $wfLog->unlockOutIP(wfUtils::getIP()); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter header('Location: ' . wp_login_url()); exit; } else { if ($_GET['func'] == 'unlockAllIPs') { wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's."); $wfLog->unblockAllIPs(); $wfLog->unlockAllIPs(); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter header('Location: ' . wp_login_url()); exit; } else { if ($_GET['func'] == 'disableRules') { wfConfig::set('firewallEnabled', 0); wfConfig::set('loginSecurityEnabled', 0); wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's via disabling firewall rules."); $wfLog->unblockAllIPs(); $wfLog->unlockAllIPs(); delete_transient('wflginfl_' . bin2hex(wfUtils::inet_pton(wfUtils::getIP()))); //Reset login failure counter wfConfig::set('cbl_countries', ''); //unblock all countries header('Location: ' . wp_login_url()); exit; } else { echo "Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader."; exit; } } } } } if (wfConfig::get('firewallEnabled')) { $wfLog = self::getLog(); $wfLog->firewallBadIPs(); $IP = wfUtils::getIP(); if ($wfLog->isWhitelisted($IP)) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) { return; } if (wfConfig::get('blockFakeBots')) { if (wfCrawl::isGooglebot() && !wfCrawl::isVerifiedGoogleCrawler()) { $wfLog->blockIP($IP, "Fake Google crawler automatically blocked"); wordfence::status(2, 'info', "Blocking fake Googlebot at IP {$IP}"); $wfLog->do503(3600, "Fake Google crawler automatically blocked."); } } if (wfConfig::get('bannedURLs', false)) { $URLs = explode(',', wfConfig::get('bannedURLs')); foreach ($URLs as $URL) { if ($_SERVER['REQUEST_URI'] == trim($URL)) { $wfLog->blockIP($IP, "Accessed a banned URL."); $wfLog->do503(3600, "Accessed a banned URL."); //exits } } } if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) { $wfLog->blockIP($IP, "POST received with blank user-agent and referer"); $wfLog->do503(3600, "POST received with blank user-agent and referer"); //exits } } }
public function logLeechAndBlock($type) { //404 or hit if (wfConfig::get('firewallEnabled')) { //Moved the following block into the "is fw enabled section" for optimization. $IP = wfUtils::getIP(); $IPnum = wfUtils::inet_pton($IP); if ($this->isWhitelisted($IP)) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) { return; } if ($type == '404') { $allowed404s = wfConfig::get('allowed404s'); if (is_string($allowed404s)) { $allowed404s = array_filter(explode("\n", $allowed404s)); $allowed404sPattern = ''; foreach ($allowed404s as $allowed404) { $allowed404sPattern .= preg_replace('/\\\\\\*/', '.*?', preg_quote($allowed404, '/')) . '|'; } $uri = $_SERVER['REQUEST_URI']; if (($index = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $index); } if ($allowed404sPattern && preg_match('/^' . substr($allowed404sPattern, 0, -1) . '$/i', $uri)) { return; } } } if ($type == '404') { $table = $this->scanTable; } else { if ($type == 'hit') { $table = $this->leechTable; } else { wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): {$type}"); return; } } $this->getDB()->queryWrite("insert into {$table} (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_pton($IP)); $hitsPerMinute = $this->getDB()->querySingle("select @wfcurrenthits"); //end block moved into "is fw enabled" section //Range blocking was here. Moved to wordfenceClass::veryFirstAction if (wfConfig::get('blockFakeBots')) { if (wfCrawl::isGooglebot() && !wfCrawl::verifyCrawlerPTR($this->googlePattern, $IP)) { $this->blockIP($IP, "Fake Google crawler automatically blocked"); wordfence::status(2, 'info', "Blocking fake Googlebot at IP {$IP}"); } } if (wfConfig::get('bannedURLs', false)) { $URLs = explode(',', wfConfig::get('bannedURLs')); foreach ($URLs as $URL) { if ($_SERVER['REQUEST_URI'] == trim($URL)) { $this->blockIP($IP, "Accessed a banned URL."); $this->do503(3600, "Accessed a banned URL."); //exits } } } if (wfConfig::get('maxGlobalRequests') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxGlobalRequests')) { //Applies to 404 or pageview $this->takeBlockingAction('maxGlobalRequests', "Exceeded the maximum global requests per minute for crawlers or humans."); } if ($type == '404') { global $wpdb; $p = $wpdb->base_prefix; if (wfConfig::get('other_WFNet')) { $this->getDB()->queryWrite("insert IGNORE into {$p}" . "wfNet404s (sig, ctime, URI) values (UNHEX(MD5('%s')), unix_timestamp(), '%s')", $_SERVER['REQUEST_URI'], $_SERVER['REQUEST_URI']); } $pat = wfConfig::get('vulnRegex'); if ($pat) { $URL = wfUtils::getRequestedURL(); if (preg_match($pat, $URL)) { $this->getDB()->queryWrite("insert IGNORE into {$p}" . "wfVulnScanners (IP, ctime, hits) values (%s, unix_timestamp(), 1) ON DUPLICATE KEY UPDATE ctime = unix_timestamp(), hits = hits + 1", wfUtils::inet_pton($IP)); if (wfConfig::get('maxScanHits') != 'DISABLED') { if (empty($_SERVER['HTTP_REFERER'])) { $this->getDB()->queryWrite("insert into " . $this->badLeechersTable . " (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfblcurrenthits := hits + 1, hits + 1, hits + 1)", $IPnum); $BL_hitsPerMinute = $this->getDB()->querySingle("select @wfblcurrenthits"); if ($BL_hitsPerMinute > wfConfig::get('maxScanHits')) { $this->takeBlockingAction('maxScanHits', "Exceeded the maximum number of 404 requests per minute for a known security vulnerability."); } } } } } } if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) { $this->blockIP($IP, "POST received with blank user-agent and referer"); $this->do503(3600, "POST received with blank user-agent and referer"); //exits } if (isset($_SERVER['HTTP_USER_AGENT']) && wfCrawl::isCrawler($_SERVER['HTTP_USER_AGENT'])) { if ($type == 'hit' && wfConfig::get('maxRequestsCrawlers') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxRequestsCrawlers')) { $this->takeBlockingAction('maxRequestsCrawlers', "Exceeded the maximum number of requests per minute for crawlers."); //may not exit } else { if ($type == '404' && wfConfig::get('max404Crawlers') != 'DISABLED' && $hitsPerMinute > wfConfig::get('max404Crawlers')) { $this->takeBlockingAction('max404Crawlers', "Exceeded the maximum number of page not found errors per minute for a crawler."); } } } else { if ($type == 'hit' && wfConfig::get('maxRequestsHumans') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxRequestsHumans')) { $this->takeBlockingAction('maxRequestsHumans', "Exceeded the maximum number of page requests per minute for humans."); } else { if ($type == '404' && wfConfig::get('max404Humans') != 'DISABLED' && $hitsPerMinute > wfConfig::get('max404Humans')) { $this->takeBlockingAction('max404Humans', "Exceeded the maximum number of page not found errors per minute for humans."); } } } } }
private function googleSafetyCheckOK() { //returns true if OK to block. Returns false if we must not block. $cacheKey = md5((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') . ' ' . wfUtils::getIP()); //Cache so we can call this multiple times in one request if (!isset(self::$gbSafeCache[$cacheKey])) { $nb = wfConfig::get('neverBlockBG'); if ($nb == 'treatAsOtherCrawlers') { self::$gbSafeCache[$cacheKey] = true; //OK to block because we're treating google like everyone else } else { if ($nb == 'neverBlockUA' || $nb == 'neverBlockVerified') { if (wfCrawl::isGoogleCrawler()) { //Check the UA using regex if ($nb == 'neverBlockVerified') { if (wfCrawl::isGooglebot()) { //UA is the one, the only, the original Googlebot if (wfCrawl::verifyCrawlerPTR($this->googlePattern, wfUtils::getIP())) { //UA check passed, now verify using PTR if configured to self::$gbSafeCache[$cacheKey] = false; //This is a verified Google crawler, so no we can't block it } else { self::$gbSafeCache[$cacheKey] = true; //This is a crawler claiming to be Google but it did not verify } } else { //UA isGoogleCrawler, but is not Googlebot itself. E.g. feedreader, google-site-verification, etc. self::$gbSafeCache[$cacheKey] = false; //This is a crawler with a google UA, but it's not Googlebot, so we don't block for safety. We can't verify these because they don't have a PTR record. e.g. Feedreader. } } else { //neverBlockUA self::$gbSafeCache[$cacheKey] = false; //User configured us to only do a UA check and this claims to be google so don't block } } else { self::$gbSafeCache[$cacheKey] = true; //This isn't a Google UA, so it's OK to block } } else { //error_log("Wordfence error: neverBlockBG option is not set."); self::$gbSafeCache[$cacheKey] = false; //Oops the config option is not set. This should never happen because it's set on install. So we return false to indicate it's not OK to block just for safety. } } } if (!isset(self::$gbSafeCache[$cacheKey])) { //error_log("Wordfence assertion fail in googleSafetyCheckOK: cached value is not set."); return false; //for safety } return self::$gbSafeCache[$cacheKey]; //return cached value }