Wordfence Security – Firewall & Malware Scan - Version 6.3.17

Version Description

  • Improvement: Prepared code for upcoming scan improvement which will greatly increase scan performance by optimizing malware signatures.
  • Improvement: Updated the bundled GeoIP database.
  • Improvement: Better scan messaging when a publicly-reachable searchreplacedb2.php utility is found.
  • Improvement: The no-cache constant for database caching is now set for W3TC for plugin updates and scans.
  • Improvement: Added an additional home/siteurl resolution check for WPML installations.
Download this release

Release Info

Developer wfryan
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 6.3.17
Comparing to
See all releases

Code changes from version 6.3.16 to 6.3.17

lib/GeoIP.dat CHANGED
Binary file
lib/GeoIPv6.dat CHANGED
Binary file
lib/wfScanEngine.php CHANGED
@@ -630,13 +630,13 @@ class wfScanEngine {
630
  wfCommonBackupFileTest::createFromRootPath('wp-config.txt'),
631
  wfCommonBackupFileTest::createFromRootPath('wp-config.original'),
632
  wfCommonBackupFileTest::createFromRootPath('wp-config.orig'),
633
- wfCommonBackupFileTest::createFromRootPath('searchreplacedb2.php'),
634
  new wfCommonBackupFileTest(content_url('/debug.log'), WP_CONTENT_DIR . '/debug.log', array(
635
  'headers' => array(
636
  'Range' => 'bytes=0-700',
637
  ),
638
  )),
639
  );
 
640
  // $userIniFilename = ini_get('user_ini.filename');
641
  // if ($userIniFilename && $userIniFilename !== '.user.ini') {
642
  // $backupFileTests[] = wfCommonBackupFileTest::createFromRootPath($userIniFilename);
