Exemple #1
0
    public static function runInstall()
    {
        if (self::$runInstallCalled) {
            return;
        }
        self::$runInstallCalled = true;
        if (function_exists('ignore_user_abort')) {
            ignore_user_abort(true);
        }
        $previous_version = get_option('wordfence_version', '0.0.0');
        update_option('wordfence_version', WORDFENCE_VERSION);
        //In case we have a fatal error we don't want to keep running install.
        //EVERYTHING HERE MUST BE IDEMPOTENT
        //Remove old legacy cron job if exists
        wp_clear_scheduled_hook('wordfence_scheduled_scan');
        $schema = new wfSchema();
        $schema->createAll();
        //if not exists
        wfConfig::setDefaults();
        //If not set
        $restOfSite = wfConfig::get('cbl_restOfSiteBlocked', 'notset');
        if ($restOfSite == 'notset') {
            wfConfig::set('cbl_restOfSiteBlocked', '1');
        }
        //Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
        wordfence::scheduleScans();
        if (wfConfig::get('autoUpdate') == '1') {
            wfConfig::enableAutoUpdate();
            //Sets up the cron
        }
        if (!wfConfig::get('apiKey')) {
            $api = new wfAPI('', wfUtils::getWPVersion());
            try {
                $keyData = $api->call('get_anon_api_key');
                if ($keyData['ok'] && $keyData['apiKey']) {
                    wfConfig::set('apiKey', $keyData['apiKey']);
                } else {
                    throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
                }
            } catch (Exception $e) {
                error_log("Could not fetch free API key from Wordfence: " . $e->getMessage());
                return;
            }
        }
        wp_clear_scheduled_hook('wordfence_daily_cron');
        wp_clear_scheduled_hook('wordfence_hourly_cron');
        if (is_main_site()) {
            wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
            //'daily'
            wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron');
        }
        $db = new wfDB();
        if ($db->columnExists('wfHits', 'HTTPHeaders')) {
            //Upgrade from 3.0.4
            global $wpdb;
            $prefix = $wpdb->base_prefix;
            $count = $db->querySingle("select count(*) as cnt from {$prefix}" . "wfHits");
            if ($count > 20000) {
                $db->queryWrite("delete from {$prefix}" . "wfHits order by id asc limit " . ($count - 20000));
            }
            $db->dropColumn('wfHits', 'HTTPHeaders');
        }
        //Upgrading from 1.5.6 or earlier needs:
        $db->createKeyIfNotExists('wfStatus', 'level', 'k2');
        if (wfConfig::get('isPaid') == 'free') {
            wfConfig::set('isPaid', '');
        }
        //End upgrade from 1.5.6
        /** @var wpdb $wpdb */
        global $wpdb;
        $prefix = $wpdb->base_prefix;
        $db->queryWriteIgnoreError("alter table {$prefix}" . "wfConfig modify column val longblob");
        $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks add column permanent tinyint UNSIGNED default 0");
        $db->queryWriteIgnoreError("alter table {$prefix}" . "wfStatus modify column msg varchar(1000) NOT NULL");
        //3.1.2 to 3.1.4
        $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks modify column blockedTime bigint signed NOT NULL");
        //3.2.1 to 3.2.2
        $db->queryWriteIgnoreError("alter table {$prefix}" . "wfLockedOut modify column blockedTime bigint signed NOT NULL");
        $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileQueue");
        $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileChanges");
        $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}wfStatus where field = 'id'");
        if (!$result || strtolower($result->Key) != 'pri') {
            //Adding primary key to this table because some backup apps use primary key during backup.
            $db->queryWriteIgnoreError("alter table {$prefix}wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
        }
        $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_options'");
        if ($optScanEnabled != '0' && $optScanEnabled != '1') {
            $db->queryWrite("update {$prefix}" . "wfConfig set val='1' where name='scansEnabled_options'");
        }
        $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_heartbleed'");
        if ($optScanEnabled != '0' && $optScanEnabled != '1') {
            //Enable heartbleed if no value is set.
            wfConfig::set('scansEnabled_heartbleed', 1);
        }
        if (wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') {
            wfCache::removeCacheDirectoryHtaccess();
        }
        // IPv6 schema changes for 6.0.1
        $tables_with_ips = array('wfCrawlers', 'wfBadLeechers', 'wfBlockedIPLog', 'wfBlocks', 'wfHits', 'wfLeechers', 'wfLockedOut', 'wfLocs', 'wfLogins', 'wfReverseCache', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners');
        foreach ($tables_with_ips as $ip_table) {
            $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'");
            if (!$result || strtolower($result->Type) == 'binary(16)') {
                continue;
            }
            $db->queryWriteIgnoreError("ALTER TABLE {$prefix}{$ip_table} MODIFY IP BINARY(16)");
            // Just to be sure we don't corrupt the data if the alter fails.
            $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'");
            if (!$result || strtolower($result->Type) != 'binary(16)') {
                continue;
            }
            $db->queryWriteIgnoreError("UPDATE {$prefix}{$ip_table} SET IP = CONCAT(LPAD(CHAR(0xff, 0xff), 12, CHAR(0)), LPAD(\n\tCHAR(\n\t\tCAST(IP as UNSIGNED) >> 24 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 16 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 8 & 0xFF,\n\t\tCAST(IP as UNSIGNED) & 0xFF\n\t),\n\t4,\n\tCHAR(0)\n))");
        }
        // Fix the data in the country column.
        // TODO: add version check so this doesn't run on every update.
        $ip_results = $wpdb->get_results("SELECT * FROM `{$prefix}wfBlockedIPLog` GROUP BY IP");
        if ($ip_results) {
            foreach ($ip_results as $ip_row) {
                $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP)), $ip_row->IP));
            }
        }
        if (wfConfig::get('other_hideWPVersion')) {
            wfUtils::hideReadme();
        }
        $colsFor610 = array('attackLogTime' => '`attackLogTime` double(17,6) unsigned NOT NULL AFTER `id`', 'statusCode' => '`statusCode` int(11) NOT NULL DEFAULT 0 AFTER `jsRun`', 'action' => "`action` varchar(64) NOT NULL DEFAULT '' AFTER `UA`", 'actionDescription' => '`actionDescription` text AFTER `action`', 'actionData' => '`actionData` text AFTER `actionDescription`');
        $hitTable = $wpdb->base_prefix . 'wfHits';
        foreach ($colsFor610 as $col => $colDefintion) {
            $count = $wpdb->get_col($wpdb->prepare(<<<SQL
SELECT * FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA=DATABASE()
AND COLUMN_NAME=%s
AND TABLE_NAME=%s
SQL
, $col, $hitTable));
            if (!$count) {
                $wpdb->query("ALTER TABLE {$hitTable} ADD COLUMN {$colDefintion}");
            }
        }
        $has404 = $wpdb->get_col($wpdb->prepare(<<<SQL
SELECT * FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA=DATABASE()
AND COLUMN_NAME='is404'
AND TABLE_NAME=%s
SQL
, $hitTable));
        if ($has404) {
            $wpdb->query(<<<SQL
UPDATE {$hitTable}
SET statusCode= CASE
WHEN is404=1 THEN 404
ELSE 200
END
SQL
);
            $wpdb->query("ALTER TABLE {$hitTable} DROP COLUMN `is404`");
        }
        $loginsTable = "{$wpdb->base_prefix}wfLogins";
        $hasHitID = $wpdb->get_col($wpdb->prepare(<<<SQL
SELECT * FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA=DATABASE()
AND COLUMN_NAME='hitID'
AND TABLE_NAME=%s
SQL
, $loginsTable));
        if (!$hasHitID) {
            $wpdb->query("ALTER TABLE {$loginsTable} ADD COLUMN hitID int(11) DEFAULT NULL AFTER `id`, ADD INDEX(hitID)");
        }
        if (!WFWAF_SUBDIRECTORY_INSTALL) {
            try {
                wfWAF::getInstance()->getStorageEngine()->setConfig('wafDisabled', false);
            } catch (wfWAFStorageFileException $e) {
                error_log($e);
            }
        }
        // Call this before creating the index in cases where the wp-cron isn't running.
        self::trimWfHits();
        $hitsTable = "{$wpdb->base_prefix}wfHits";
        $hasAttackLogTimeIndex = $wpdb->get_var($wpdb->prepare(<<<SQL
SELECT COLUMN_KEY FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = %s
AND COLUMN_NAME = 'attackLogTime'
SQL
, $hitsTable));
        if (!$hasAttackLogTimeIndex) {
            $wpdb->query("ALTER TABLE {$hitsTable} ADD INDEX `attackLogTime` (`attackLogTime`)");
        }
        //Must be the final line
    }
    /**
     * @param mixed $ip_address
     * @param int|null $unixday
     */
    public static function logBlockedIP($ip_address, $unixday = null)
    {
        /** @var wpdb $wpdb */
        global $wpdb;
        if (wfUtils::isValidIP($ip_address)) {
            $ip_bin = wfUtils::inet_pton($ip_address);
        } else {
            $ip_bin = $ip_address;
            $ip_address = wfUtils::inet_ntop($ip_bin);
        }
        $blocked_table = "{$wpdb->base_prefix}wfBlockedIPLog";
        $unixday_insert = 'FLOOR(UNIX_TIMESTAMP() / 86400)';
        if (is_int($unixday)) {
            $unixday_insert = absint($unixday);
        }
        $country = wfUtils::IP2Country($ip_address);
        $wpdb->query($wpdb->prepare(<<<SQL
INSERT INTO {$blocked_table} (IP, countryCode, blockCount, unixday)
VALUES (%s, %s, 1, {$unixday_insert})
ON DUPLICATE KEY UPDATE blockCount = blockCount + 1
SQL
, $ip_bin, $country));
    }
 public function displayIP($binaryIP)
 {
     $readableIP = wfUtils::inet_ntop($binaryIP);
     $country = wfUtils::countryCode2Name(wfUtils::IP2Country($readableIP));
     return "{$readableIP} (" . ($country ? $country : 'Unknown') . ")";
 }
 public function firewallBadIPs()
 {
     $IP = wfUtils::getIP();
     if ($this->isWhitelisted($IP)) {
         return;
     }
     $IPnum = wfUtils::inet_pton($IP);
     //New range and UA pattern blocking:
     $r1 = $this->getDB()->querySelect("select id, blockType, blockString from " . $this->ipRangesTable);
     foreach ($r1 as $blockRec) {
         if ($blockRec['blockType'] == 'IU') {
             $ipRangeBlocked = false;
             $uaPatternBlocked = false;
             $refBlocked = false;
             $bDat = explode('|', $blockRec['blockString']);
             $ipRange = $bDat[0];
             $uaPattern = $bDat[1];
             $refPattern = isset($bDat[2]) ? $bDat[2] : '';
             if ($ipRange) {
                 list($start_range, $end_range) = explode('-', $ipRange);
                 if (preg_match('/[\\.:]/', $start_range)) {
                     $start_range = wfUtils::inet_pton($start_range);
                     $end_range = wfUtils::inet_pton($end_range);
                 } else {
                     $start_range = wfUtils::inet_pton(long2ip($start_range));
                     $end_range = wfUtils::inet_pton(long2ip($end_range));
                 }
                 if (strcmp($IPnum, $start_range) >= 0 && strcmp($IPnum, $end_range) <= 0) {
                     $ipRangeBlocked = true;
                 }
             }
             if ($uaPattern) {
                 if (wfUtils::isUABlocked($uaPattern)) {
                     $uaPatternBlocked = true;
                 }
             }
             if ($refPattern) {
                 if (wfUtils::isRefererBlocked($refPattern)) {
                     $refBlocked = true;
                 }
             }
             $doBlock = false;
             if ($uaPattern && $ipRange && $refPattern) {
                 if ($uaPatternBlocked && $ipRangeBlocked && $refBlocked) {
                     $doBlock = true;
                 }
             }
             if ($uaPattern && $ipRange) {
                 if ($uaPatternBlocked && $ipRangeBlocked) {
                     $doBlock = true;
                 }
             }
             if ($uaPattern && $refPattern) {
                 if ($uaPatternBlocked && $refBlocked) {
                     $doBlock = true;
                 }
             }
             if ($ipRange && $refPattern) {
                 if ($ipRangeBlocked && $refBlocked) {
                     $doBlock = true;
                 }
             } else {
                 if ($uaPattern) {
                     if ($uaPatternBlocked) {
                         $doBlock = true;
                     }
                 } else {
                     if ($ipRange) {
                         if ($ipRangeBlocked) {
                             $doBlock = true;
                         }
                     } else {
                         if ($refPattern) {
                             if ($refBlocked) {
                                 $doBlock = true;
                             }
                         }
                     }
                 }
             }
             if ($doBlock) {
                 $this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']);
                 wfActivityReport::logBlockedIP($IP);
                 $this->do503(3600, "Advanced blocking in effect.");
             }
         }
     }
     //End range/UA blocking
     // Country blocking
     if (wfConfig::get('isPaid')) {
         $blockedCountries = wfConfig::get('cbl_countries', false);
         $bareRequestURI = wfUtils::extractBareURI($_SERVER['REQUEST_URI']);
         $bareBypassRedirURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassRedirURL', ''));
         $skipCountryBlocking = false;
         if ($bareBypassRedirURI && $bareRequestURI == $bareBypassRedirURI) {
             //Run this before country blocking because even if the user isn't blocked we need to set the bypass cookie so they can bypass future blocks.
             $bypassRedirDest = wfConfig::get('cbl_bypassRedirDest', '');
             if ($bypassRedirDest) {
                 self::setCBLCookieBypass();
                 $this->redirect($bypassRedirDest);
                 //exits
             }
         }
         $bareBypassViewURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassViewURL', ''));
         if ($bareBypassViewURI && $bareBypassViewURI == $bareRequestURI) {
             self::setCBLCookieBypass();
             $skipCountryBlocking = true;
         }
         if (!$skipCountryBlocking && $blockedCountries && !self::isCBLBypassCookieSet()) {
             if (is_user_logged_in() && !wfConfig::get('cbl_loggedInBlocked', false)) {
                 //User is logged in and we're allowing logins
                 //Do nothing
             } else {
                 if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false && !wfConfig::get('cbl_loginFormBlocked', false)) {
                     //It's the login form and we're allowing that
                     //Do nothing
                 } else {
                     if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') === false && !wfConfig::get('cbl_restOfSiteBlocked', false)) {
                         //It's the rest of the site and we're allowing that
                         //Do nothing
                     } else {
                         if ($country = wfUtils::IP2Country($IP)) {
                             foreach (explode(',', $blockedCountries) as $blocked) {
                                 if (strtoupper($blocked) == strtoupper($country)) {
                                     //At this point we know the user has been blocked
                                     if (wfConfig::get('cbl_action') == 'redir') {
                                         $redirURL = wfConfig::get('cbl_redirURL');
                                         $eRedirHost = wfUtils::extractHostname($redirURL);
                                         $isExternalRedir = false;
                                         if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) {
                                             //It's an external redirect...
                                             $isExternalRedir = true;
                                         }
                                         if (!$isExternalRedir && wfUtils::extractBareURI($redirURL) == $bareRequestURI) {
                                             //Is this the URI we want to redirect to, then don't block it
                                             //Do nothing
                                             /* Uncomment the following if page components aren't loading for the page we redirect to.
                                             			   Uncommenting is not recommended because it means that anyone from a blocked country
                                             			   can crawl your site by sending the page blocked users are redirected to as the referer for every request.
                                             			   But it's your call.
                                             			} else if(wfUtils::extractBareURI($_SERVER['HTTP_REFERER']) == $redirURL){ //If the referer the page we want to redirect to? Then this might be loading as a component so don't block.
                                             				//Do nothing
                                             			*/
                                         } else {
                                             $this->redirect(wfConfig::get('cbl_redirURL'));
                                         }
                                     } else {
                                         $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
                                         wfConfig::inc('totalCountryBlocked');
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($rec = $this->getDB()->querySingleRec("select blockedTime, reason from " . $this->blocksTable . " where IP=%s and (permanent=1 OR (blockedTime + %s > unix_timestamp()))", $IPnum, wfConfig::get('blockedTime'))) {
         $this->getDB()->queryWrite("update " . $this->blocksTable . " set lastAttempt=unix_timestamp(), blockedHits = blockedHits + 1 where IP=%s", $IPnum);
         $now = $this->getDB()->querySingle("select unix_timestamp()");
         $secsToGo = $rec['blockedTime'] + wfConfig::get('blockedTime') - $now;
         if (wfConfig::get('other_WFNet') && strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false) {
             //We're on the login page and this IP has been blocked
             wordfence::wfsnReportBlockedAttempt($IP, 'login');
         }
         $this->do503($secsToGo, $rec['reason']);
     }
 }
    /**
     *
     */
    public static function processAttackData()
    {
        global $wpdb;
        $waf = wfWAF::getInstance();
        if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
            $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
        }
        //Send alert email if needed
        if (wfConfig::get('wafAlertOnAttacks')) {
            $alertInterval = wfConfig::get('wafAlertInterval', 0);
            $cutoffTime = max(time() - $alertInterval, wfConfig::get('wafAlertLastSendTime'));
            $wafAlertWhitelist = wfConfig::get('wafAlertWhitelist', '');
            $wafAlertWhitelist = preg_split("/[,\r\n]+/", $wafAlertWhitelist);
            foreach ($wafAlertWhitelist as $index => &$entry) {
                $entry = trim($entry);
                if (!preg_match('/^(?:\\d{1,3}(?:\\.|$)){4}/', $entry) && !preg_match('/^((?:[\\da-f]{1,4}(?::|)){0,8})(::)?((?:[\\da-f]{1,4}(?::|)){0,8})$/i', $entry)) {
                    unset($wafAlertWhitelist[$index]);
                    continue;
                }
                $packed = @wfUtils::inet_pton($entry);
                if ($packed === false) {
                    unset($wafAlertWhitelist[$index]);
                    continue;
                }
                $entry = bin2hex($packed);
            }
            $wafAlertWhitelist = array_filter($wafAlertWhitelist);
            $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits\n\tWHERE action = 'blocked:waf' " . (count($wafAlertWhitelist) ? "AND HEX(IP) NOT IN (" . implode(", ", array_fill(0, count($wafAlertWhitelist), '%s')) . ")" : "") . "AND attackLogTime > %.6f\n\tORDER BY attackLogTime DESC\n\tLIMIT 10", array_merge($wafAlertWhitelist, array($cutoffTime))));
            $attackCount = $wpdb->get_var('SELECT FOUND_ROWS()');
            if ($attackCount >= wfConfig::get('wafAlertThreshold')) {
                $durationMessage = wfUtils::makeDuration($alertInterval);
                $message = <<<ALERTMSG
The Wordfence Web Application Firewall has blocked {$attackCount} attacks over the last {$durationMessage}. Below is a sample of these recent attacks:


ALERTMSG;
                $attackTable = array();
                $dateMax = $ipMax = $countryMax = 0;
                foreach ($attackData as $row) {
                    $row->longDescription = "Blocked for " . $row->actionDescription;
                    $actionData = json_decode($row->actionData, true);
                    if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) {
                        continue;
                    }
                    $paramKey = base64_decode($actionData['paramKey']);
                    $paramValue = base64_decode($actionData['paramValue']);
                    if (strlen($paramValue) > 100) {
                        $paramValue = substr($paramValue, 0, 100) . chr(2026);
                    }
                    if (preg_match('/([a-z0-9_]+\\.[a-z0-9_]+)(?:\\[(.+?)\\](.*))?/i', $paramKey, $matches)) {
                        switch ($matches[1]) {
                            case 'request.queryString':
                                $row->longDescription = "Blocked for " . $row->actionDescription . ' in query string: ' . $matches[2] . '=' . $paramValue;
                                break;
                            case 'request.body':
                                $row->longDescription = "Blocked for " . $row->actionDescription . ' in POST body: ' . $matches[2] . '=' . $paramValue;
                                break;
                            case 'request.cookie':
                                $row->longDescription = "Blocked for " . $row->actionDescription . ' in cookie: ' . $matches[2] . '=' . $paramValue;
                                break;
                            case 'request.fileNames':
                                $row->longDescription = "Blocked for a " . $row->actionDescription . ' in file: ' . $matches[2] . '=' . $paramValue;
                                break;
                        }
                    }
                    $date = date_i18n('F j, Y g:ia', floor($row->attackLogTime));
                    $dateMax = max(strlen($date), $dateMax);
                    $ip = wfUtils::inet_ntop($row->IP);
                    $ipMax = max(strlen($ip), $ipMax);
                    $country = wfUtils::countryCode2Name(wfUtils::IP2Country($ip));
                    $country = empty($country) ? 'Unknown' : $country;
                    $countryMax = max(strlen($country), $countryMax);
                    $attackTable[] = array('date' => $date, 'IP' => $ip, 'country' => $country, 'message' => $row->longDescription);
                }
                foreach ($attackTable as $row) {
                    $date = str_pad($row['date'], $dateMax + 2);
                    $ip = str_pad($row['IP'] . " ({$row['country']})", $ipMax + $countryMax + 8);
                    $attackMessage = $row['message'];
                    $message .= $date . $ip . $attackMessage . "\n";
                }
                self::alert('Increased Attack Rate', $message, false);
                wfConfig::set('wafAlertLastSendTime', time());
            }
        }
        //Send attack data
        $limit = 500;
        $lastSendTime = wfConfig::get('lastAttackDataSendTime');
        $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits\nWHERE action in ('blocked:waf', 'learned:waf', 'logged:waf', 'blocked:waf-always')\nAND attackLogTime > %.6f\nLIMIT %d", $lastSendTime, $limit));
        $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()');
        if ($attackData && wfConfig::get('other_WFNet', true)) {
            $response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey')));
            if (!is_wp_error($response)) {
                $okToSendBody = wp_remote_retrieve_body($response);
                if ($okToSendBody === 'ok') {
                    // Build JSON to send
                    $dataToSend = array();
                    $attackDataToUpdate = array();
                    foreach ($attackData as $attackDataRow) {
                        $actionData = (array) wfRequestModel::unserializeActionData($attackDataRow->actionData);
                        $dataToSend[] = array($attackDataRow->attackLogTime, $attackDataRow->ctime, wfUtils::inet_ntop($attackDataRow->IP), array_key_exists('learningMode', $actionData) ? $actionData['learningMode'] : 0, array_key_exists('paramKey', $actionData) ? base64_encode($actionData['paramKey']) : false, array_key_exists('paramValue', $actionData) ? base64_encode($actionData['paramValue']) : false, array_key_exists('failedRules', $actionData) ? $actionData['failedRules'] : '', strpos($attackDataRow->URL, 'https') === 0 ? 1 : 0, array_key_exists('fullRequest', $actionData) ? $actionData['fullRequest'] : '');
                        if (array_key_exists('fullRequest', $actionData)) {
                            unset($actionData['fullRequest']);
                            $attackDataToUpdate[$attackDataRow->id] = array('actionData' => wfRequestModel::serializeActionData($actionData));
                        }
                        if ($attackDataRow->attackLogTime > $lastSendTime) {
                            $lastSendTime = $attackDataRow->attackLogTime;
                        }
                    }
                    $response = wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array('action' => 'send_waf_attack_data', 'k' => $waf->getStorageEngine()->getConfig('apiKey'), 's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') : sprintf('%s://%s/', $waf->getRequest()->getProtocol(), rawurlencode($waf->getRequest()->getHost())), 't' => microtime(true)), null, '&'), array('body' => json_encode($dataToSend), 'headers' => array('Content-Type' => 'application/json'), 'timeout' => 30));
                    if (!is_wp_error($response) && ($body = wp_remote_retrieve_body($response))) {
                        $jsonData = json_decode($body, true);
                        if (is_array($jsonData) && array_key_exists('success', $jsonData)) {
                            // Successfully sent data, remove the full request from the table to reduce storage size
                            foreach ($attackDataToUpdate as $hitID => $dataToUpdate) {
                                $wpdb->update($wpdb->base_prefix . 'wfHits', $dataToUpdate, array('id' => $hitID));
                            }
                            wfConfig::set('lastAttackDataSendTime', $lastSendTime);
                            if ($totalRows > $limit) {
                                self::scheduleSendAttackData();
                            }
                            if (array_key_exists('data', $jsonData) && array_key_exists('watchedIPList', $jsonData['data'])) {
                                $waf->getStorageEngine()->setConfig('watchedIPs', $jsonData['data']['watchedIPList']);
                            }
                        }
                    }
                } else {
                    if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) {
                        self::scheduleSendAttackData(time() + $matches[1]);
                    }
                }
                // Could be that the server is down, so hold off on sending data for a little while.
            } else {
                self::scheduleSendAttackData(time() + 7200);
            }
        } else {
            if (!wfConfig::get('other_WFNet', true)) {
                wfConfig::set('lastAttackDataSendTime', time());
            }
        }
        self::trimWfHits();
    }
 public static function runInstall()
 {
     if (self::$runInstallCalled) {
         return;
     }
     self::$runInstallCalled = true;
     if (function_exists('ignore_user_abort')) {
         ignore_user_abort(true);
     }
     $previous_version = get_option('wordfence_version', '0.0.0');
     update_option('wordfence_version', WORDFENCE_VERSION);
     //In case we have a fatal error we don't want to keep running install.
     //EVERYTHING HERE MUST BE IDEMPOTENT
     //Remove old legacy cron job if exists
     wp_clear_scheduled_hook('wordfence_scheduled_scan');
     $schema = new wfSchema();
     $schema->createAll();
     //if not exists
     wfConfig::setDefaults();
     //If not set
     $restOfSite = wfConfig::get('cbl_restOfSiteBlocked', 'notset');
     if ($restOfSite == 'notset') {
         wfConfig::set('cbl_restOfSiteBlocked', '1');
     }
     //Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
     wordfence::scheduleScans();
     if (wfConfig::get('autoUpdate') == '1') {
         wfConfig::enableAutoUpdate();
         //Sets up the cron
     }
     if (!wfConfig::get('apiKey')) {
         $api = new wfAPI('', wfUtils::getWPVersion());
         try {
             $keyData = $api->call('get_anon_api_key');
             if ($keyData['ok'] && $keyData['apiKey']) {
                 wfConfig::set('apiKey', $keyData['apiKey']);
             } else {
                 throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
             }
         } catch (Exception $e) {
             error_log("Could not fetch free API key from Wordfence: " . $e->getMessage());
             return;
         }
     }
     wp_clear_scheduled_hook('wordfence_daily_cron');
     wp_clear_scheduled_hook('wordfence_hourly_cron');
     if (is_main_site()) {
         wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
         //'daily'
         wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron');
     }
     $db = new wfDB();
     if ($db->columnExists('wfHits', 'HTTPHeaders')) {
         //Upgrade from 3.0.4
         global $wpdb;
         $prefix = $wpdb->base_prefix;
         $count = $db->querySingle("select count(*) as cnt from {$prefix}" . "wfHits");
         if ($count > 20000) {
             $db->queryWrite("delete from {$prefix}" . "wfHits order by id asc limit " . ($count - 20000));
         }
         $db->dropColumn('wfHits', 'HTTPHeaders');
     }
     //Upgrading from 1.5.6 or earlier needs:
     $db->createKeyIfNotExists('wfStatus', 'level', 'k2');
     if (wfConfig::get('isPaid') == 'free') {
         wfConfig::set('isPaid', '');
     }
     //End upgrade from 1.5.6
     /** @var wpdb $wpdb */
     global $wpdb;
     $prefix = $wpdb->base_prefix;
     $db->queryWriteIgnoreError("alter table {$prefix}" . "wfConfig modify column val longblob");
     $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks add column permanent tinyint UNSIGNED default 0");
     $db->queryWriteIgnoreError("alter table {$prefix}" . "wfStatus modify column msg varchar(1000) NOT NULL");
     //3.1.2 to 3.1.4
     $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks modify column blockedTime bigint signed NOT NULL");
     //3.2.1 to 3.2.2
     $db->queryWriteIgnoreError("alter table {$prefix}" . "wfLockedOut modify column blockedTime bigint signed NOT NULL");
     $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileQueue");
     $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileChanges");
     $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}wfStatus where field = 'id'");
     if (!$result || strtolower($result->Key) != 'pri') {
         //Adding primary key to this table because some backup apps use primary key during backup.
         $db->queryWriteIgnoreError("alter table {$prefix}wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
     }
     $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_options'");
     if ($optScanEnabled != '0' && $optScanEnabled != '1') {
         $db->queryWrite("update {$prefix}" . "wfConfig set val='1' where name='scansEnabled_options'");
     }
     $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_heartbleed'");
     if ($optScanEnabled != '0' && $optScanEnabled != '1') {
         //Enable heartbleed if no value is set.
         wfConfig::set('scansEnabled_heartbleed', 1);
     }
     if (wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') {
         wfCache::removeCacheDirectoryHtaccess();
     }
     // IPv6 schema changes for 6.0.1
     $tables_with_ips = array('wfCrawlers', 'wfBadLeechers', 'wfBlockedIPLog', 'wfBlocks', 'wfHits', 'wfLeechers', 'wfLockedOut', 'wfLocs', 'wfLogins', 'wfReverseCache', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners');
     foreach ($tables_with_ips as $ip_table) {
         $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'");
         if (!$result || strtolower($result->Type) == 'binary(16)') {
             continue;
         }
         $db->queryWriteIgnoreError("ALTER TABLE {$prefix}{$ip_table} MODIFY IP BINARY(16)");
         // Just to be sure we don't corrupt the data if the alter fails.
         $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'");
         if (!$result || strtolower($result->Type) != 'binary(16)') {
             continue;
         }
         $db->queryWriteIgnoreError("UPDATE {$prefix}{$ip_table} SET IP = CONCAT(LPAD(CHAR(0xff, 0xff), 12, CHAR(0)), LPAD(\n\tCHAR(\n\t\tCAST(IP as UNSIGNED) >> 24 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 16 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 8 & 0xFF,\n\t\tCAST(IP as UNSIGNED) & 0xFF\n\t),\n\t4,\n\tCHAR(0)\n))");
     }
     // Fix the data in the country column.
     // TODO: add version check so this doesn't run on every update.
     $ip_results = $wpdb->get_results("SELECT * FROM `{$prefix}wfBlockedIPLog` GROUP BY IP");
     if ($ip_results) {
         foreach ($ip_results as $ip_row) {
             $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP)), $ip_row->IP));
         }
     }
     //Must be the final line
 }
    }
}
?>
			</tr>
			</table>
		</div>
		<table border="0" cellpadding="0" cellspacing="0"><tr>
			<td><input type="button" name="but4" class="button-primary" value="Save blocking options and country list" onclick="WFAD.saveCountryBlocking();" /></td>
			<td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr>
		</table>
		<span style="font-size: 10px;">Note that we use an IP to country database that is 99.5% accurate to identify which country a visitor is from.</span>
	</div>
