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}#x27;");
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