/** * @param string $binary * @return wfWAFAttackDataStorageFileEngineRow */ public static function unpack($binary) { $attackLogTime = wfWAFAttackDataStorageFileEngine::unpackMicrotime(wfWAFUtils::substr($binary, 0, 8)); $data = wfWAFAttackDataStorageFileEngine::decompress(wfWAFUtils::substr($binary, 8)); return new self($attackLogTime, $data); }
/** * @param array $matches * @return string */ private function highlightParam($matches) { $value = ''; if (is_array($this->highlightMatches)) { // preg_match_all if (is_array($this->highlightMatches[0])) { $value = $matches[3]; $replace = array(); foreach ($this->highlightMatches[0] as $key => $match) { $this->highlightMatches[0][$key] = $this->callHighlightMatchFilter($match); $replace[] = sprintf($this->highlightMatchFormat, $this->callHighlightMatchFilter($match)); } if ($replace) { $value = str_replace($this->highlightMatches[0], $replace, $value); } } else { // preg_match $param = $this->callHighlightMatchFilter($this->highlightMatches[0]); $value = str_replace($param, sprintf($this->highlightMatchFormat, $param), $matches[3]); } } if (wfWAFUtils::strlen($value) === 0) { $value = sprintf($this->highlightMatchFormat, $value); } return $matches[1] . sprintf($this->highlightParamFormat, $matches[2] . '=' . $value) . $matches[4]; }
public static function requestDetectProxyCallback($timeout = 0.01, $blocking = false) { $nonce = bin2hex(wfWAFUtils::random_bytes(32)); $callback = self::getSiteBaseURL() . '?_wfsf=detectProxy'; wfConfig::set('detectProxyNonce', $nonce, wfConfig::DONT_AUTOLOAD); wfConfig::set('detectProxyRecommendation', '', wfConfig::DONT_AUTOLOAD); $payload = array('nonce' => $nonce, 'callback' => $callback); $siteurl = ''; if (function_exists('get_bloginfo')) { if (is_multisite()) { $siteurl = network_home_url(); $siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char. } else { $siteurl = home_url(); } } wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array('action' => 'detect_proxy', 'k' => wfConfig::get('apiKey'), 's' => $siteurl, 't' => microtime(true)), null, '&'), array('body' => json_encode($payload), 'headers' => array('Content-Type' => 'application/json'), 'timeout' => $timeout, 'blocking' => $blocking)); //Asynchronous so we don't care about a response at this point. }
public function fire() { $waf = $this->getWaf(); if (!$waf) { return; } $guessSiteURL = sprintf('%s://%s/', $waf->getRequest()->getProtocol(), $waf->getRequest()->getHost()); try { $this->response = wfWAFHTTP::get(WFWAF_API_URL_SEC . "?" . http_build_query(array('action' => 'get_waf_rules', 'k' => $waf->getStorageEngine()->getConfig('apiKey'), 's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') : $guessSiteURL, 'h' => $waf->getStorageEngine()->getConfig('homeURL') ? $waf->getStorageEngine()->getConfig('homeURL') : $guessSiteURL, 'openssl' => $waf->hasOpenSSL() ? 1 : 0, 'betaFeed' => (int) $waf->getStorageEngine()->getConfig('betaThreatDefenseFeed')), null, '&')); if ($this->response) { $jsonData = wfWAFUtils::json_decode($this->response->getBody(), true); if (is_array($jsonData)) { if ($waf->hasOpenSSL() && isset($jsonData['data']['signature']) && isset($jsonData['data']['rules']) && $waf->verifySignedRequest(base64_decode($jsonData['data']['signature']), $jsonData['data']['rules'])) { $waf->updateRuleSet(base64_decode($jsonData['data']['rules']), isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true); if (array_key_exists('premiumCount', $jsonData['data'])) { $waf->getStorageEngine()->setConfig('premiumCount', $jsonData['data']['premiumCount']); } } else { if (!$waf->hasOpenSSL() && isset($jsonData['data']['hash']) && isset($jsonData['data']['rules']) && $waf->verifyHashedRequest($jsonData['data']['hash'], $jsonData['data']['rules'])) { $waf->updateRuleSet(base64_decode($jsonData['data']['rules']), isset($jsonData['data']['timestamp']) ? $jsonData['data']['timestamp'] : true); if (array_key_exists('premiumCount', $jsonData['data'])) { $waf->getStorageEngine()->setConfig('premiumCount', $jsonData['data']['premiumCount']); } } } } } } catch (wfWAFHTTPTransportException $e) { error_log($e->getMessage()); } catch (wfWAFBuildRulesException $e) { error_log($e->getMessage()); } }
<p>A potentially unsafe operation has been detected in your request to this site, and has been blocked by Wordfence.</p> <?php if ($urlParamsToWhitelist) { ?> <p>If you are an administrator and you are certain this is a false positive, you can automatically whitelist this request and repeat the same action.</p> <form id="whitelist-form" action="<?php echo htmlentities($waf->getRequest()->getPath(), ENT_QUOTES, 'utf-8'); ?> " method="post"> <input type="hidden" name="wfwaf-false-positive-params" value="<?php echo htmlentities(wfWAFUtils::json_encode($urlParamsToWhitelist), ENT_QUOTES, 'utf-8'); ?> "> <input type="hidden" name="wfwaf-false-positive-nonce" value="<?php echo htmlentities($waf->getAuthCookieValue('nonce', ''), ENT_QUOTES, 'utf-8'); ?> "> <div id="whitelist-actions"> <p> <label> <input id="verified-false-positive-checkbox" type="checkbox" name="wfwaf-false-positive-verified" value="1"> <em>I am certain this is a false positive.</em> </label>
function geoip_name_by_addr_v6($gi, $addr) { if ($addr == NULL) { return 0; } $ipnum = wfWAFUtils::inet_pton($addr); return _get_org_v6($gi, $ipnum); }
/** * @return bool|wfWAFLexerToken * @throws wfWAFParserSyntaxError */ public function nextToken() { if (!$this->scanner->eos()) { /** @var wfWAFLexerTokenMatcher $tokenMatcher */ foreach ($this->tokenMatchers as $tokenMatcher) { $this->scanner->skip('/^\\s+/s'); if ($this->scanner->eos()) { return false; } if (($this->flags & self::FLAG_TOKENIZE_MYSQL_PORTABLE_COMMENTS) === 0 && ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START || $tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END)) { continue; } if (!$this->hasPortableCommentStart && $tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) { continue; } if ($tokenMatcher->useMaximalMunch() && ($match = $this->scanner->check($tokenMatcher->getMatch())) !== null) { $biggestToken = $this->createToken($tokenMatcher->getTokenID(), $match); /** @var wfWAFLexerTokenMatcher $tokenMatcher2 */ foreach ($this->tokenMatchers as $tokenMatcher2) { if ($tokenMatcher === $tokenMatcher2) { continue; } if (($match2 = $this->scanner->check($tokenMatcher2->getMatch())) !== null) { $biggestToken2 = $this->createToken($tokenMatcher2->getTokenID(), $match2); if (wfWAFUtils::strlen($biggestToken2->getValue()) > wfWAFUtils::strlen($biggestToken->getValue())) { $biggestToken = $biggestToken2; } } } $this->scanner->advancePointer(wfWAFUtils::strlen($biggestToken->getValue())); return $biggestToken; } else { if (($match = $this->scanner->scan($tokenMatcher->getMatch())) !== null) { $token = $this->createToken($tokenMatcher->getTokenID(), $match); if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_START) { $this->hasPortableCommentStart = true; } else { if ($tokenMatcher->getTokenID() === self::MYSQL_PORTABLE_COMMENT_END) { $this->hasPortableCommentStart = false; } } return $token; } } } $char = $this->scanner->scanChar(); $e = new wfWAFParserSyntaxError(sprintf('Invalid character "%s" (\\x%02x) found on line %d, column %d', $char, ord($char), $this->scanner->getLine(), $this->scanner->getColumn())); $e->setParseLine($this->scanner->getLine()); $e->setParseColumn($this->scanner->getColumn()); throw $e; } return false; }
/** * @param string $string * @throws InvalidArgumentException */ public function setString($string) { if (!is_string($string)) { throw new InvalidArgumentException(sprintf('String expected, got [%s]', gettype($string))); } $this->setLength(wfWAFUtils::strlen($string)); $this->string = $string; $this->reset(); }
public function filePatternsMatch($subject) { $request = $this->getWAF()->getRequest(); $files = $request->getFiles(); $patterns = $this->getWAF()->getMalwareSignatures(); if (!is_array($patterns) || !is_array($files)) { return false; } foreach ($files as $file) { if ($file['name'] == (string) $subject) { $fh = @fopen($file['tmp_name'], 'r'); if (!$fh) { return false; } $totalRead = 0; $readsize = max(min(10 * 1024 * 1024, wfWAFUtils::iniSizeToBytes(ini_get('upload_max_filesize'))), 1 * 1024 * 1024); while (!feof($fh)) { $data = fread($fh, $readsize); $totalRead += strlen($data); if ($totalRead < 1) { return false; } foreach ($patterns as $rule) { if (preg_match('/(' . $rule . ')/i', $data, $matches)) { return true; } } } } } return false; }
/** * Return a set of where clauses to use in MySQL. * * @param string $column * @return false|null|string */ public function toSQL($column = 'ip') { /** @var wpdb $wpdb */ global $wpdb; $ip_string = $this->getIPString(); if (strpos($ip_string, '.') !== false && preg_match('/\\[\\d+\\-\\d+\\]/', $ip_string)) { $whiteParts = explode('.', $ip_string); $sql = "(SUBSTR({$column}, 1, 12) = LPAD(CHAR(0xff, 0xff), 12, CHAR(0)) AND "; for ($i = 0, $j = 24; $i <= 3; $i++, $j -= 8) { // MySQL can only perform bitwise operations on integers $conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, 13, 8)), 16, 10) as UNSIGNED INTEGER)', $column); if (preg_match('/^\\[(\\d+)\\-(\\d+)\\]$/', $whiteParts[$i], $m)) { $sql .= $wpdb->prepare("{$conv} >> {$j} & 0xFF BETWEEN %d AND %d", $m[1], $m[2]); } else { $sql .= $wpdb->prepare("{$conv} >> {$j} & 0xFF = %d", $whiteParts[$i]); } $sql .= ' AND '; } $sql = substr($sql, 0, -5) . ')'; return $sql; } else { if (strpos($ip_string, ':') !== false) { $ip_string = strtolower(self::expandIPv6Range($ip_string)); if (preg_match('/\\[[a-f0-9]+\\-[a-f0-9]+\\]/i', $ip_string)) { $whiteParts = explode(':', $ip_string); $sql = '('; for ($i = 0; $i <= 7; $i++) { // MySQL can only perform bitwise operations on integers $conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, %d, 8)), 16, 10) as UNSIGNED INTEGER)', $column, $i < 4 ? 1 : 9); $j = 16 * (3 - $i % 4); if (preg_match('/^\\[([a-f0-9]+)\\-([a-f0-9]+)\\]$/i', $whiteParts[$i], $m)) { $sql .= $wpdb->prepare("{$conv} >> {$j} & 0xFFFF BETWEEN 0x%x AND 0x%x", hexdec($m[1]), hexdec($m[2])); } else { $sql .= $wpdb->prepare("{$conv} >> {$j} & 0xFFFF = 0x%x", hexdec($whiteParts[$i])); } $sql .= ' AND '; } $sql = substr($sql, 0, -5) . ')'; return $sql; } } } return $wpdb->prepare("({$column} = %s)", wfWAFUtils::inet_pton($ip_string)); }
/** * @todo Implement wfWAFHTTPTransportStreams::send. * @param wfWAFHTTP $request * @return mixed * @throws wfWAFHTTPTransportException */ public function send($request) { $timeout = 5; $url = $request->getUrl(); if ($queryString = $request->getQueryString()) { if (is_array($queryString)) { $queryString = http_build_query($queryString); } $url .= (wfWAFUtils::strpos($url, '?') !== false ? '&' : '?') . $queryString; } $urlParsed = parse_url($request->getUrl()); $headers = "Host: {$urlParsed['host']}\r\n"; if ($auth = $request->getAuth()) { $headers .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\r\n"; } if ($cookies = $request->getCookies()) { if (is_array($cookies)) { $cookies = self::buildCookieString($cookies); } $headers .= "Cookie: {$cookies}\r\n"; } $hasUA = false; if ($_headers = $request->getHeaders()) { if (is_array($_headers)) { foreach ($_headers as $header => $value) { if (trim(wfWAFUtils::strtolower($header)) === 'user-agent') { $hasUA = true; } $headers .= $header . ': ' . $value . "\r\n"; } } } if (!$hasUA) { $headers .= "User-Agent: Wordfence Streams UA\r\n"; } $httpOptions = array('method' => $request->getMethod(), 'ignore_errors' => true, 'timeout' => $timeout, 'follow_location' => 1, 'max_redirects' => 5); if (wfWAFUtils::strlen($request->getBody()) > 0) { $httpOptions['content'] = $request->getBody(); $headers .= 'Content-Length: ' . wfWAFUtils::strlen($httpOptions['content']) . "\r\n"; } $httpOptions['header'] = $headers; $options = array(wfWAFUtils::strtolower($urlParsed['scheme']) => $httpOptions); $context = stream_context_create($options); $stream = fopen($request->getUrl(), 'r', false, $context); if (!is_resource($stream)) { return false; } $metaData = stream_get_meta_data($stream); // Get the HTTP response code $httpResponse = array_shift($metaData['wrapper_data']); if (preg_match_all('/(\\w+\\/\\d\\.\\d) (\\d{3})/', $httpResponse, $matches) !== false) { // $protocol = $matches[1][0]; $status = (int) $matches[2][0]; } else { // $protocol = null; $status = null; } $responseObj = new wfWAFHTTPResponse(); $responseObj->setHeaders(join("\r\n", $metaData['wrapper_data'])); $responseObj->setBody(stream_get_contents($stream)); $responseObj->setStatusCode($status); // Close the stream after use fclose($stream); return $responseObj; }
/** * @return mixed|string * @throws wfWAFRuleParserSyntaxError */ private function expectLiteral() { $expectedToken = $this->expectNextToken(); $this->expectTokenTypeInArray($expectedToken, array(wfWAFRuleLexer::T_SINGLE_STRING_LITERAL, wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL, wfWAFRuleLexer::T_IDENTIFIER, wfWAFRuleLexer::T_NUMBER_LITERAL, wfWAFRuleLexer::T_OPEN_BRACKET)); if ($expectedToken->getType() === wfWAFRuleLexer::T_SINGLE_STRING_LITERAL) { // Remove quotes, strip slashes $value = wfWAFUtils::substr($expectedToken->getValue(), 1, -1); $value = str_replace("\\'", "'", $value); } else { if ($expectedToken->getType() === wfWAFRuleLexer::T_DOUBLE_STRING_LITERAL) { // Remove quotes, strip slashes $value = wfWAFUtils::substr($expectedToken->getValue(), 1, -1); $value = str_replace('\\"', '"', $value); } else { if ($expectedToken->getType() === wfWAFRuleLexer::T_IDENTIFIER) { // Remove quotes, strip slashes $value = new wfWAFRuleVariable($this->getWAF(), $expectedToken->getValue()); } else { if ($expectedToken->getType() === wfWAFRuleLexer::T_OPEN_BRACKET) { $value = array(); while (true) { $nextToken = $this->expectNextToken(); if ($nextToken->getType() === wfWAFRuleLexer::T_CLOSE_BRACKET) { break; } if ($nextToken->getType() === wfWAFRuleLexer::T_COMMA) { continue; } $this->index--; $value[] = $this->expectLiteral(); } } else { $value = $expectedToken->getValue(); } } } } return $value; }
public function lengthLessThan($subject) { return wfWAFUtils::strlen(is_array($subject) ? join('', $subject) : (string) $subject) < $this->getExpected(); }
public function fire() { $waf = $this->getWaf(); if (!$waf) { return; } $guessSiteURL = sprintf('%s://%s/', $waf->getRequest()->getProtocol(), $waf->getRequest()->getHost()); try { $request = new wfWAFHTTP(); $request->setHeaders(array('Content-Type' => 'application/json')); $response = wfWAFHTTP::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') : $guessSiteURL, 't' => microtime(true)), null, '&'), '[]', $request); if ($response instanceof wfWAFHTTPResponse && $response->getBody()) { $jsonData = wfWAFUtils::json_decode($response->getBody(), true); if (array_key_exists('data', $jsonData) && array_key_exists('watchedIPList', $jsonData['data'])) { $waf->getStorageEngine()->setConfig('watchedIPs', $jsonData['data']['watchedIPList']); } } } catch (wfWAFHTTPTransportException $e) { error_log($e->getMessage()); } }
public static function authenticateFilter($authUser, $username, $passwd) { wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits. $IP = wfUtils::getIP(); $secEnabled = wfConfig::get('loginSecurityEnabled'); $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array()); $userDat = isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false; $checkTwoFactor = $secEnabled && !self::getLog()->isWhitelisted($IP) && wfConfig::get('isPaid') && isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0 && is_object($userDat) && get_class($userDat) == 'WP_User'; if ($checkTwoFactor) { $twoFactorRecord = false; $hasActivatedTwoFactorUser = false; foreach ($twoFactorUsers as &$t) { if ($t[3] == 'activated') { $userID = $t[0]; $testUser = get_user_by('ID', $userID); if (is_object($testUser) && wfUtils::isAdmin($testUser)) { $hasActivatedTwoFactorUser = true; } if ($userID == $userDat->ID) { $twoFactorRecord =& $t; } } } if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) { //User authenticated with name and password, 2FA code ready to check $userID = $userDat->ID; if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) { //Do nothing. This is the code path the old method of including the code in the password field will take -- since we already have a valid $authUser, skip the nonce verification portion } else { if (isset($_POST['wordfence_twoFactorNonce'])) { $twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_twoFactorNonce']); if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) { self::$authError = new WP_Error('twofactor_required', __('<strong>VERIFICATION FAILED</strong>: Two factor authentication verification failed. Please try again.')); return self::processBruteForceAttempt(self::$authError, $username, $passwd); } } else { //Code path for old method, invalid password the second time self::$authError = $authUser; if (is_wp_error(self::$authError) && (self::$authError->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || self::$authError->get_error_code() == 'incorrect_password' || $authUser->get_error_code() == 'authentication_failed') && wfConfig::get('loginSec_maskLoginErrors')) { self::$authError = new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?'), $username, wp_lostpassword_url())); } return self::processBruteForceAttempt(self::$authError, $username, $passwd); } } if (isset($twoFactorRecord[5])) { //New method TOTP $mode = $twoFactorRecord[5]; $code = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_authFactor']); $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); try { $codeResult = $api->call('twoFactorTOTP_verify', array(), array('totpid' => $twoFactorRecord[6], 'code' => $code, 'mode' => $mode)); if (isset($codeResult['notPaid']) && $codeResult['notPaid']) { //No longer a paid key, let them sign in without two factor } else { if (isset($codeResult['ok']) && $codeResult['ok']) { //Everything's good, let the sign in continue } else { if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) { //Using the old method of appending the code to the password if ($mode == 'authenticator') { self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code from your authenticator app to the end of your password (e.g., <code>wf123456</code>).')); } else { self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code sent to your phone to the end of your password (e.g., <code>wf123456</code>).')); } } else { $loginNonce = wfWAFUtils::random_bytes(20); if ($loginNonce === false) { //Should never happen but is technically possible self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.')); return self::$authError; } $loginNonce = bin2hex($loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time()); if ($mode == 'authenticator') { self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated by your authenticator app. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); } else { self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated sent to your phone. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); } } return self::processBruteForceAttempt(self::$authError, $username, $passwd); } } } catch (Exception $e) { if (self::isDebugOn()) { error_log('TOTP validation error: ' . $e->getMessage()); } } // Couldn't connect to noc1, let them sign in since the password was correct. } else { //Old method phone authentication $authFactor = $_POST['wordfence_authFactor']; if (strlen($authFactor) == 4) { $authFactor = 'wf' . $authFactor; } if ($authFactor == $twoFactorRecord[2] && $twoFactorRecord[4] > time()) { // Set this 2FA code to expire in 30 seconds (for other plugins hooking into the auth process) $twoFactorRecord[4] = time() + 30; wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); } else { if ($authFactor == $twoFactorRecord[2]) { $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); try { $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1])); if (isset($codeResult['notPaid']) && $codeResult['notPaid']) { //No longer a paid key, let them sign in without two factor } else { if (isset($codeResult['ok']) && $codeResult['ok']) { $twoFactorRecord[2] = $codeResult['code']; $twoFactorRecord[4] = time() + 1800; //30 minutes until code expires wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error. $loginNonce = wfWAFUtils::random_bytes(20); if ($loginNonce === false) { //Should never happen but is technically possible self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.')); return self::$authError; } $loginNonce = bin2hex($loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time()); self::$authError = new WP_Error('twofactor_required', __('<strong>CODE EXPIRED. CHECK YOUR PHONE:</strong> The code you entered has expired. Codes are only valid for 30 minutes for security reasons. We have sent you a new code. Please sign in using your username, password, and the new code we sent you.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); return self::$authError; } } //else: No new code was received. Let them sign in with the expired code. } catch (Exception $e) { // Couldn't connect to noc1, let them sign in since the password was correct. } } else { //Bad code, so cancel the login and return an error to user. $loginNonce = wfWAFUtils::random_bytes(20); if ($loginNonce === false) { //Should never happen but is technically possible self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.')); return self::$authError; } $loginNonce = bin2hex($loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time()); self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter your password and the code we sent to your phone. The code should start with \'wf\' and should be four characters (e.g., wfAB12).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); return self::processBruteForceAttempt(self::$authError, $username, $passwd); } } } delete_user_meta($userDat->ID, '_wf_twoFactorNonce'); delete_user_meta($userDat->ID, '_wf_twoFactorNonceTime'); $authUser = $userDat; //Log in as the user we saved in the wp_authenticate action } else { if (get_class($authUser) == 'WP_User') { //User authenticated with name and password, prompt for the 2FA code //Verify at least one administrator has 2FA enabled $requireAdminTwoFactor = $hasActivatedTwoFactorUser && wfConfig::get('loginSec_requireAdminTwoFactor'); if ($twoFactorRecord) { if ($twoFactorRecord[0] == $userDat->ID && $twoFactorRecord[3] == 'activated') { //Yup, enabled, so require the code $loginNonce = wfWAFUtils::random_bytes(20); if ($loginNonce === false) { //Should never happen but is technically possible, allow login $requireAdminTwoFactor = false; } else { $loginNonce = bin2hex($loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce); update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time()); if (isset($twoFactorRecord[5])) { //New method TOTP authentication if ($twoFactorRecord[5] == 'authenticator') { if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) { $retries = get_option('limit_login_retries', array()); $ip = limit_login_get_address(); if (!is_array($retries)) { $retries = array(); } if (isset($retries[$ip]) && is_int($retries[$ip])) { $retries[$ip]--; } else { $retries[$ip] = 0; } update_option('limit_login_retries', $retries); } $allowSeparatePrompt = ini_get('output_buffering') > 0; if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) { self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); return self::$authError; } else { self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).')); return self::$authError; } } else { //Phone TOTP $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); try { $codeResult = $api->call('twoFactorTOTP_sms', array(), array('totpid' => $twoFactorRecord[6])); if (isset($codeResult['notPaid']) && $codeResult['notPaid']) { $requireAdminTwoFactor = false; //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up. } else { if (isset($codeResult['ok']) && $codeResult['ok']) { if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) { $retries = get_option('limit_login_retries', array()); $ip = limit_login_get_address(); if (!is_array($retries)) { $retries = array(); } if (isset($retries[$ip]) && is_int($retries[$ip])) { $retries[$ip]--; } else { $retries[$ip] = 0; } update_option('limit_login_retries', $retries); } $allowSeparatePrompt = ini_get('output_buffering') > 0; if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) { self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); return self::$authError; } else { self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).')); return self::$authError; } } else { //oops, our API returned an error. $requireAdminTwoFactor = false; //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems. } } } catch (Exception $e) { if (self::isDebugOn()) { error_log('TOTP SMS error: ' . $e->getMessage()); } $requireAdminTwoFactor = false; // Couldn't connect to noc1, let them sign in since the password was correct. } } } else { //Old method phone authentication $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); try { $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1])); if (isset($codeResult['notPaid']) && $codeResult['notPaid']) { $requireAdminTwoFactor = false; //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up. } else { if (isset($codeResult['ok']) && $codeResult['ok']) { $twoFactorRecord[2] = $codeResult['code']; $twoFactorRecord[4] = time() + 1800; //30 minutes until code expires wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error. if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) { $retries = get_option('limit_login_retries', array()); $ip = limit_login_get_address(); if (!is_array($retries)) { $retries = array(); } if (isset($retries[$ip]) && is_int($retries[$ip])) { $retries[$ip]--; } else { $retries[$ip] = 0; } update_option('limit_login_retries', $retries); } $allowSeparatePrompt = ini_get('output_buffering') > 0; if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) { self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->'); return self::$authError; } else { self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password (e.g., <code>wfABCD</code>).')); return self::$authError; } } else { //oops, our API returned an error. $requireAdminTwoFactor = false; //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems. } } } catch (Exception $e) { $requireAdminTwoFactor = false; // Couldn't connect to noc1, let them sign in since the password was correct. } } //end: Old method phone authentication } } } if ($requireAdminTwoFactor && wfUtils::isAdmin($authUser)) { $username = $authUser->user_login; self::getLog()->logLogin('loginFailValidUsername', 1, $username); wordfence::alert("Admin Login Blocked", "A user with username \"{$username}\" who has administrator access tried to sign in to your WordPress site. Access was denied because all administrator accounts are required to have Cellphone Sign-in enabled but this account does not.", wfUtils::getIP()); self::$authError = new WP_Error('twofactor_disabled_required', __('<strong>Cellphone Sign-in Required</strong>: Cellphone Sign-in is required for all administrator accounts. Please contact the site administrator to enable it for your account.')); return self::$authError; } //User is not configured for two factor. Sign in without two factor. } } } //End: if ($checkTwoFactor) return self::processBruteForceAttempt($authUser, $username, $passwd); }
/** * @param mixed $subject * @return array|string */ public static function stripMagicQuotes($subject) { $sybase = ini_get('magic_quotes_sybase'); $sybaseEnabled = is_numeric($sybase) && $sybase || is_string($sybase) && $sybase && !in_array(wfWAFUtils::strtolower($sybase), array('off', 'false')); if (function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc() || $sybaseEnabled) { return self::stripslashes_deep($subject); } return $subject; }
/** * @param string $addr Should be in dot or colon notation (127.0.0.1 or ::1) * @return bool */ private function _isPrivateIP($ip) { // Run this through the preset list for IPv4 addresses. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { $wordfenceLib = realpath(dirname(__FILE__) . '/../lib'); include $wordfenceLib . '/wfIPWhitelist.php'; // defines $wfIPWhitelist $private = $wfIPWhitelist['private']; foreach ($private as $a) { if (wfWAFUtils::subnetContainsIP($a, $ip)) { return true; } } } return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; }
protected function checkForWhitelisted($ip) { $wordfenceLib = realpath(dirname(__FILE__) . '/../lib'); include $wordfenceLib . '/wfIPWhitelist.php'; // defines $wfIPWhitelist foreach ($wfIPWhitelist as $group) { foreach ($group as $subnet) { if ($subnet instanceof wfWAFUserIPRange) { //Not currently reached if ($subnet->isIPInRange($ip)) { return true; } } elseif (wfWAFUtils::subnetContainsIP($subnet, $ip)) { return true; } } } return false; }