</div>
<script type="text/javascript">
jQuery(function(){ WFAD.setOwnCountry('<?php 
echo wfUtils::IP2Country(wfUtils::getIP());
?>
'); });
<?php 
if (wfConfig::get('cbl_countries')) {
    ?>
jQuery(function(){ WFAD.loadBlockedCountries('<?php 
    echo wfConfig::get('cbl_countries');
    ?>
'); });
<?php 
}
?>
</script>
<script type="text/x-jquery-template" id="wfWelcomeContentCntBlk">
<div>
 public function checkForBlockedCountry()
 {
     static $hasRun;
     if (isset($hasRun)) {
         return;
     }
     $hasRun = true;
     $blockedCountries = wfConfig::get('cbl_countries', false);
     $bareRequestURI = untrailingslashit(wfUtils::extractBareURI($_SERVER['REQUEST_URI']));
     $IP = wfUtils::getIP();
     if ($country = wfUtils::IP2Country($IP)) {
         foreach (explode(',', $blockedCountries) as $blocked) {
             if (strtoupper($blocked) == strtoupper($country)) {
                 //At this point we know the user has been blocked
                 if (wfConfig::get('cbl_action') == 'redir') {
                     $redirURL = wfConfig::get('cbl_redirURL');
                     $eRedirHost = wfUtils::extractHostname($redirURL);
                     $isExternalRedir = false;
                     if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) {
                         //It's an external redirect...
                         $isExternalRedir = true;
                     }
                     if (!$isExternalRedir && untrailingslashit(wfUtils::extractBareURI($redirURL)) == $bareRequestURI) {
                         //Is this the URI we want to redirect to, then don't block it
                         //Do nothing
                         /* Uncomment the following if page components aren't loading for the page we redirect to.
                         			   Uncommenting is not recommended because it means that anyone from a blocked country
                         			   can crawl your site by sending the page blocked users are redirected to as the referer for every request.
                         			   But it's your call.
                         			} else if(wfUtils::extractBareURI($_SERVER['HTTP_REFERER']) == $redirURL){ //If the referer the page we want to redirect to? Then this might be loading as a component so don't block.
                         				//Do nothing
                         			*/
                     } else {
                         wfConfig::inc('totalCountryBlocked');
                         $this->initLogRequest();
                         $this->currentRequest->actionDescription = 'blocked access via country blocking and redirected to URL (' . wfConfig::get('cbl_redirURL') . ')';
                         $this->currentRequest->statusCode = 503;
                         if (!$this->currentRequest->action) {
                             $this->currentRequest->action = 'blocked:wordfence';
                         }
                         $this->logHit();
                         wfActivityReport::logBlockedIP($IP);
                         $this->redirect(wfConfig::get('cbl_redirURL'));
                     }
                 } else {
                     $this->currentRequest->actionDescription = 'blocked access via country blocking';
                     wfConfig::inc('totalCountryBlocked');
                     wfActivityReport::logBlockedIP($IP);
                     $this->do503(3600, "Access from your area has been temporarily limited for security reasons");
                 }
             }
         }
     }
 }
						echo "</tr><tr>\n";
					}
				}
			?>
			</tr>
			</table>
		</div>
		<table border="0" cellpadding="0" cellspacing="0"><tr>
			<td><input type="button" name="but4" class="button-primary" value="Save blocking options and country list" onclick="WFAD.saveCountryBlocking();" /></td>
			<td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr>
		</table>
		<span style="font-size: 10px;">Note that we use an IP to country database that is 99.5% accurate to identify which country a visitor is from.</span>
	</div>
</div>
<script type="text/javascript">
jQuery(function(){ WFAD.setOwnCountry('<?php echo wfUtils::IP2Country(wfUtils::getIP()); ?>'); });
<?php
if(wfConfig::get('cbl_countries')){
?>
jQuery(function(){ WFAD.loadBlockedCountries('<?php echo wfConfig::get('cbl_countries'); ?>'); });
<?php
}
?>
</script>
<script type="text/x-jquery-template" id="wfWelcomeContentCntBlk">
<div>
<h3>Premium Feature: Block or redirect countries</h3>
<strong><p>Being targeted by hackers in a specific country?</p></strong>
<p>
	The premium version of Wordfence offers country blocking.
	This uses a commercial geolocation database to block hackers, spammers