@@ -650,13 +650,13 @@ class wfScanEngine {
650
  $key = "configReadable" . bin2hex($test->getUrl());
651
  $added = $this->addIssue(
652
  'configReadable',
653
- 2,
654
  $key,
655
  $key,
656
  'Publicly accessible config, backup, or log file found: ' . esc_html($pathFromRoot),
657
  '<a href="' . $test->getUrl() . '" target="_blank" rel="noopener noreferrer">' . $test->getUrl() . '</a> is publicly
658
- accessible and may expose sensitive information about your site. Files such as this one are commonly
659
- checked for by scanners such as WPScan and should be removed or made inaccessible.',
660
  array(
661
  'url' => $test->getUrl(),
662
  'file' => $pathFromRoot,
@@ -1891,6 +1891,7 @@ class wfScanEngine {
1891
  }
1892
  }
1893
  public static function startScan($isFork = false, $scanMode = self::SCAN_MODE_FULL){
 
1894
  if(! $isFork){ //beginning of scan
1895
  wfConfig::inc('totalScansRun');
1896
  wfConfig::set('wfKillRequested', 0, wfConfig::DONT_AUTOLOAD);
@@ -2239,13 +2240,37 @@ class wfScanKnownFilesException extends Exception {
2239
  }
2240
 
2241
  class wfCommonBackupFileTest {
2242
-
 
 
2243
  /**
2244
  * @param string $path
 
 
2245
  * @return wfCommonBackupFileTest
2246
  */
2247
- public static function createFromRootPath($path) {
2248
- return new self(site_url($path), ABSPATH . $path);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2249
  }
2250
 
2251
  private $url;
@@ -2254,6 +2279,8 @@ class wfCommonBackupFileTest {
2254
  * @var array
2255
  */
2256
  private $requestArgs;
 
 
2257
  private $response;
2258
 
2259
 
@@ -2262,9 +2289,11 @@ class wfCommonBackupFileTest {
2262
  * @param string $path
2263
  * @param array $requestArgs
2264
  */
2265
- public function __construct($url, $path, $requestArgs = array()) {
2266
  $this->url = $url;
2267
  $this->path = $path;
 
 
2268
  $this->requestArgs = $requestArgs;
2269
  }
2270
 
@@ -2286,6 +2315,10 @@ class wfCommonBackupFileTest {
2286
  $contents = fread($handle, 700);
2287
  fclose($handle);
2288
  $remoteContents = substr(wp_remote_retrieve_body($this->response), 0, 700);
 
 
 
 
2289
  return $contents === $remoteContents;
2290
  }
2291
  }
630
  wfCommonBackupFileTest::createFromRootPath('wp-config.txt'),
631
  wfCommonBackupFileTest::createFromRootPath('wp-config.original'),
632
  wfCommonBackupFileTest::createFromRootPath('wp-config.orig'),
 
633
  new wfCommonBackupFileTest(content_url('/debug.log'), WP_CONTENT_DIR . '/debug.log', array(
634
  'headers' => array(
635
  'Range' => 'bytes=0-700',
636
  ),
637
  )),
638
  );
639
+ $backupFileTests = array_merge($backupFileTests, wfCommonBackupFileTest::createAllForFile('searchreplacedb2.php', wfCommonBackupFileTest::MATCH_REGEX, '/<title>Search and replace DB/i'));
640
  // $userIniFilename = ini_get('user_ini.filename');
641
  // if ($userIniFilename && $userIniFilename !== '.user.ini') {
642
  // $backupFileTests[] = wfCommonBackupFileTest::createFromRootPath($userIniFilename);
650
  $key = "configReadable" . bin2hex($test->getUrl());
651
  $added = $this->addIssue(
652
  'configReadable',
653
+ 1,
654
  $key,
655
  $key,
656
  'Publicly accessible config, backup, or log file found: ' . esc_html($pathFromRoot),
657
  '<a href="' . $test->getUrl() . '" target="_blank" rel="noopener noreferrer">' . $test->getUrl() . '</a> is publicly
658
+ accessible and may expose sensitive information about your site or allow administrative functions to be performed by anyone. Files such as this one are commonly
659
+ checked for by both attackers and scanners such as WPScan and should be removed or made inaccessible.',
660
  array(
661
  'url' => $test->getUrl(),
662
  'file' => $pathFromRoot,
1891
  }
1892
  }
1893
  public static function startScan($isFork = false, $scanMode = self::SCAN_MODE_FULL){
1894
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
1895
  if(! $isFork){ //beginning of scan
1896
  wfConfig::inc('totalScansRun');
1897
  wfConfig::set('wfKillRequested', 0, wfConfig::DONT_AUTOLOAD);
2240
  }
2241
 
2242
  class wfCommonBackupFileTest {
2243
+ const MATCH_EXACT = 'exact';
2244
+ const MATCH_REGEX = 'regex';
2245
+
2246
  /**
2247
  * @param string $path
2248
+ * @param string $mode
2249
+ * @param bool|string $matcher If $mode is MATCH_REGEX, this will be the regex pattern.
2250
  * @return wfCommonBackupFileTest
2251
  */
2252
+ public static function createFromRootPath($path, $mode = self::MATCH_EXACT, $matcher = false) {
2253
+ return new self(site_url($path), ABSPATH . $path, array(), $mode, $matcher);
2254
+ }
2255
+
2256
+ /**
2257
+ * Identical to createFromRootPath except it returns an entry for each file in the index that matches $name
2258
+ *
2259
+ * @param $name
2260
+ * @param string $mode
2261
+ * @param bool|string $matcher
2262
+ * @return array
2263
+ */
2264
+ public static function createAllForFile($file, $mode = self::MATCH_EXACT, $matcher = false) {
2265
+ global $wpdb;
2266
+ $escapedFile = esc_sql(preg_quote($file));
2267
+ $files = $wpdb->get_col("SELECT path FROM {$wpdb->base_prefix}wfKnownFileList WHERE path REGEXP '(^|/){$escapedFile}$'");
2268
+ $tests = array();
2269
+ foreach ($files as $f) {
2270
+ $tests[] = new self(site_url($f), ABSPATH . $f, array(), $mode, $matcher);
2271
+ }
2272
+
2273
+ return $tests;
2274
  }
2275
 
2276
  private $url;
2279
  * @var array
2280
  */
2281
  private $requestArgs;
2282
+ private $mode;
2283
+ private $matcher;
2284
  private $response;
2285
 
2286
 
2289
  * @param string $path
2290
  * @param array $requestArgs
2291
  */
2292
+ public function __construct($url, $path, $requestArgs = array(), $mode = self::MATCH_EXACT, $matcher = false) {
2293
  $this->url = $url;
2294
  $this->path = $path;
2295
+ $this->mode = $mode;
2296
+ $this->matcher = $matcher;
2297
  $this->requestArgs = $requestArgs;
2298
  }
2299
 
2315
  $contents = fread($handle, 700);
2316
  fclose($handle);
2317
  $remoteContents = substr(wp_remote_retrieve_body($this->response), 0, 700);
2318
+ if ($this->mode == self::MATCH_REGEX) {
2319
+ return preg_match($this->matcher, $remoteContents);
2320
+ }
2321
+ //else MATCH_EXACT
2322
  return $contents === $remoteContents;
2323
  }
2324
  }
lib/wfUtils.php CHANGED
@@ -1804,6 +1804,11 @@ class wfUtils {
1804
  return $cached_url;
1805
  }
1806
 
 
 
 
 
 
1807
  if ( empty( $blog_id ) || !is_multisite() ) {
1808
  $url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'home' LIMIT 1");
1809
  if (empty($url)) { //get_option uses siteurl instead if home is empty
@@ -1879,6 +1884,11 @@ class wfUtils {
1879
  return $cached_url;
1880
  }
1881
 
 
 
 
 
 
1882
  if ( empty( $blog_id ) || !is_multisite() ) {
1883
  $url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl' LIMIT 1");
1884
  }
1804
  return $cached_url;
1805
  }
1806
 
1807
+ if (defined('WP_HOME') && WORDFENCE_PREFER_WP_HOME_FOR_WPML) {
1808
+ $cached_url = WP_HOME;
1809
+ return $cached_url;
1810
+ }
1811
+
1812
  if ( empty( $blog_id ) || !is_multisite() ) {
1813
  $url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'home' LIMIT 1");
1814
  if (empty($url)) { //get_option uses siteurl instead if home is empty
1884
  return $cached_url;
1885
  }
1886
 
1887
+ if (defined('WP_SITEURL') && WORDFENCE_PREFER_WP_HOME_FOR_WPML) {
1888
+ $cached_url = WP_SITEURL;
1889
+ return $cached_url;
1890
+ }
1891
+
1892
  if ( empty( $blog_id ) || !is_multisite() ) {
1893
  $url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl' LIMIT 1");
1894
  }
lib/wordfenceClass.php CHANGED
@@ -376,6 +376,7 @@ class wordfence {
376
  if (function_exists('ignore_user_abort')) {
377
  ignore_user_abort(true);
378
  }
 
379
  $previous_version = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, 'wordfence_version', '0.0.0') : get_option('wordfence_version', '0.0.0'));
380
  if (is_multisite() && function_exists('update_network_option')) {
381
  update_network_option(null, 'wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
@@ -1034,6 +1035,7 @@ SQL
1034
  public static function ajax_doScan_callback(){
1035
  ignore_user_abort(true);
1036
  self::$wordfence_wp_version = false;
 
1037
  //This is messy, but not sure of a better way to do this without guaranteeing we get $wp_version
1038
  require(ABSPATH . 'wp-includes/version.php');
1039
  self::$wordfence_wp_version = $wp_version;
@@ -1992,6 +1994,7 @@ SQL
1992
  return $authUser;
1993
  }
1994
  public static function wfsnBatchReportBlockedAttempts() {
 
1995
  $threshold = wfConfig::get('lastBruteForceDataSendTime', 0);;
1996
 
1997
  $wfdb = new wfDB();
@@ -2053,6 +2056,7 @@ SQL
2053
  }
2054
  }
2055
  private static function wfsnScheduleBatchReportBlockedAttempts($timeToSend = null) {
 
2056
  if ($timeToSend === null) {
2057
  $timeToSend = time() + 30;
2058
  }
@@ -2069,11 +2073,13 @@ SQL
2069
  }
2070
  }
2071
  public static function wfsnReportBlockedAttempt($IP, $type){
 
2072
  self::wfsnScheduleBatchReportBlockedAttempts();
2073
  $endpointType = self::wfsnEndpointType();
2074
  self::getLog()->getCurrentRequest()->actionData = wfRequestModel::serializeActionData(array('type' => $endpointType));
2075
  }
2076
  public static function wfsnBatchReportFailedAttempts() {
 
2077
  $threshold = time();
2078
 
2079
  $wfdb = new wfDB();
@@ -2125,6 +2131,7 @@ SQL
2125
  }
2126
  }
2127
  private static function wfsnScheduleBatchReportFailedAttempts($timeToSend = null) {
 
2128
  if ($timeToSend === null) {
2129
  $timeToSend = time() + 30;
2130
  }
@@ -2141,6 +2148,7 @@ SQL
2141
  }
2142
  }
2143
  public static function wfsnIsBlocked($IP, $hitType){
 
2144
  $wfdb = new wfDB();
2145
  global $wpdb;
2146
  $p = $wpdb->base_prefix;
@@ -6913,6 +6921,8 @@ to your httpd.conf if using Apache, or find documentation on how to disable dire
6913
  */
6914
  public static function processAttackData() {
6915
  global $wpdb;
 
 
6916
  $waf = wfWAF::getInstance();
6917
  if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
6918
  $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
@@ -7112,6 +7122,7 @@ LIMIT %d", $lastSendTime, $limit));
7112
 
7113
  public static function syncAttackData($exit = true) {
7114
  global $wpdb;
 
7115
  $waf = wfWAF::getInstance();
7116
  $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
7117
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
376
  if (function_exists('ignore_user_abort')) {
377
  ignore_user_abort(true);
378
  }
379
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
380
  $previous_version = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, 'wordfence_version', '0.0.0') : get_option('wordfence_version', '0.0.0'));
381
  if (is_multisite() && function_exists('update_network_option')) {
382
  update_network_option(null, 'wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
1035
  public static function ajax_doScan_callback(){
1036
  ignore_user_abort(true);
1037
  self::$wordfence_wp_version = false;
1038
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
1039
  //This is messy, but not sure of a better way to do this without guaranteeing we get $wp_version
1040
  require(ABSPATH . 'wp-includes/version.php');
1041
  self::$wordfence_wp_version = $wp_version;
1994
  return $authUser;
1995
  }
1996
  public static function wfsnBatchReportBlockedAttempts() {
1997
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
1998
  $threshold = wfConfig::get('lastBruteForceDataSendTime', 0);;
1999
 
2000
  $wfdb = new wfDB();
2056
  }
2057
  }
2058
  private static function wfsnScheduleBatchReportBlockedAttempts($timeToSend = null) {
2059
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
2060
  if ($timeToSend === null) {
2061
  $timeToSend = time() + 30;
2062
  }
2073
  }
2074
  }
2075
  public static function wfsnReportBlockedAttempt($IP, $type){
2076
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
2077
  self::wfsnScheduleBatchReportBlockedAttempts();
2078
  $endpointType = self::wfsnEndpointType();
2079
  self::getLog()->getCurrentRequest()->actionData = wfRequestModel::serializeActionData(array('type' => $endpointType));
2080
  }
2081
  public static function wfsnBatchReportFailedAttempts() {
2082
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
2083
  $threshold = time();
2084
 
2085
  $wfdb = new wfDB();
2131
  }
2132
  }
2133
  private static function wfsnScheduleBatchReportFailedAttempts($timeToSend = null) {
2134
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
2135
  if ($timeToSend === null) {
2136
  $timeToSend = time() + 30;
2137
  }
2148
  }
2149
  }
2150
  public static function wfsnIsBlocked($IP, $hitType){
2151
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
2152
  $wfdb = new wfDB();
2153
  global $wpdb;
2154
  $p = $wpdb->base_prefix;
6921
  */
6922
  public static function processAttackData() {
6923
  global $wpdb;
6924
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
6925
+
6926
  $waf = wfWAF::getInstance();
6927
  if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
6928
  $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
7122
 
7123
  public static function syncAttackData($exit = true) {
7124
  global $wpdb;
7125
+ if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
7126
  $waf = wfWAF::getInstance();
7127
  $lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$wpdb->base_prefix}wfHits");
7128
  if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
lib/wordfenceConstants.php CHANGED
@@ -19,4 +19,5 @@ if (!defined('WORDFENCE_SCAN_ISSUES_PER_PAGE')) { define('WORDFENCE_SCAN_ISSUES_
19
  if (!defined('WORDFENCE_BLOCKED_IPS_PER_PAGE')) { define('WORDFENCE_BLOCKED_IPS_PER_PAGE', 100); }
20
  if (!defined('WORDFENCE_DISABLE_FILE_VIEWER')) { define('WORDFENCE_DISABLE_FILE_VIEWER', false); }
21
  if (!defined('WORDFENCE_SCAN_FAILURE_THRESHOLD')) { define('WORDFENCE_SCAN_FAILURE_THRESHOLD', 300); }
 
22
  ?>
19
  if (!defined('WORDFENCE_BLOCKED_IPS_PER_PAGE')) { define('WORDFENCE_BLOCKED_IPS_PER_PAGE', 100); }
20
  if (!defined('WORDFENCE_DISABLE_FILE_VIEWER')) { define('WORDFENCE_DISABLE_FILE_VIEWER', false); }
21
  if (!defined('WORDFENCE_SCAN_FAILURE_THRESHOLD')) { define('WORDFENCE_SCAN_FAILURE_THRESHOLD', 300); }
22
+ if (!defined('WORDFENCE_PREFER_WP_HOME_FOR_WPML')) { define('WORDFENCE_PREFER_WP_HOME_FOR_WPML', false); } //When determining the unfiltered `home` and `siteurl` with WPML installed, use WP_HOME and WP_SITEURL if set instead of the database values
23
  ?>
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.8.1
6
- Stable tag: 6.3.16
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
@@ -160,6 +160,13 @@ Secure your website with Wordfence.
160
 
161
  == Changelog ==
162
 
 
 
 
 
 
 
 
163
  = 6.3.16 =
164
  * Improvement: Introduced a new scan stage to check for malicious URLs and content within WordPress core, plugin, and theme options.
165
  * Improvement: New scan stage includes a new check for TrafficTrade malware.
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.8.1
6
+ Stable tag: 6.3.17
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
160
 
161
  == Changelog ==
162
 
163
+ = 6.3.17 =
164
+ * Improvement: Prepared code for upcoming scan improvement which will greatly increase scan performance by optimizing malware signatures.
165
+ * Improvement: Updated the bundled GeoIP database.
166
+ * Improvement: Better scan messaging when a publicly-reachable searchreplacedb2.php utility is found.
167
+ * Improvement: The no-cache constant for database caching is now set for W3TC for plugin updates and scans.
168
+ * Improvement: Added an additional home/siteurl resolution check for WPML installations.
169
+
170
  = 6.3.16 =
171
  * Improvement: Introduced a new scan stage to check for malicious URLs and content within WordPress core, plugin, and theme options.
172
  * Improvement: New scan stage includes a new check for TrafficTrade malware.
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
- Version: 6.3.16
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.3.16');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 6.3.17
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.3.17');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17