Wordfence Security – Firewall & Malware Scan - Version 6.3.6

Version Description

  • Improvement: Optimized the malware signature scan to reduce memory usage.
  • Improvement: Optimized the overall scan to make fewer network calls.
  • Improvement: Running an update now automatically dismisses the corresponding scan issue if present.
  • Improvement: Added a time limit to the live activity status so only current messages are shown.
  • Improvement: WAF configuration files are now excluded by default from the recently modified files list in the activity report.
  • Improvement: Background pausing for live activity and traffic may now be disabled.
  • Improvement: Added additional WAF support to allow us to more easily address false positives.
  • Improvement: Blocking pages presented by Wordfence now indicate the source and contain information to help diagnose caching problems.
  • Fix: All external URLs in the tour are now https.
  • Fix: Corrected a typo in the unlock email template.
  • Fix: Fixed the target of a label on the options page.
Download this release

Release Info

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

Code changes from version 6.3.5 to 6.3.6

js/admin.js CHANGED
@@ -511,7 +511,7 @@
511
}, parseInt(WordfenceAdminVars.actUpdateInterval));
512
},
513
updateActivityLog: function() {
514
- if (this.activityLogUpdatePending || !this.windowHasFocus()) {
515
if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.activityLogUpdatePending) {
516
jQuery('body').addClass('wordfenceLiveActivityPaused');
517
}
@@ -700,7 +700,7 @@
700
}
701
},
702
updateTicker: function(forceUpdate) {
703
- if ((!forceUpdate) && (this.tickerUpdatePending || !this.windowHasFocus())) {
704
if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.tickerUpdatePending) {
705
jQuery('body').addClass('wordfenceLiveActivityPaused');
706
}
511
}, parseInt(WordfenceAdminVars.actUpdateInterval));
512
},
513
updateActivityLog: function() {
514
+ if (this.activityLogUpdatePending || (!this.windowHasFocus() && WordfenceAdminVars.allowsPausing == '1')) {
515
if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.activityLogUpdatePending) {
516
jQuery('body').addClass('wordfenceLiveActivityPaused');
517
}
700
}
701
},
702
updateTicker: function(forceUpdate) {
703
+ if ((!forceUpdate) && (this.tickerUpdatePending || (!this.windowHasFocus() && WordfenceAdminVars.allowsPausing == '1'))) {
704
if (!jQuery('body').hasClass('wordfenceLiveActivityPaused') && !this.tickerUpdatePending) {
705
jQuery('body').addClass('wordfenceLiveActivityPaused');
706
}
js/tourTip.js CHANGED
@@ -143,7 +143,7 @@ window['wordfenceTour'] = {
143
144
jQuery(function(){
145
if(WordfenceAdminVars.tourClosed != '1' && WordfenceAdminVars.welcomeClosed != '1'){
146
- var formHTML = '<div style="padding: 0 5px 0 15px;" id="wordfenceEmailDiv"><form target="_new" style="display: inline;" method="post" class="af-form-wrapper" action="http://www.aweber.com/scripts/addlead.pl" ><div style="display: none;"><input type="hidden" name="meta_web_form_id" value="1428034071" /><input type="hidden" name="meta_split_id" value="" /><input type="hidden" name="listname" value="wordfence" /><input type="hidden" name="redirect" value="http://www.aweber.com/thankyou-coi.htm?m=text" id="redirect_ae9f0882518768f447c80ea8f3b7afde" /><input type="hidden" name="meta_adtracking" value="widgetForm" /><input type="hidden" name="meta_message" value="1" /><input type="hidden" name="meta_required" value="email" /><input type="hidden" name="meta_tooltip" value="" /></div><input class="text" id="wfListEmail" type="text" name="email" value="Enter your email" tabindex="500" onclick="wordfenceTour.wfClearEmailField(); return false;" /><input name="submit" type="submit" value="Get Alerted" tabindex="501" onclick="var evt = event || window.event; try { return wordfenceTour.processEmailClick(evt); } catch(err){ evt.returnValue = false; evt.preventDefault(); }" /><div style="display: none;"><img src="http://forms.aweber.com/form/displays.htm?id=jCxMHAzMLAzsjA==" alt="" /></div><div style="padding: 5px; font-size: 10px;"><input type="checkbox" id="wfJoinListCheck" value="1" checked /><span style="font-size: 10px;">Also join our WordPress Security email list to receive WordPress Security Alerts and Wordfence news.</span></div></form></div>';
147
var elem = '#toplevel_page_Wordfence';
148
jQuery(elem).pointer({
149
close: function(){},
143
144
jQuery(function(){
145
if(WordfenceAdminVars.tourClosed != '1' && WordfenceAdminVars.welcomeClosed != '1'){
146
+ var formHTML = '<div style="padding: 0 5px 0 15px;" id="wordfenceEmailDiv"><form target="_new" style="display: inline;" method="post" class="af-form-wrapper" action="https://www.aweber.com/scripts/addlead.pl" ><div style="display: none;"><input type="hidden" name="meta_web_form_id" value="1428034071" /><input type="hidden" name="meta_split_id" value="" /><input type="hidden" name="listname" value="wordfence" /><input type="hidden" name="redirect" value="https://www.aweber.com/thankyou-coi.htm?m=text" id="redirect_ae9f0882518768f447c80ea8f3b7afde" /><input type="hidden" name="meta_adtracking" value="widgetForm" /><input type="hidden" name="meta_message" value="1" /><input type="hidden" name="meta_required" value="email" /><input type="hidden" name="meta_tooltip" value="" /></div><input class="text" id="wfListEmail" type="text" name="email" value="Enter your email" tabindex="500" onclick="wordfenceTour.wfClearEmailField(); return false;" /><input name="submit" type="submit" value="Get Alerted" tabindex="501" onclick="var evt = event || window.event; try { return wordfenceTour.processEmailClick(evt); } catch(err){ evt.returnValue = false; evt.preventDefault(); }" /><div style="display: none;"><img src="https://forms.aweber.com/form/displays.htm?id=jCxMHAzMLAzsjA==" alt="" /></div><div style="padding: 5px; font-size: 10px;"><input type="checkbox" id="wfJoinListCheck" value="1" checked /><span style="font-size: 10px;">Also join our WordPress Security email list to receive WordPress Security Alerts and Wordfence news.</span></div></form></div>';
147
var elem = '#toplevel_page_Wordfence';
148
jQuery(elem).pointer({
149
close: function(){},
lib/dashboard.php CHANGED
@@ -27,7 +27,6 @@
27
<?php if(wfConfig::get('scheduledScansEnabled')){ ?>
28
<?php if(wfConfig::get('scheduledScansEnabled')){ ?><tr><td style="padding-right: 20px;">Security Scans Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
29
<?php if(wfConfig::get('scansEnabled_public')){ ?><tr><td style="padding-right: 20px;">Scan public facing site:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
30
- <?php if(wfConfig::get('scansEnabled_heartbleed')){ ?><tr><td style="padding-right: 20px;">Scan for HeartBleed Vulnerability:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
31
<?php if(wfConfig::get('scansEnabled_core')){ ?><tr><td style="padding-right: 20px;">Scan Core Files:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
32
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
33
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
27
<?php if(wfConfig::get('scheduledScansEnabled')){ ?>
28
<?php if(wfConfig::get('scheduledScansEnabled')){ ?><tr><td style="padding-right: 20px;">Security Scans Enabled:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
29
<?php if(wfConfig::get('scansEnabled_public')){ ?><tr><td style="padding-right: 20px;">Scan public facing site:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
30
<?php if(wfConfig::get('scansEnabled_core')){ ?><tr><td style="padding-right: 20px;">Scan Core Files:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
31
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
32
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">&#10004;</td></tr> <?php } ?>
lib/live_activity.php CHANGED
@@ -4,6 +4,8 @@
4
<div class="wf-live-activity-title">Wordfence Live Activity:</div>
5
<div class="wf-live-activity-message"></div>
6
</div>
7
<div class="wf-live-activity-state"><p>Live Updates Paused &mdash; Click inside window to resume</p></div>
8
</div>
9
</div>
4
<div class="wf-live-activity-title">Wordfence Live Activity:</div>
5
<div class="wf-live-activity-message"></div>
6
</div>
7
+ <?php if (wfConfig::get('liveActivityPauseEnabled')): ?>
8
<div class="wf-live-activity-state"><p>Live Updates Paused &mdash; Click inside window to resume</p></div>
9
+ <?php endif; ?>
10
</div>
11
</div>
lib/menu_activity.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php if (wfConfig::liveTrafficEnabled()): ?>
2
<div id="wfLiveTrafficOverlayAnchor"></div>
3
<div id="wfLiveTrafficDisabledMessage">
4
<h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
@@ -194,13 +194,13 @@
194
<div id="wf-lt-listings" data-bind="foreach: listings">
195
<div data-bind="attr: { id: ('wfActEvent_' + id()), 'class': cssClasses }">
196
<div>
197
- <span data-bind="if: action() != 'loginOK' && user()">
198
<span data-bind="html: user.avatar" class="wfAvatar"></span>
199
<a data-bind="attr: { href: user.editLink }, text: user().display_name"
200
target="_blank"></a>
201
</span>
202
<span data-bind="if: loc()">
203
- <span data-bind="if: action() != 'loginOK' && user()"> in</span>
204
<img data-bind="attr: { src: '<?php echo wfUtils::getBaseURL() . 'images/flags/'; ?>' + loc().countryCode.toLowerCase() + '.png',
205
alt: loc().countryName, title: loc().countryName }" width="16"
206
height="11"
@@ -211,7 +211,7 @@
211
</span>
212
<span data-bind="if: !loc()">
213
<span
214
- data-bind="text: action() != 'loginOK' && user() ? 'at an' : 'An'"></span> unknown location at IP <a
215
data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }"
216
target="_blank"></a>
217
</span>
1
+ <?php if (wfConfig::liveTrafficEnabled() && wfConfig::get('liveActivityPauseEnabled')): ?>
2
<div id="wfLiveTrafficOverlayAnchor"></div>
3
<div id="wfLiveTrafficDisabledMessage">
4
<h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
194
<div id="wf-lt-listings" data-bind="foreach: listings">
195
<div data-bind="attr: { id: ('wfActEvent_' + id()), 'class': cssClasses }">
196
<div>
197
+ <span data-bind="if: action() != 'loginOK' && action() != 'loginFailValidUsername' && action() != 'loginFailInvalidUsername' && user()">
198
<span data-bind="html: user.avatar" class="wfAvatar"></span>
199
<a data-bind="attr: { href: user.editLink }, text: user().display_name"
200
target="_blank"></a>
201
</span>
202
<span data-bind="if: loc()">
203
+ <span data-bind="if: action() != 'loginOK' && action() != 'loginFailValidUsername' && action() != 'loginFailInvalidUsername' && user()"> in</span>
204
<img data-bind="attr: { src: '<?php echo wfUtils::getBaseURL() . 'images/flags/'; ?>' + loc().countryCode.toLowerCase() + '.png',
205
alt: loc().countryName, title: loc().countryName }" width="16"
206
height="11"
211
</span>
212
<span data-bind="if: !loc()">
213
<span
214
+ data-bind="text: action() != 'loginOK' && action() != 'loginFailValidUsername' && action() != 'loginFailInvalidUsername' && user() ? 'at an' : 'An'"></span> unknown location at IP <a
215
data-bind="text: IP, attr: { href: WFAD.makeIPTrafLink(IP()) }"
216
target="_blank"></a>
217
</span>
lib/menu_options.php CHANGED
@@ -400,10 +400,6 @@ $w = new wfConfig();
400
'id' => 'scansEnabled_checkHowGetIPs',
401
'label' => 'Scan for misconfigured How does Wordfence get IPs <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_misconfigured_How_does_Wordfence_get_IPs" target="_blank" class="wfhelp"></a>',
402
),
403
- array(
404
- 'id' => 'scansEnabled_heartbleed',
405
- 'label' => 'Scan for the HeartBleed vulnerability <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability" target="_blank" class="wfhelp"></a>',
406
- ),
407
array(
408
'id' => 'scansEnabled_checkReadableConfig',
409
'label' => 'Scan for publicly accessible configuration, backup, or log files <a href="http://docs.wordfence.com/en/Wordfence_options#Configuration_Readable" target="_blank" class="wfhelp"></a>',
@@ -648,7 +644,7 @@ $w = new wfConfig();
648
<div class="wfMarker" id="wfMarkerLoginSecurity"></div>
649
<h3>Login Security Options <a href="http://docs.wordfence.com/en/Wordfence_options#Login_Security_Options" target="_blank" class="wfhelp"></a></h3>
650
<div class="wf-form-group">
651
- <label for="blockedTime" class="wf-col-sm-5 wf-control-label">Enforce strong passwords <a href="http://docs.wordfence.com/en/Wordfence_options#Enforce_strong_passwords.3F" target="_blank" class="wfhelp"></a></label>
652
<div class="wf-col-sm-7">
653
<select class="wf-form-control" id="loginSec_strongPasswds" name="loginSec_strongPasswds">
654
<option value="">Do not force users to use strong passwords</option>
@@ -939,6 +935,10 @@ $w = new wfConfig();
939
</div>
940
<?php
941
$options = array( //Contents should already be HTML-escaped as needed
942
array(
943
'id' => 'deleteTablesOnDeact',
944
'label' => 'Delete Wordfence tables and data on deactivation <a href="http://docs.wordfence.com/en/Wordfence_options#Delete_Wordfence_tables_and_data_on_deactivation.3F" target="_blank" class="wfhelp"></a>',
400
'id' => 'scansEnabled_checkHowGetIPs',
401
'label' => 'Scan for misconfigured How does Wordfence get IPs <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_misconfigured_How_does_Wordfence_get_IPs" target="_blank" class="wfhelp"></a>',
402
),
403
array(
404
'id' => 'scansEnabled_checkReadableConfig',
405
'label' => 'Scan for publicly accessible configuration, backup, or log files <a href="http://docs.wordfence.com/en/Wordfence_options#Configuration_Readable" target="_blank" class="wfhelp"></a>',
644
<div class="wfMarker" id="wfMarkerLoginSecurity"></div>
645
<h3>Login Security Options <a href="http://docs.wordfence.com/en/Wordfence_options#Login_Security_Options" target="_blank" class="wfhelp"></a></h3>
646
<div class="wf-form-group">
647
+ <label for="loginSec_strongPasswds" class="wf-col-sm-5 wf-control-label">Enforce strong passwords <a href="http://docs.wordfence.com/en/Wordfence_options#Enforce_strong_passwords.3F" target="_blank" class="wfhelp"></a></label>
648
<div class="wf-col-sm-7">
649
<select class="wf-form-control" id="loginSec_strongPasswds" name="loginSec_strongPasswds">
650
<option value="">Do not force users to use strong passwords</option>
935
</div>
936
<?php
937
$options = array( //Contents should already be HTML-escaped as needed
938
+ array(
939
+ 'id' => 'liveActivityPauseEnabled',
940
+ 'label' => 'Pause live updates when window loses focus <a href="http://docs.wordfence.com/en/Wordfence_options#Pause_live_updates_when_window_loses_focus" target="_blank" class="wfhelp"></a>',
941
+ ),
942
array(
943
'id' => 'deleteTablesOnDeact',
944
'label' => 'Delete Wordfence tables and data on deactivation <a href="http://docs.wordfence.com/en/Wordfence_options#Delete_Wordfence_tables_and_data_on_deactivation.3F" target="_blank" class="wfhelp"></a>',
lib/menu_scan.php CHANGED
@@ -1,10 +1,12 @@
1
<?php
2
$sigUpdateTime = wfConfig::get('signatureUpdateTime');
3
?>
4
<div id="wfLiveTrafficOverlayAnchor"></div>
5
<div id="wfLiveTrafficDisabledMessage">
6
<h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
7
</div>
8
<div class="wrap wordfence">
9
<div class="wf-container-fluid">
10
1
<?php
2
$sigUpdateTime = wfConfig::get('signatureUpdateTime');
3
?>
4
+ <?php if (wfConfig::get('liveActivityPauseEnabled')): ?>
5
<div id="wfLiveTrafficOverlayAnchor"></div>
6
<div id="wfLiveTrafficDisabledMessage">
7
<h2>Live Updates Paused<br /><small>Click inside window to resume</small></h2>
8
</div>
9
+ <?php endif; ?>
10
<div class="wrap wordfence">
11
<div class="wf-container-fluid">
12
lib/menu_scan_options.php CHANGED
@@ -15,10 +15,6 @@ $w = new wfConfig();
15
'id' => 'scansEnabled_checkHowGetIPs',
16
'label' => 'Scan for misconfigured How does Wordfence get IPs <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_misconfigured_How_does_Wordfence_get_IPs" target="_blank" class="wfhelp"></a>',
17
),
18
- array(
19
- 'id' => 'scansEnabled_heartbleed',
20
- 'label' => 'Scan for the HeartBleed vulnerability <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability" target="_blank" class="wfhelp"></a>',
21
- ),
22
array(
23
'id' => 'scansEnabled_checkReadableConfig',
24
'label' => 'Scan for publicly accessible configuration, backup, or log files <a href="http://docs.wordfence.com/en/Wordfence_options#Configuration_Readable" target="_blank" class="wfhelp"></a>',
15
'id' => 'scansEnabled_checkHowGetIPs',
16
'label' => 'Scan for misconfigured How does Wordfence get IPs <a href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_misconfigured_How_does_Wordfence_get_IPs" target="_blank" class="wfhelp"></a>',
17
),
18
array(
19
'id' => 'scansEnabled_checkReadableConfig',
20
'label' => 'Scan for publicly accessible configuration, backup, or log files <a href="http://docs.wordfence.com/en/Wordfence_options#Configuration_Readable" target="_blank" class="wfhelp"></a>',
lib/wf503.php CHANGED
@@ -20,5 +20,5 @@ still benefit from the other security features that Wordfence provides.
20
<?php require('wfUnlockMsg.php'); ?>
21
22
</p>
23
- <address>This response was generated by Wordfence.</address>
24
</body></html>
20
<?php require('wfUnlockMsg.php'); ?>
21
22
</p>
23
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
24
</body></html>
lib/wfAPI.php CHANGED
@@ -98,13 +98,27 @@ class wfAPI {
98
$error_message = $response->get_error_message();
99
throw new Exception("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the the Wordfence scanning servers" . ($error_message ? ": $error_message" : '.'));
100
}
101
102
if (!empty($response['response']['code'])) {
103
$this->lastHTTPStatus = (int) $response['response']['code'];
104
}
105
106
if (200 != $this->lastHTTPStatus) {
107
- throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [$this->lastHTTPStatus]");
108
}
109
110
$this->curlContent = wp_remote_retrieve_body($response);
98
$error_message = $response->get_error_message();
99
throw new Exception("There was an " . ($error_message ? '' : 'unknown ') . "error connecting to the the Wordfence scanning servers" . ($error_message ? ": $error_message" : '.'));
100
}
101
+
102
+ $dateHeader = @$response['headers']['date'];
103
+ if (!empty($dateHeader) && (time() - wfConfig::get('timeoffset_wf_updated', 0) > 3600)) {
104
+ if (function_exists('date_create_from_format')) {
105
+ $dt = DateTime::createFromFormat('D, j M Y G:i:s O', $dateHeader);
106
+ $timestamp = $dt->getTimestamp();
107
+ }
108
+ else {
109
+ $timestamp = strtotime($dateHeader);
110
+ }
111
+ $offset = $timestamp - time();
112
+ wfConfig::set('timeoffset_wf', $offset);
113
+ wfConfig::set('timeoffset_wf_updated', time());
114
+ }
115
116
if (!empty($response['response']['code'])) {
117
$this->lastHTTPStatus = (int) $response['response']['code'];
118
}
119
120
if (200 != $this->lastHTTPStatus) {
121
+ throw new Exception("The Wordfence scanning servers are currently unavailable. This may be for maintenance or a temporary outage. If this still occurs in an hour, please contact support. [$this->lastHTTPStatus]");
122
}
123
124
$this->curlContent = wp_remote_retrieve_body($response);
lib/wfConfig.php CHANGED
@@ -35,7 +35,6 @@ class wfConfig {
35
"scheduledScansEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
36
"lowResourceScansEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD),
37
"scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD),
38
- "scansEnabled_heartbleed" => array('value' => true, 'autoload' => self::AUTOLOAD),
39
"scansEnabled_checkHowGetIPs" => array('value' => true, 'autoload' => self::AUTOLOAD),
40
"scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD),
41
"scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD),
@@ -57,6 +56,7 @@ class wfConfig {
57
"scansEnabled_highSense" => array('value' => false, 'autoload' => self::AUTOLOAD),
58
"scansEnabled_oldVersions" => array('value' => true, 'autoload' => self::AUTOLOAD),
59
"scansEnabled_suspiciousAdminUsers" => array('value' => true, 'autoload' => self::AUTOLOAD),
60
"firewallEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
61
"blockFakeBots" => array('value' => false, 'autoload' => self::AUTOLOAD),
62
"autoBlockScanners" => array('value' => true, 'autoload' => self::AUTOLOAD),
@@ -122,7 +122,7 @@ class wfConfig {
122
'maxScanHits_action' => "throttle",
123
'blockedTime' => "300",
124
'email_summary_interval' => 'weekly',
125
- 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/plugins/wordfence/tmp',
126
'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png\n/browserconfig.xml",
127
'wafAlertWhitelist' => '',
128
'wafAlertInterval' => 600,
@@ -130,7 +130,7 @@ class wfConfig {
130
'howGetIPs_trusted_proxies' => '',
131
)
132
);
133
- public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs', 'vulnerabilities_plugin', 'vulnerabilities_theme', 'dashboardData');
134
public static function setDefaults() {
135
foreach (self::$defaultConfig['checkboxes'] as $key => $config) {
136
$val = $config['value'];
35
"scheduledScansEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
36
"lowResourceScansEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD),
37
"scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD),
38
"scansEnabled_checkHowGetIPs" => array('value' => true, 'autoload' => self::AUTOLOAD),
39
"scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD),
40
"scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD),
56
"scansEnabled_highSense" => array('value' => false, 'autoload' => self::AUTOLOAD),
57
"scansEnabled_oldVersions" => array('value' => true, 'autoload' => self::AUTOLOAD),
58
"scansEnabled_suspiciousAdminUsers" => array('value' => true, 'autoload' => self::AUTOLOAD),
59
+ "liveActivityPauseEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
60
"firewallEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
61
"blockFakeBots" => array('value' => false, 'autoload' => self::AUTOLOAD),
62
"autoBlockScanners" => array('value' => true, 'autoload' => self::AUTOLOAD),
122
'maxScanHits_action' => "throttle",
123
'blockedTime' => "300",
124
'email_summary_interval' => 'weekly',
125
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wflogs',
126
'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png\n/browserconfig.xml",
127
'wafAlertWhitelist' => '',
128
'wafAlertInterval' => 600,
130
'howGetIPs_trusted_proxies' => '',
131
)
132
);
133
+ public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs', 'vulnerabilities_plugin', 'vulnerabilities_theme', 'dashboardData', 'malwarePrefixes');
134
public static function setDefaults() {
135
foreach (self::$defaultConfig['checkboxes'] as $key => $config) {
136
$val = $config['value'];
lib/wfIssues.php CHANGED
@@ -6,33 +6,49 @@ class wfIssues {
6
//Properties that are serialized on sleep:
7
private $updateCalled = false;
8
private $issuesTable = '';
9
private $maxIssues = 0;
10
private $newIssues = array();
11
public $totalIssues = 0;
12
public $totalCriticalIssues = 0;
13
public $totalWarningIssues = 0;
14
public function __sleep(){ //Same order here as vars above
15
- return array('updateCalled', 'issuesTable', 'maxIssues', 'newIssues', 'totalIssues', 'totalCriticalIssues', 'totalWarningIssues');
16
}
17
public function __construct(){
18
global $wpdb;
19
$this->issuesTable = $wpdb->base_prefix . 'wfIssues';
20
$this->maxIssues = wfConfig::get('scan_maxIssues', 0);
21
}
22
public function __wakeup(){
23
$this->db = new wfDB();
24
}
25
- public function addIssue($type, $severity,
26
-
27
$ignoreP, /* some piece of data used for md5 for permanent ignores */
28
$ignoreC, /* some piece of data used for md5 for ignoring until something changes */
29
- $shortMsg, $longMsg, $templateData
30
){
31
-
32
-
33
- $ignoreP = md5($ignoreP);
34
- $ignoreC = md5($ignoreC);
35
- $rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from " . $this->issuesTable . " where (ignoreP='%s' OR ignoreC='%s')", $ignoreP, $ignoreC);
36
if($rec){
37
if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){
38
if($type != 'file' && $type != 'database'){ //Filter out duplicate new issues but not infected files because we want to see all infections even if file contents are identical
@@ -43,27 +59,30 @@ class wfIssues {
43
if($rec['status'] == 'ignoreC' && $rec['ignoreC'] == $ignoreC){ return false; }
44
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return false; }
45
}
46
-
47
- if($severity == 1){
48
- $this->totalCriticalIssues++;
49
- } else if($severity == 2){
50
- $this->totalWarningIssues++;
51
- }
52
- $this->totalIssues++;
53
- if (empty($this->maxIssues) || $this->totalIssues <= $this->maxIssues)
54
- {
55
- $this->newIssues[] = array(
56
- 'type' => $type,
57
- 'severity' => $severity,
58
- 'ignoreP' => $ignoreP,
59
- 'ignoreC' => $ignoreC,
60
- 'shortMsg' => $shortMsg,
61
- 'longMsg' => $longMsg,
62
- 'tmplData' => $templateData
63
- );
64
}
65
66
- $this->getDB()->queryWrite("insert into " . $this->issuesTable . " (time, status, type, severity, ignoreP, ignoreC, shortMsg, longMsg, data) values (unix_timestamp(), '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s')",
67
'new',
68
$type,
69
$severity,
@@ -212,9 +231,76 @@ class wfIssues {
212
}
213
return $ret; //array of lists of issues by status
214
}
215
public function getIssueCount() {
216
return (int) $this->getDB()->querySingle("select COUNT(*) from " . $this->issuesTable . " WHERE status = 'new'");
217
}
218
public function updateSummaryItem($key, $val){
219
$arr = wfConfig::get_ser('wf_summaryItems', array());
220
$arr[$key] = $val;
6
//Properties that are serialized on sleep:
7
private $updateCalled = false;
8
private $issuesTable = '';
9
+ private $pendingIssuesTable = '';
10
private $maxIssues = 0;
11
private $newIssues = array();
12
public $totalIssues = 0;
13
public $totalCriticalIssues = 0;
14
public $totalWarningIssues = 0;
15
public function __sleep(){ //Same order here as vars above
16
+ return array('updateCalled', 'issuesTable', 'pendingIssuesTable', 'maxIssues', 'newIssues', 'totalIssues', 'totalCriticalIssues', 'totalWarningIssues');
17
}
18
public function __construct(){
19
global $wpdb;
20
$this->issuesTable = $wpdb->base_prefix . 'wfIssues';
21
+ $this->pendingIssuesTable = $wpdb->base_prefix . 'wfPendingIssues';
22
$this->maxIssues = wfConfig::get('scan_maxIssues', 0);
23
}
24
public function __wakeup(){
25
$this->db = new wfDB();
26
}
27
+ public function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData, $alreadyHashed = false) {
28
+ $this->_addIssue('issue', $type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData, $alreadyHashed);
29
+ }
30
+ public function addPendingIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData) {
31
+ $this->_addIssue('pending', $type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
32
+ }
33
+ private function _addIssue($group, $type, $severity,
34
$ignoreP, /* some piece of data used for md5 for permanent ignores */
35
$ignoreC, /* some piece of data used for md5 for ignoring until something changes */
36
+ $shortMsg, $longMsg, $templateData, $alreadyHashed = false
37
){
38
+
39
+ if ($group == 'pending') {
40
+ $table = $this->pendingIssuesTable;
41
+ }
42
+ else {
43
+ $table = $this->issuesTable;
44
+ }
45
+
46
+ if (!$alreadyHashed) {
47
+ $ignoreP = md5($ignoreP);
48
+ $ignoreC = md5($ignoreC);
49
+ }
50
+
51
+ $rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from {$this->issuesTable} where (ignoreP = '%s' OR ignoreC = '%s')", $ignoreP, $ignoreC);
52
if($rec){
53
if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){
54
if($type != 'file' && $type != 'database'){ //Filter out duplicate new issues but not infected files because we want to see all infections even if file contents are identical
59
if($rec['status'] == 'ignoreC' && $rec['ignoreC'] == $ignoreC){ return false; }
60
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return false; }
61
}
62
+
63
+ if ($group != 'pending') {
64
+ if ($severity == 1) {
65
+ $this->totalCriticalIssues++;
66
+ }
67
+ else if ($severity == 2) {
68
+ $this->totalWarningIssues++;
69
+ }
70
+ $this->totalIssues++;
71
+ if (empty($this->maxIssues) || $this->totalIssues <= $this->maxIssues)
72
+ {
73
+ $this->newIssues[] = array(
74
+ 'type' => $type,
75
+ 'severity' => $severity,
76
+ 'ignoreP' => $ignoreP,
77
+ 'ignoreC' => $ignoreC,
78
+ 'shortMsg' => $shortMsg,
79
+ 'longMsg' => $longMsg,
80
+ 'tmplData' => $templateData
81
+ );
82
+ }
83
}
84
85
+ $this->getDB()->queryWrite("insert into {$table} (time, status, type, severity, ignoreP, ignoreC, shortMsg, longMsg, data) values (unix_timestamp(), '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s')",
86
'new',
87
$type,
88
$severity,
231
}
232
return $ret; //array of lists of issues by status
233
}
234
+ public function getPendingIssues($offset = 0, $limit = 100){
235
+ /** @var wpdb $wpdb */
236
+ global $wpdb;
237
+ $issues = $this->getDB()->querySelect("SELECT * FROM {$this->pendingIssuesTable} ORDER BY id ASC LIMIT %d,%d", $offset, $limit);
238
+ foreach($issues as &$i){
239
+ $i['data'] = unserialize($i['data']);
240
+ }
241
+ return $issues;
242
+ }
243
public function getIssueCount() {
244
return (int) $this->getDB()->querySingle("select COUNT(*) from " . $this->issuesTable . " WHERE status = 'new'");
245
}
246
+ public function getPendingIssueCount() {
247
+ return (int) $this->getDB()->querySingle("select COUNT(*) from " . $this->pendingIssuesTable . " WHERE status = 'new'");
248
+ }
249
+ public function reconcileUpgradeIssues($report = null, $useCachedValued = false) {
250
+ if ($report === null) {
251
+ $report = new wfActivityReport();
252
+ }
253
+
254
+ $updatesNeeded = $report->getUpdatesNeeded($useCachedValued);
255
+ if ($updatesNeeded) {
256
+ if (!$updatesNeeded['core']) {
257
+ $this->getDB()->queryWrite("DELETE FROM {$this->issuesTable} WHERE status = 'new' AND type = 'wfUpgrade'");
258
+ }
259
+
260
+ if ($updatesNeeded['plugins']) {
261
+ $upgradeNames = array();
262
+ foreach ($updatesNeeded['plugins'] as $p) {
263
+ $name = $p['Name'];
264
+ $upgradeNames[$name] = 1;
265
+ }
266
+ $upgradeIssues = $this->getDB()->querySelect("SELECT * FROM {$this->issuesTable} WHERE status = 'new' AND type = 'wfPluginUpgrade'");
267
+ foreach ($upgradeIssues as $issue) {
268
+ $data = unserialize($issue['data']);
269
+ $name = $data['Name'];
270
+ if (!isset($upgradeNames[$name])) { //Some plugins don't have a slug associated with them, so we anchor on the name
271
+ $this->deleteIssue($issue['id']);
272
+ }
273
+ }
274
+ }
275
+ else {
276
+ $this->getDB()->queryWrite("DELETE FROM {$this->issuesTable} WHERE status = 'new' AND type = 'wfPluginUpgrade'");
277
+ }
278
+
279
+ if ($updatesNeeded['themes']) {
280
+ $upgradeNames = array();
281
+ foreach ($updatesNeeded['themes'] as $t) {
282
+ $name = $t['Name'];
283
+ $upgradeNames[$name] = 1;
284
+ }
285
+ $upgradeIssues = $this->getDB()->querySelect("SELECT * FROM {$this->issuesTable} WHERE status = 'new' AND type = 'wfThemeUpgrade'");
286
+ foreach ($upgradeIssues as $issue) {
287
+ $data = unserialize($issue['data']);
288
+ $name = $data['Name'];
289
+ if (!isset($upgradeNames[$name])) { //Some themes don't have a slug associated with them, so we anchor on the name
290
+ $this->deleteIssue($issue['id']);
291
+ }
292
+ }
293
+ }
294
+ else {
295
+ $this->getDB()->queryWrite("DELETE FROM {$this->issuesTable} WHERE status = 'new' AND type = 'wfThemeUpgrade'");
296
+ }
297
+ }
298
+ else {
299
+ $this->getDB()->queryWrite("DELETE FROM {$this->issuesTable} WHERE status = 'new' AND (type = 'wfUpgrade' OR type = 'wfPluginUpgrade' OR type = 'wfThemeUpgrade')");
300
+ }
301
+
302
+ wfScanEngine::refreshScanNotification($this);
303
+ }
304
public function updateSummaryItem($key, $val){
305
$arr = wfConfig::get_ser('wf_summaryItems', array());
306
$arr[$key] = $val;
lib/wfScanEngine.php CHANGED
@@ -122,7 +122,6 @@ class wfScanEngine {
122
$this->jobList[] = 'checkSpamvertized';
123
$this->jobList[] = 'checkSpamIP';
124
$this->jobList[] = 'checkGSB';
125
- $this->jobList[] = 'heartbleed';
126
$this->jobList[] = 'checkHowGetIPs_init';
127
$this->jobList[] = 'checkHowGetIPs_main';
128
$this->jobList[] = 'knownFiles_init';
@@ -278,24 +277,6 @@ class wfScanEngine {
278
public function getCurrentJob(){
279
return $this->jobList[0];
280
}
281
- private function scan_heartbleed(){
282
- if(wfConfig::get('scansEnabled_heartbleed')){
283
- $this->statusIDX['heartbleed'] = wordfence::statusStart("Scanning your site for the HeartBleed vulnerability");
284
- $result = $this->api->call('scan_heartbleed', array(), array(
285
- 'siteURL' => site_url()
286
- ));
287
- $haveIssues = false;
288
- if($result['haveIssues'] && is_array($result['issues']) ){
289
- foreach($result['issues'] as $issue){
290
- $this->addIssue($issue['type'], $issue['level'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
291
- $haveIssues = true;
292
- }
293
- }
294
- wordfence::statusEnd($this->statusIDX['heartbleed'], $haveIssues);
295
- } else {
296
- wordfence::statusDisabled("Skipping HeartBleed scan");
297
- }
298
- }
299
private function scan_publicSite(){
300
if(wfConfig::get('isPaid')){
301
if(wfConfig::get('scansEnabled_public')){
@@ -594,7 +575,7 @@ class wfScanEngine {
594
}
595
private function scan_knownFiles_init(){
596
$this->status(1, 'info', "Contacting Wordfence to initiate scan");
597
- $this->api->call('log_scan', array(), array());
598
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
599
$baseContents = scandir(ABSPATH);
600
if(! is_array($baseContents)){
@@ -627,7 +608,8 @@ class wfScanEngine {
627
$this->status(2, 'info', "Found " . sizeof($knownFilesThemes) . " themes");
628
$this->i->updateSummaryItem('totalThemes', sizeof($knownFilesThemes));
629
630
- $this->hasher = new wordfenceHash(strlen(ABSPATH), ABSPATH, $includeInKnownFilesScan, $knownFilesThemes, $knownFilesPlugins, $this);
631
}
632
private function scan_knownFiles_main(){
633
$this->hasher->run($this); //Include this so we can call addIssue and ->api->
@@ -1337,8 +1319,17 @@ class wfScanEngine {
1337
public function status($level, $type, $msg){
1338
wordfence::status($level, $type, $msg);
1339
}
1340
- public function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
1341
- return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
1342
}
1343
public static function requestKill(){
1344
wfConfig::set('wfKillRequested', time(), wfConfig::DONT_AUTOLOAD);
122
$this->jobList[] = 'checkSpamvertized';
123
$this->jobList[] = 'checkSpamIP';
124
$this->jobList[] = 'checkGSB';
125
$this->jobList[] = 'checkHowGetIPs_init';
126
$this->jobList[] = 'checkHowGetIPs_main';
127
$this->jobList[] = 'knownFiles_init';
277
public function getCurrentJob(){
278
return $this->jobList[0];
279
}
280
private function scan_publicSite(){
281
if(wfConfig::get('isPaid')){
282
if(wfConfig::get('scansEnabled_public')){
575
}
576
private function scan_knownFiles_init(){
577
$this->status(1, 'info', "Contacting Wordfence to initiate scan");
578
+ $response = $this->api->call('log_scan', array(), array());
579
$baseWPStuff = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
580
$baseContents = scandir(ABSPATH);
581
if(! is_array($baseContents)){
608
$this->status(2, 'info', "Found " . sizeof($knownFilesThemes) . " themes");
609
$this->i->updateSummaryItem('totalThemes', sizeof($knownFilesThemes));
610
611
+ $malwarePrefixesHash = (isset($response['malwarePrefixes']) ? wfUtils::hex2bin($response['malwarePrefixes']) : '');
612
+ $this->hasher = new wordfenceHash(strlen(ABSPATH), ABSPATH, $includeInKnownFilesScan, $knownFilesThemes, $knownFilesPlugins, $this, $malwarePrefixesHash);
613
}
614
private function scan_knownFiles_main(){
615
$this->hasher->run($this); //Include this so we can call addIssue and ->api->
1319
public function status($level, $type, $msg){
1320
wordfence::status($level, $type, $msg);
1321
}
1322
+ public function addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData, $alreadyHashed = false) {
1323
+ return $this->i->addIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData, $alreadyHashed);
1324
+ }
1325
+ public function addPendingIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData){
1326
+ return $this->i->addPendingIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
1327
+ }
1328
+ public function getPendingIssueCount() {
1329
+ return $this->i->getPendingIssueCount();
1330
+ }
1331
+ public function getPendingIssues($offset = 0, $limit = 100) {
1332
+ return $this->i->getPendingIssues($offset, $limit);
1333
}
1334
public static function requestKill(){
1335
wfConfig::set('wfKillRequested', time(), wfConfig::DONT_AUTOLOAD);
lib/wfSchema.php CHANGED
@@ -58,17 +58,31 @@ class wfSchema {
58
KEY k2(IP, ctime)
59
) default charset=latin1",
60
"wfIssues" => "(
61
- id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
62
- time int UNSIGNED NOT NULL,
63
- status varchar(10) NOT NULL,
64
- type varchar(20) NOT NULL,
65
- severity tinyint UNSIGNED NOT NULL,
66
- ignoreP char(32) NOT NULL,
67
- ignoreC char(32) NOT NULL,
68
- shortMsg varchar(255) NOT NULL,
69
- longMsg text,
70
- data text
71
- ) default charset=utf8",
72
"wfLeechers" => "(
73
eMin int UNSIGNED NOT NULL,
74
IP int UNSIGNED NOT NULL,
58
KEY k2(IP, ctime)
59
) default charset=latin1",
60
"wfIssues" => "(
61
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
62
+ `time` int(10) unsigned NOT NULL,
63
+ `status` varchar(10) NOT NULL,
64
+ `type` varchar(20) NOT NULL,
65
+ `severity` tinyint(3) unsigned NOT NULL,
66
+ `ignoreP` char(32) NOT NULL,
67
+ `ignoreC` char(32) NOT NULL,
68
+ `shortMsg` varchar(255) NOT NULL,
69
+ `longMsg` text,
70
+ `data` text,
71
+ PRIMARY KEY (`id`)
72
+ ) DEFAULT CHARSET=utf8",
73
+ "wfPendingIssues" => "(
74
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
75
+ `time` int(10) unsigned NOT NULL,
76
+ `status` varchar(10) NOT NULL,
77
+ `type` varchar(20) NOT NULL,
78
+ `severity` tinyint(3) unsigned NOT NULL,
79
+ `ignoreP` char(32) NOT NULL,
80
+ `ignoreC` char(32) NOT NULL,
81
+ `shortMsg` varchar(255) NOT NULL,
82
+ `longMsg` text,
83
+ `data` text,
84
+ PRIMARY KEY (`id`)
85
+ ) DEFAULT CHARSET=utf8",
86
"wfLeechers" => "(
87
eMin int UNSIGNED NOT NULL,
88
IP int UNSIGNED NOT NULL,
lib/wfUtils.php CHANGED
@@ -1775,6 +1775,12 @@ class wfUtils {
1775
return 'unknown';
1776
}
1777
1778
/**
1779
* Identical to the same functions in wfWAFUtils.
1780
*
@@ -1930,6 +1936,21 @@ class wfUtils {
1930
$args = func_get_args();
1931
return self::callMBSafeStrFunction('strrpos', $args);
1932
}
1933
}
1934
1935
// GeoIP lib uses these as well
1775
return 'unknown';
1776
}
1777
1778
+ public static function hex2bin($string) { //Polyfill for PHP < 5.4
1779
+ if (!is_string($string)) { return false; }
1780
+ if (strlen($string) % 2 == 1) { return false; }
1781
+ return pack('H*', $string);
1782
+ }
1783
+
1784
/**
1785
* Identical to the same functions in wfWAFUtils.
1786
*
1936
$args = func_get_args();
1937
return self::callMBSafeStrFunction('strrpos', $args);
1938
}
1939
+
1940
+ /**
1941
+ * Returns the current timestamp, adjusted as needed to get close to what we consider a true timestamp. We use this
1942
+ * because a significant number of servers are using a drastically incorrect time.
1943
+ *
1944
+ * @return int
1945
+ */
1946
+ public static function normalizedTime() {
1947
+ $offset = wfConfig::get('timeoffset_ntp', false);
1948
+ if ($offset === false) {
1949
+ $offset = wfConfig::get('timeoffset_wf', false);
1950
+ if ($offset === false) { $offset = 0; }
1951
+ }
1952
+ return time() + $offset;
1953
+ }
1954
}
1955
1956
// GeoIP lib uses these as well
lib/wordfenceClass.php CHANGED
@@ -121,19 +121,7 @@ class wordfence {
121
$wfdb = new wfDB();
122
123
if(wfConfig::get('other_WFNet')){
124
- $q1 = $wfdb->querySelect("select URI from $p"."wfNet404s where ctime > unix_timestamp() - 3600 limit 1000");
125
- $URIs = array();
126
- foreach($q1 as $rec){
127
- $URIs[] = $rec['URI'];
128
- }
129
$wfdb->truncate($p . "wfNet404s");
130
- if(sizeof($URIs) > 0){
131
- try {
132
- $api->call('send_net_404s', array(), array( 'URIs' => json_encode($URIs) ));
133
- } catch(Exception $e){
134
- //Ignore
135
- }
136
- }
137
138
$q2 = $wfdb->querySelect("select IP from $p"."wfVulnScanners where ctime > unix_timestamp() - 3600");
139
$scanCont = "";
@@ -361,6 +349,9 @@ class wordfence {
361
}
362
}
363
364
wp_schedule_single_event(time(), 'wordfence_completeCoreUpdateNotification');
365
}
366
public static function _completeCoreUpdateNotification() {
@@ -481,11 +472,6 @@ SQL
481
$db->queryWrite("update $prefix"."wfConfig set val='1' where name='scansEnabled_options'");
482
}
483
484
- $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_heartbleed'");
485
- if($optScanEnabled != '0' && $optScanEnabled != '1'){ //Enable heartbleed if no value is set.
486
- wfConfig::set('scansEnabled_heartbleed', 1);
487
- }
488
-
489
// IPv6 schema changes for 6.0.1
490
$tables_with_ips = array(
491
'wfCrawlers',
@@ -692,6 +678,28 @@ SQL
692
$wpdb->query("ALTER TABLE {$blockedIPLogTable} ADD PRIMARY KEY (IP, unixday, blockType)");
693
}
694
695
//Check the How does Wordfence get IPs setting
696
wfUtils::requestDetectProxyCallback();
697
@@ -1230,7 +1238,7 @@ SQL
1230
));
1231
wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
1232
}
1233
- echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . wp_kses($email, array()) . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>";
1234
exit();
1235
} else if($wfFunc == 'unlockAccess'){
1236
if (!preg_match('/^(?:(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9](?::|$)){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))#x2F;i', get_transient('wfunlock_' . $_GET['key']))) {
@@ -1300,6 +1308,13 @@ SQL
1300
$waf->getStorageEngine()->setConfig($key, $value);
1301
}
1302
1303
if (class_exists('wfWAFIPBlocksController')) {
1304
wfWAFIPBlocksController::synchronizeConfigSettings();
1305
}
@@ -3547,7 +3562,7 @@ HTACCESS;
3547
$jsonData = array(
3548
'serverTime' => $serverTime,
3549
'serverMicrotime' => microtime(true),
3550
- 'msg' => wp_kses_data( (string) $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"))
3551
);
3552
$events = array();
3553
$alsoGet = $_POST['alsoGet'];
@@ -4841,6 +4856,7 @@ HTML;
4841
'cacheType' => wfConfig::get('cacheType'),
4842
'liveTrafficEnabled' => wfConfig::liveTrafficEnabled(),
4843
'scanIssuesPerPage' => WORDFENCE_SCAN_ISSUES_PER_PAGE,
4844
));
4845
}
4846
public static function activation_warning(){
121
$wfdb = new wfDB();
122
123
if(wfConfig::get('other_WFNet')){
124
$wfdb->truncate($p . "wfNet404s");
125
126
$q2 = $wfdb->querySelect("select IP from $p"."wfVulnScanners where ctime > unix_timestamp() - 3600");
127
$scanCont = "";
349
}
350
}
351
352
+ $i = new wfIssues();
353
+ $i->reconcileUpgradeIssues($report, true);
354
+
355
wp_schedule_single_event(time(), 'wordfence_completeCoreUpdateNotification');
356
}
357
public static function _completeCoreUpdateNotification() {
472
$db->queryWrite("update $prefix"."wfConfig set val='1' where name='scansEnabled_options'");
473
}
474
475
// IPv6 schema changes for 6.0.1
476
$tables_with_ips = array(
477
'wfCrawlers',
678
$wpdb->query("ALTER TABLE {$blockedIPLogTable} ADD PRIMARY KEY (IP, unixday, blockType)");
679
}
680
681
+ //6.3.6
682
+ if (!wfConfig::get('migration636_email_summary_excluded_directories')) {
683
+ $excluded_directories = explode(',', (string) wfConfig::get('email_summary_excluded_directories'));
684
+ $key = array_search('wp-content/plugins/wordfence/tmp', $excluded_directories); if ($key !== false) { unset($excluded_directories[$key]); }
685
+ $key = array_search('wp-content/wflogs', $excluded_directories); if ($key === false) { $excluded_directories[] = 'wp-content/wflogs'; }
686
+ wfConfig::set('email_summary_excluded_directories', implode(',', $excluded_directories));
687
+ wfConfig::set('migration636_email_summary_excluded_directories', 1, wfConfig::DONT_AUTOLOAD);
688
+ }
689
+
690
+ $fileModsTable = wfDB::networkPrefix() . 'wfFileMods';
691
+ $hasSHAC = $wpdb->get_col($wpdb->prepare(<<<SQL
692
+ SELECT * FROM information_schema.COLUMNS
693
+ WHERE TABLE_SCHEMA=DATABASE()
694
+ AND COLUMN_NAME='SHAC'
695
+ AND TABLE_NAME=%s
696
+ SQL
697
+ , $fileModsTable));
698
+ if (!$hasSHAC) {
699
+ $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN `SHAC` BINARY(32) NOT NULL DEFAULT '' AFTER `newMD5`");
700
+ $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN `isSafeFile` VARCHAR(1) NOT NULL DEFAULT '?' AFTER `stoppedOnPosition`");
701
+ }
702
+
703
//Check the How does Wordfence get IPs setting
704
wfUtils::requestDetectProxyCallback();
705
1238
));
1239
wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
1240
}
1241
+ echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . wp_kses($email, array()) . "\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, they have been emailed instructions on how to regain access to this system. The instructions we sent will expire 30 minutes from now.</body></html>";
1242
exit();
1243
} else if($wfFunc == 'unlockAccess'){
1244
if (!preg_match('/^(?:(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9](?::|$)){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))#x2F;i', get_transient('wfunlock_' . $_GET['key']))) {
1308
$waf->getStorageEngine()->setConfig($key, $value);
1309
}
1310
1311
+ if (wfConfig::get('timeoffset_wf') !== false) {
1312
+ $waf->getStorageEngine()->setConfig('timeoffset_wf', wfConfig::get('timeoffset_wf'));
1313
+ }
1314
+ else {
1315
+ $waf->getStorageEngine()->unsetConfig('timeoffset_wf');
1316
+ }
1317
+
1318
if (class_exists('wfWAFIPBlocksController')) {
1319
wfWAFIPBlocksController::synchronizeConfigSettings();
1320
}
3562
$jsonData = array(
3563
'serverTime' => $serverTime,
3564
'serverMicrotime' => microtime(true),
3565
+ 'msg' => wp_kses_data( (string) $wfdb->querySingle("SELECT msg FROM {$p}wfStatus WHERE level < 3 AND ctime > (UNIX_TIMESTAMP() - 3600) ORDER BY ctime DESC LIMIT 1"))
3566
);
3567
$events = array();
3568
$alsoGet = $_POST['alsoGet'];
4856
'cacheType' => wfConfig::get('cacheType'),
4857
'liveTrafficEnabled' => wfConfig::liveTrafficEnabled(),
4858
'scanIssuesPerPage' => WORDFENCE_SCAN_ISSUES_PER_PAGE,
4859
+ 'allowsPausing' => wfConfig::get('liveActivityPauseEnabled'),
4860
));
4861
}
4862
public static function activation_warning(){
lib/wordfenceHash.php CHANGED
@@ -41,7 +41,7 @@ class wordfenceHash {
41
* @param wfScanEngine $engine
42
* @throws Exception
43
*/
44
- public function __construct($striplen, $path, $only, $themes, $plugins, $engine){
45
$this->striplen = $striplen;
46
$this->path = $path;
47
$this->only = $only;
@@ -71,6 +71,7 @@ class wordfenceHash {
71
//$this->db->queryWrite("update " . $this->db->prefix() . "wfFileMods set oldMD5 = newMD5");
72
$this->db->truncate($this->db->prefix() . "wfFileMods");
73
$this->db->truncate($this->db->prefix() . "wfKnownFileList");
74
$fetchCoreHashesStatus = wordfence::statusStart("Fetching core, theme and plugin file signatures from Wordfence");
75
try {
76
$this->knownFiles = $this->engine->getKnownFilesLoader()
@@ -80,21 +81,32 @@ class wordfenceHash {
80
throw $e;
81
}
82
wordfence::statusEnd($fetchCoreHashesStatus, false, true);
83
- if($this->malwareEnabled){
84
$malwarePrefixStatus = wordfence::statusStart("Fetching list of known malware files from Wordfence");
85
- $malwareData = $engine->api->getStaticURL('/malwarePrefixes.bin');
86
- if(! $malwareData){
87
- wordfence::statusEndErr();
88
- throw new Exception("Could not fetch malware signatures from Wordfence servers.");
89
- }
90
- if(strlen($malwareData) % 4 != 0){
91
- wordfence::statusEndErr();
92
- throw new Exception("Malware data received from Wordfence servers was not valid.");
93
}
94
- $this->malwareData = array();
95
- for($i = 0; $i < strlen($malwareData); $i += 4){
96
- $this->malwareData[substr($malwareData, $i, 4)] = '1';
97
}
98
wordfence::statusEnd($malwarePrefixStatus, false, true);
99
}
100
@@ -118,12 +130,18 @@ class wordfenceHash {
118
if($this->coreUnknownEnabled){ $this->status['coreUnknown'] = wordfence::statusStart("Scanning for unknown files in wp-admin and wp-includes"); } else { wordfence::statusDisabled("Skipping unknown core file scan"); }
119
}
120
public function __sleep(){
121
- return array('striplen', 'totalFiles', 'totalDirs', 'totalData', 'stoppedOnFile', 'coreEnabled', 'pluginsEnabled', 'themesEnabled', 'malwareEnabled', 'coreUnknownEnabled', 'knownFiles', 'malwareData', 'haveIssues', 'status', 'possibleMalware', 'path', 'only', 'totalForks', 'alertedOnUnknownWordPressVersion', 'foldersProcessed', 'suspectedFiles', 'indexed', 'indexSize', 'currentIndex', 'foldersEntered');
122
}
123
public function __wakeup(){
124
$this->db = new wfDB();
125
$this->startTime = microtime(true);
126
$this->totalForks++;
127
}
128
public function getSuspectedFiles() {
129
return array_keys($this->suspectedFiles);
@@ -164,6 +182,9 @@ class wordfenceHash {
164
$this->_checkForTimeout($file);
165
}
166
167
wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data.");
168
if($this->coreEnabled){ wordfence::statusEnd($this->status['core'], $this->haveIssues['core']); }
169
if($this->themesEnabled){ wordfence::statusEnd($this->status['themes'], $this->haveIssues['themes']); }
@@ -409,28 +430,24 @@ class wordfenceHash {
409
{
410
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
411
$fileContents = @file_get_contents($localFile);
412
- if ($fileContents && (!preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*#x2F;s', $fileContents)))
413
- { //<?php
414
- if (!$this->isSafeFile($shac))
415
- {
416
-
417
- $this->haveIssues['core'] = true;
418
- $this->engine->addIssue(
419
- 'knownfile',
420
- 1,
421
- 'coreModified' . $file . $md5,
422
- 'coreModified' . $file,
423
- 'WordPress core file modified: ' . $file,
424
- "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
425
- array(
426
- 'file' => $file,
427
- 'cType' => 'core',
428
- 'canDiff' => true,
429
- 'canFix' => true,
430
- 'canDelete' => false
431
- )
432
- );
433
- }
434
}
435
}
436
}
@@ -448,13 +465,12 @@ class wordfenceHash {
448
$shouldGenerateIssue = false;
449
}
450
451
- if (!$this->isSafeFile($shac) && $shouldGenerateIssue)
452
{
453
$itemName = $this->knownFiles['plugins'][$file][0];
454
$itemVersion = $this->knownFiles['plugins'][$file][1];
455
$cKey = $this->knownFiles['plugins'][$file][2];
456
- $this->haveIssues['plugins'] = true;
457
- $this->engine->addIssue(
458
'knownfile',
459
2,
460
'modifiedplugin' . $file . $md5,
@@ -469,7 +485,8 @@ class wordfenceHash {
469
'canDelete' => false,
470
'cName' => $itemName,
471
'cVersion' => $itemVersion,
472
- 'cKey' => $cKey
473
)
474
);
475
}
@@ -490,13 +507,12 @@ class wordfenceHash {
490
$shouldGenerateIssue = false;
491
}
492
493
- if (!$this->isSafeFile($shac) && $shouldGenerateIssue)
494
{
495
$itemName = $this->knownFiles['themes'][$file][0];
496
$itemVersion = $this->knownFiles['themes'][$file][1];
497
$cKey = $this->knownFiles['themes'][$file][2];
498
- $this->haveIssues['themes'] = true;
499
- $this->engine->addIssue(
500
'knownfile',
501
2,
502
'modifiedtheme' . $file . $md5,
@@ -511,7 +527,8 @@ class wordfenceHash {
511
'canDelete' => false,
512
'cName' => $itemName,
513
'cVersion' => $itemVersion,
514
- 'cKey' => $cKey
515
)
516
);
517
}
@@ -536,7 +553,7 @@ class wordfenceHash {
536
'cType' => 'core',
537
'canDiff' => false,
538
'canFix' => false,
539
- 'canDelete' => true
540
)
541
);
542
}
@@ -544,9 +561,10 @@ class wordfenceHash {
544
}
545
}
546
// knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
547
- // we could split this into files who's path we recognize and file's who's path we recognize AND who have a valid sig.
548
- // But because we want to scan files who's sig we don't recognize, regardless of known path or not, we only need one "knownFile" field.
549
- $this->db->queryWrite("insert into " . $this->db->prefix() . "wfFileMods (filename, filenameMD5, knownFile, oldMD5, newMD5) values ('%s', unhex(md5('%s')), %d, '', unhex('%s')) ON DUPLICATE KEY UPDATE newMD5=unhex('%s'), knownFile=%d", $file, $file, $knownFile, $md5, $md5, $knownFile);
550
551
$this->totalFiles++;
552
$this->totalData += @filesize($realFile); //We already checked if file overflows int in the fileTooBig routine above
@@ -558,6 +576,61 @@ class wordfenceHash {
558
}
559
wfUtils::endProcessingFile();
560
}
561
public static function wfHash($file){
562
$fp = @fopen($file, "rb");
563
if (!$fp) {
@@ -639,13 +712,57 @@ class wordfenceHash {
639
return false;
640
}
641
private function isMalwarePrefix($hexMD5){
642
- $binPrefix = pack("H*", substr($hexMD5, 0, 8));
643
- if(isset($this->malwareData[$binPrefix])){
644
- return true;
645
}
646
return false;
647
}
648
- private function isSafeFile($shac){
649
$result = $this->engine->api->call('is_safe_file', array(), array('shac' => strtoupper($shac)));
650
if(isset($result['isSafe']) && $result['isSafe'] == 1){
651
return true;
41
* @param wfScanEngine $engine
42
* @throws Exception
43
*/
44
+ public function __construct($striplen, $path, $only, $themes, $plugins, $engine, $malwarePrefixesHash){
45
$this->striplen = $striplen;
46
$this->path = $path;
47
$this->only = $only;
71
//$this->db->queryWrite("update " . $this->db->prefix() . "wfFileMods set oldMD5 = newMD5");
72
$this->db->truncate($this->db->prefix() . "wfFileMods");
73
$this->db->truncate($this->db->prefix() . "wfKnownFileList");
74
+ $this->db->truncate($this->db->prefix() . "wfPendingIssues");
75
$fetchCoreHashesStatus = wordfence::statusStart("Fetching core, theme and plugin file signatures from Wordfence");
76
try {
77
$this->knownFiles = $this->engine->getKnownFilesLoader()
81
throw $e;
82
}
83
wordfence::statusEnd($fetchCoreHashesStatus, false, true);
84
+ if ($this->malwareEnabled) {
85
$malwarePrefixStatus = wordfence::statusStart("Fetching list of known malware files from Wordfence");
86
+
87
+ $stored = wfConfig::get_ser('malwarePrefixes', array(), false);
88
+ if (is_array($stored) && isset($stored['hash']) && $stored['hash'] == $malwarePrefixesHash && isset($stored['prefixes']) && wfWAFUtils::strlen($stored['prefixes']) % 4 == 0) {
89
+ wordfence::status(4, 'info', "Using cached malware prefixes");
90
}
91
+ else {
92
+ wordfence::status(4, 'info', "Fetching fresh malware prefixes");
93
+
94
+ $malwareData = $engine->api->getStaticURL('/malwarePrefixes.bin');
95
+ if (!$malwareData) {
96
+ wordfence::statusEndErr();
97
+ throw new Exception("Could not fetch malware signatures from Wordfence servers.");
98
+ }
99
+
100
+ if (wfWAFUtils::strlen($malwareData) % 4 != 0) {
101
+ wordfence::statusEndErr();
102
+ throw new Exception("Malware data received from Wordfence servers was not valid.");
103
+ }
104
+
105
+ $stored = array('hash' => $malwarePrefixesHash, 'prefixes' => $malwareData);
106
+ wfConfig::set_ser('malwarePrefixes', $stored, true, wfConfig::DONT_AUTOLOAD);
107
}
108
+
109
+ $this->malwareData = $stored['prefixes'];
110
wordfence::statusEnd($malwarePrefixStatus, false, true);
111
}
112
130
if($this->coreUnknownEnabled){ $this->status['coreUnknown'] = wordfence::statusStart("Scanning for unknown files in wp-admin and wp-includes"); } else { wordfence::statusDisabled("Skipping unknown core file scan"); }
131
}
132
public function __sleep(){
133
+ return array('striplen', 'totalFiles', 'totalDirs', 'totalData', 'stoppedOnFile', 'coreEnabled', 'pluginsEnabled', 'themesEnabled', 'malwareEnabled', 'coreUnknownEnabled', 'knownFiles', 'haveIssues', 'status', 'possibleMalware', 'path', 'only', 'totalForks', 'alertedOnUnknownWordPressVersion', 'foldersProcessed', 'suspectedFiles', 'indexed', 'indexSize', 'currentIndex', 'foldersEntered');
134
}
135
public function __wakeup(){
136
$this->db = new wfDB();
137
$this->startTime = microtime(true);
138
$this->totalForks++;
139
+
140
+ $stored = wfConfig::get_ser('malwarePrefixes', array(), false);
141
+ if (!isset($stored['prefixes'])) {
142
+ $stored['prefixes'] = '';
143
+ }
144
+ $this->malwareData = $stored['prefixes'];
145
}
146
public function getSuspectedFiles() {
147
return array_keys($this->suspectedFiles);
182
$this->_checkForTimeout($file);
183
}
184
185
+ wordfence::status(4, 'info', "Processing pending issues");
186
+ $this->_processPendingIssues();
187
+
188
wordfence::status(2, 'info', "Analyzed " . $this->totalFiles . " files containing " . wfUtils::formatBytes($this->totalData) . " of data.");
189
if($this->coreEnabled){ wordfence::statusEnd($this->status['core'], $this->haveIssues['core']); }
190
if($this->themesEnabled){ wordfence::statusEnd($this->status['themes'], $this->haveIssues['themes']); }
430
{
431
$localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
432
$fileContents = @file_get_contents($localFile);
433
+ if ($fileContents && (!preg_match('/<\?' . 'php[\r\n\s\t]*\/\/[\r\n\s\t]*Silence is golden\.[\r\n\s\t]*(?:\?>)?[\r\n\s\t]*#x2F;s', $fileContents))) {
434
+ $this->haveIssues['core'] = true;
435
+ $this->engine->addPendingIssue(
436
+ 'knownfile',
437
+ 1,
438
+ 'coreModified' . $file . $md5,
439
+ 'coreModified' . $file,
440
+ 'WordPress core file modified: ' . $file,
441
+ "This WordPress core file has been modified and differs from the original file distributed with this version of WordPress.",
442
+ array(
443
+ 'file' => $file,
444
+ 'cType' => 'core',
445
+ 'canDiff' => true,
446
+ 'canFix' => true,
447
+ 'canDelete' => false,
448
+ 'haveIssues' => 'core'
449
+ )
450
+ );
451
}
452
}
453
}
465
$shouldGenerateIssue = false;
466
}
467
468
+ if ($shouldGenerateIssue)
469
{
470
$itemName = $this->knownFiles['plugins'][$file][0];
471
$itemVersion = $this->knownFiles['plugins'][$file][1];
472
$cKey = $this->knownFiles['plugins'][$file][2];
473
+ $this->engine->addPendingIssue(
474
'knownfile',
475
2,
476
'modifiedplugin' . $file . $md5,
485
'canDelete' => false,
486
'cName' => $itemName,
487
'cVersion' => $itemVersion,
488
+ 'cKey' => $cKey,
489
+ 'haveIssues' => 'plugins'
490
)
491
);
492
}
507
$shouldGenerateIssue = false;
508
}
509
510
+ if ($shouldGenerateIssue)
511
{
512
$itemName = $this->knownFiles['themes'][$file][0];
513
$itemVersion = $this->knownFiles['themes'][$file][1];
514
$cKey = $this->knownFiles['themes'][$file][2];
515
+ $this->engine->addPendingIssue(
516
'knownfile',
517
2,
518
'modifiedtheme' . $file . $md5,
527
'canDelete' => false,
528
'cName' => $itemName,
529
'cVersion' => $itemVersion,
530
+ 'cKey' => $cKey,
531
+ 'haveIssues' => 'themes'
532
)
533
);
534
}
553
'cType' => 'core',
554
'canDiff' => false,
555
'canFix' => false,
556
+ 'canDelete' => true,
557
)
558
);
559
}
561
}
562
}
563
// knownFile means that the file is both part of core or a known plugin or theme AND that we recognize the file's hash.
564
+ // we could split this into files whose path we recognize and file's whose path we recognize AND who have a valid sig.
565
+ // But because we want to scan files whose sig we don't recognize, regardless of known path or not, we only need one "knownFile" field.
566
+ $fileModsTable = $this->db->prefix() . 'wfFileMods';
567
+ $this->db->queryWrite("INSERT INTO {$fileModsTable} (filename, filenameMD5, knownFile, oldMD5, newMD5, SHAC) VALUES ('%s', UNHEX(MD5('%s')), %d, '', UNHEX('%s'), UNHEX('%s')) ON DUPLICATE KEY UPDATE newMD5 = UNHEX('%s'), SHAC = UNHEX('%s'), knownFile = %d", $file, $file, $knownFile, $md5, $shac, $md5, $shac, $knownFile);
568
569
$this->totalFiles++;
570
$this->totalData += @filesize($realFile); //We already checked if file overflows int in the fileTooBig routine above
576
}
577
wfUtils::endProcessingFile();
578
}
579
+ private function _processPendingIssues() {
580
+ $fileModsTable = $this->db->prefix() . 'wfFileMods';
581
+
582
+ $count = $this->engine->getPendingIssueCount();
583
+ $offset = 0;
584
+ while ($offset < $count) {
585
+ $issues = $this->engine->getPendingIssues($offset);
586
+ if (count($issues) == 0) {
587
+ break;
588
+ }
589
+
590
+ //Do a bulk check of is_safe_file
591
+ $hashesToCheck = array();
592
+ foreach ($issues as &$i) {
593
+ $shac = $this->db->querySingle("SELECT HEX(SHAC) FROM {$fileModsTable} WHERE filename = '%s' AND isSafeFile = '?'", $i['data']['file']);
594
+ $shac = strtoupper($shac);
595
+ $i['shac'] = null;
596
+ if ($shac !== null) {
597
+ $shac = strtoupper($shac);
598
+ $i['shac'] = $shac;
599
+ $hashesToCheck[] = $shac;
600
+ }
601
+ }
602
+
603
+ $safeFiles = array();
604
+ if (count($hashesToCheck) > 0) {
605
+ $safeFiles = $this->isSafeFile($hashesToCheck);
606
+ }
607
+
608
+ //Migrate non-safe file issues to official issues
609
+ foreach ($issues as &$i) {
610
+ if (!in_array($i['shac'], $safeFiles)) {
611
+ $haveIssuesType = $i['data']['haveIssues'];
612
+ $this->haveIssues[$haveIssuesType] = true;
613
+ $this->engine->addIssue(
614
+ $i['type'],
615
+ $i['severity'],
616
+ $i['ignoreP'],
617
+ $i['ignoreC'],
618
+ $i['shortMsg'],
619
+ $i['longMsg'],
620
+ $i['data'],
621
+ true //Prevent ignoreP and ignoreC from being hashed again
622
+ );
623
+ $this->db->queryWrite("UPDATE {$fileModsTable} SET isSafeFile = '0' WHERE SHAC = UNHEX('%s')", $i['shac']);
624
+ }
625
+ else {
626
+ $this->db->queryWrite("UPDATE {$fileModsTable} SET isSafeFile = '1' WHERE SHAC = UNHEX('%s')", $i['shac']);
627
+ }
628
+ }
629
+
630
+ $offset += count($issues);
631
+ $this->engine->checkForKill();
632
+ }
633
+ }
634
public static function wfHash($file){
635
$fp = @fopen($file, "rb");
636
if (!$fp) {
712
return false;
713
}
714
private function isMalwarePrefix($hexMD5){
715
+ $hasPrefix = $this->_prefixListContainsMD5($this->malwareData, wfUtils::hex2bin($hexMD5));
716
+ return $hasPrefix !== false;
717
+ }
718
+
719
+ /**
720
+ * @param $prefixList The prefix list to search, sorted as a binary string.
721
+ * @param $md5 The binary MD5 hash to search for.
722
+ * @return bool|int false if not found, otherwise the index in the list
723
+ */
724
+ private function _prefixListContainsMD5($prefixList, $md5) {
725
+ $size = 4; //bytes
726
+ $p = substr($md5, 0, $size);
727
+
728
+ $count = ceil(wfWAFUtils::strlen($prefixList) / $size);
729
+ $low = 0;
730
+ $high = $count - 1;
731
+
732
+ while ($low <= $high) {
733
+ $mid = (int) (($high + $low) / 2);
734
+ $val = wfWAFUtils::substr($prefixList, $mid * $size, $size);
735
+ $cmp = strcmp($val, $p);
736
+ if ($cmp < 0) {
737
+ $low = $mid + 1;
738
+ }
739
+ else if ($cmp > 0) {
740
+ $high = $mid - 1;
741
+ }
742
+ else {
743
+ return $mid;
744
+ }
745
}
746
+
747
return false;
748
}
749
+
750
+ /**
751
+ * Queries the is_safe_file endpoint. If provided an array, it does a bulk check and returns an array containing the
752
+ * hashes that were marked as safe. If provided a string, it returns a boolean to indicate the safeness of the file.
753
+ *
754
+ * @param string|array $shac
755
+ * @return array|bool
756
+ */
757
+ private function isSafeFile($shac) {
758
+ if (is_array($shac)) {
759
+ $result = $this->engine->api->call('is_safe_file', array(), array('multipleSHAC' => json_encode($shac)));
760
+ if (isset($result['isSafe'])) {
761
+ return $result['isSafe'];
762
+ }
763
+ return array();
764
+ }
765
+
766
$result = $this->engine->api->call('is_safe_file', array(), array('shac' => strtoupper($shac)));
767
if(isset($result['isSafe']) && $result['isSafe'] == 1){
768
return true;
lib/wordfenceScanner.php CHANGED
@@ -301,21 +301,20 @@ class wordfenceScanner {
301
302
$treatAsBinary = ($isPHP || $isHTML || wfConfig::get('scansEnabled_scanImages'));
303
if ($treatAsBinary && wfUtils::strpos($data, '$allowed'.'Sites') !== false && wfUtils::strpos($data, "define ('VER"."SION', '1.") !== false && wfUtils::strpos($data, "TimThum"."b script created by") !== false) {
304
- if(! $this->isSafeFile($this->path . $file)){
305
- $this->addResult(array(
306
- 'type' => 'file',
307
- 'severity' => 1,
308
- 'ignoreP' => $this->path . $file,
309
- 'ignoreC' => $fileSum,
310
- 'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
311
- 'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it." . $extraMsg,
312
- 'data' => array_merge(array(
313
- 'file' => $file,
314
- ), $dataForFile),
315
- ));
316
- break;
317
- }
318
- }
319
else {
320
$regexMatched = false;
321
foreach ($this->patterns['rules'] as $rule) {
@@ -336,28 +335,27 @@ class wordfenceScanner {
336
else if (($type == 'both' || $type == 'browser') && !$treatAsBinary) { continue; }
337
338
if (preg_match('/(' . $rule[2] . ')/i', $data, $matches, PREG_OFFSET_CAPTURE)) {
339
- if (!$this->isSafeFile($this->path . $file)) {
340
- $matchString = $matches[1][0];
341
- $matchOffset = $matches[1][1];
342
- $beforeString = wfWAFUtils::substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
343
- $afterString = wfWAFUtils::substr($data, $matchOffset + strlen($matchString), 100);
344
- if (!$logOnly) {
345
- $this->addResult(array(
346
- 'type' => 'file',
347
- 'severity' => 1,
348
- 'ignoreP' => $this->path . $file,
349
- 'ignoreC' => $fileSum,
350
- 'shortMsg' => "File appears to be malicious: " . esc_html($file),
351
- 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\" class=\"wf-split-word\">\"" . wfUtils::potentialBinaryStringToHTML((wfUtils::strlen($matchString) > 200 ? wfUtils::substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>.' . $extraMsg,
352
- 'data' => array_merge(array(
353
- 'file' => $file,
354
- ), $dataForFile),
355
- ));
356
- }
357
- $regexMatched = true;
358
- $this->scanEngine->recordMetric('malwareSignature', $rule[0], array('file' => $file, 'match' => $matchString, 'before' => $beforeString, 'after' => $afterString), false);
359
- break;
360
}
361
}
362
363
if ($forkObj->shouldFork()) {
@@ -383,20 +381,19 @@ class wordfenceScanner {
383
}
384
}
385
if ($badStringFound) {
386
- if (!$this->isSafeFile($this->path . $file)) {
387
- $this->addResult(array(
388
- 'type' => 'file',
389
- 'severity' => 1,
390
- 'ignoreP' => $this->path . $file,
391
- 'ignoreC' => $fileSum,
392
- 'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
393
- 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '<span class=\"wf-split-word\">" . esc_html($badStringFound) . "</span>' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans. This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.",
394
- 'data' => array_merge(array(
395
- 'file' => $file,
396
- ), $dataForFile),
397
- ));
398
- break;
399
- }
400
}
401
}
402
@@ -430,6 +427,7 @@ class wordfenceScanner {
430
$siteURL = get_site_url();
431
$siteHost = parse_url($siteURL, PHP_URL_HOST);
432
foreach($hooverResults as $file => $hresults){
433
$dataForFile = $this->dataForFile($file, $this->path . $file);
434
435
foreach($hresults as $result){
@@ -447,41 +445,54 @@ class wordfenceScanner {
447
}
448
449
if($result['badList'] == 'goog-malware-shavar'){
450
- if(! $this->isSafeFile($this->path . $file)){
451
- $this->addResult(array(
452
- 'type' => 'file',
453
- 'severity' => 1,
454
- 'ignoreP' => $this->path . $file,
455
- 'ignoreC' => md5_file($this->path . $file),
456
- 'shortMsg' => "File contains suspected malware URL: " . esc_html($this->path . $file),
457
- 'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . esc_html($this->patterns['word3']) . " when scanning files so the URL may not be visible if you view this file. The URL is: " . esc_html($result['URL']) . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
458
- 'data' => array_merge(array(
459
- 'file' => $file,
460
- 'badURL' => $result['URL'],
461
- 'gsb' => 'goog-malware-shavar'
462
- ), $dataForFile),
463
- ));
464
- }
465
} else if($result['badList'] == 'googpub-phish-shavar'){
466
- if(! $this->isSafeFile($this->path . $file)){
467
- $this->addResult(array(
468
- 'type' => 'file',
469
- 'severity' => 1,
470
- 'ignoreP' => $this->path . $file,
471
- 'ignoreC' => md5_file($this->path . $file),
472
- 'shortMsg' => "File contains suspected phishing URL: " . esc_html($this->path . $file),
473
- 'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . esc_html($result['URL']),
474
- 'data' => array_merge(array(
475
- 'file' => $file,
476
- 'badURL' => $result['URL'],
477
- 'gsb' => 'googpub-phish-shavar'
478
- ), $dataForFile),
479
- ));
480
- }
481
}
482
}
483
}
484
wfUtils::endProcessingFile();
485
486
return $this->results;
487
}
@@ -502,13 +513,28 @@ class wordfenceScanner {
502
//We don't have a results for this file so append
503
$this->results[] = $result;
504
}
505
- private function isSafeFile($file){
506
if(! $this->api){
507
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
508
}
509
-
510
- $wfHash = wordfenceHash::wfHash($file);
511
- $result = $this->api->call('is_safe_file', array(), array('shac' => strtoupper($wfHash[1])));
512
if(isset($result['isSafe']) && $result['isSafe'] == 1){
513
return true;
514
}
@@ -584,15 +610,19 @@ class wordfenceScanner {
584
* @property string $filename
585
* @property string $filenameMD5
586
* @property string $newMD5
587
* @property string $stoppedOnSignature
588
* @property string $stoppedOnPosition
589
*/
590
class wordfenceMalwareScanFile {
591
protected $_filename;
592
protected $_filenameMD5;
593
protected $_newMD5;
594
protected $_stoppedOnSignature;
595
protected $_stoppedOnPosition;
596
597
protected static function getDB() {
598
static $db = null;
@@ -609,20 +639,28 @@ class wordfenceMalwareScanFile {
609
610
public static function files($limit = 500) {
611
$db = self::getDB();
612
- $result = $db->querySelect("SELECT filename, filenameMD5, HEX(newMD5) AS newMD5, stoppedOnSignature, stoppedOnPosition FROM " . wfDB::networkPrefix() . "wfFileMods WHERE oldMD5 != newMD5 AND knownFile = 0 limit %d", $limit);
613
$files = array();
614
foreach ($result as $row) {
615
- $files[] = new wordfenceMalwareScanFile($row['filename'], $row['filenameMD5'], $row['newMD5'], $row['stoppedOnSignature'], $row['stoppedOnPosition']);
616
}
617
return $files;
618
}
619
620
- public function __construct($filename, $filenameMD5, $newMD5, $stoppedOnSignature, $stoppedOnPosition) {
621
$this->_filename = $filename;
622
$this->_filenameMD5 = $filenameMD5;
623
$this->_newMD5 = $newMD5;
624
$this->_stoppedOnSignature = $stoppedOnSignature;
625
$this->_stoppedOnPosition = $stoppedOnPosition;
626
}
627
628
public function __get($key) {
@@ -633,10 +671,14 @@ class wordfenceMalwareScanFile {
633
return $this->_filenameMD5;
634
case 'newMD5':
635
return $this->_newMD5;
636
case 'stoppedOnSignature':
637
return $this->_stoppedOnSignature;
638
case 'stoppedOnPosition':
639
return $this->_stoppedOnPosition;
640
}
641
}
642
@@ -655,6 +697,18 @@ class wordfenceMalwareScanFile {
655
$db = self::getDB();
656
$db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET stoppedOnSignature = '%s', stoppedOnPosition = %d WHERE filenameMD5 = '%s'", $this->stoppedOnSignature, $this->stoppedOnPosition, $this->filenameMD5);
657
}
658
}
659
660
?>
301
302
$treatAsBinary = ($isPHP || $isHTML || wfConfig::get('scansEnabled_scanImages'));
303
if ($treatAsBinary && wfUtils::strpos($data, '$allowed'.'Sites') !== false && wfUtils::strpos($data, "define ('VER"."SION', '1.") !== false && wfUtils::strpos($data, "TimThum"."b script created by") !== false) {
304
+ $this->addResult(array(
305
+ 'type' => 'file',
306
+ 'severity' => 1,
307
+ 'ignoreP' => $this->path . $file,
308
+ 'ignoreC' => $fileSum,
309
+ 'shortMsg' => "File is an old version of TimThumb which is vulnerable.",
310
+ 'longMsg' => "This file appears to be an old version of the TimThumb script which makes your system vulnerable to attackers. Please upgrade the theme or plugin that uses this or remove it." . $extraMsg,
311
+ 'data' => array_merge(array(
312
+ 'file' => $file,
313
+ 'shac' => $record->SHAC,
314
+ ), $dataForFile),
315
+ ));
316
+ break;
317
+ }
318
else {
319
$regexMatched = false;
320
foreach ($this->patterns['rules'] as $rule) {
335
else if (($type == 'both' || $type == 'browser') && !$treatAsBinary) { continue; }
336
337
if (preg_match('/(' . $rule[2] . ')/i', $data, $matches, PREG_OFFSET_CAPTURE)) {
338
+ $matchString = $matches[1][0];
339
+ $matchOffset = $matches[1][1];
340
+ $beforeString = wfWAFUtils::substr($data, max(0, $matchOffset - 100), $matchOffset - max(0, $matchOffset - 100));
341
+ $afterString = wfWAFUtils::substr($data, $matchOffset + strlen($matchString), 100);
342
+ if (!$logOnly) {
343
+ $this->addResult(array(
344
+ 'type' => 'file',
345
+ 'severity' => 1,
346
+ 'ignoreP' => $this->path . $file,
347
+ 'ignoreC' => $fileSum,
348
+ 'shortMsg' => "File appears to be malicious: " . esc_html($file),
349
+ 'longMsg' => "This file appears to be installed by a hacker to perform malicious activity. If you know about this file you can choose to ignore it to exclude it from future scans. The text we found in this file that matches a known malicious file is: <strong style=\"color: #F00;\" class=\"wf-split-word\">\"" . wfUtils::potentialBinaryStringToHTML((wfUtils::strlen($matchString) > 200 ? wfUtils::substr($matchString, 0, 200) . '...' : $matchString)) . "\"</strong>. The infection type is: <strong>" . esc_html($rule[3]) . '</strong>.' . $extraMsg,
350
+ 'data' => array_merge(array(
351
+ 'file' => $file,
352
+ 'shac' => $record->SHAC,
353
+ ), $dataForFile),
354
+ ));
355
}
356
+ $regexMatched = true;
357
+ $this->scanEngine->recordMetric('malwareSignature', $rule[0], array('file' => $file, 'match' => $matchString, 'before' => $beforeString, 'after' => $afterString), false);
358
+ break;
359
}
360
361
if ($forkObj->shouldFork()) {
381
}
382
}
383
if ($badStringFound) {
384
+ $this->addResult(array(
385
+ 'type' => 'file',
386
+ 'severity' => 1,
387
+ 'ignoreP' => $this->path . $file,
388
+ 'ignoreC' => $fileSum,
389
+ 'shortMsg' => "This file may contain malicious executable code: " . esc_html($this->path . $file),
390
+ 'longMsg' => "This file is a PHP executable file and contains the word 'eval' (without quotes) and the word '<span class=\"wf-split-word\">" . esc_html($badStringFound) . "</span>' (without quotes). The eval() function along with an encoding function like the one mentioned are commonly used by hackers to hide their code. If you know about this file you can choose to ignore it to exclude it from future scans. This file was detected because you have enabled HIGH SENSITIVITY scanning. This option is more aggressive than the usual scans, and may cause false positives.",
391
+ 'data' => array_merge(array(
392
+ 'file' => $file,
393
+ 'shac' => $record->SHAC,
394
+ ), $dataForFile),
395
+ ));
396
+ break;
397
}
398
}
399
427
$siteURL = get_site_url();
428
$siteHost = parse_url($siteURL, PHP_URL_HOST);
429
foreach($hooverResults as $file => $hresults){
430
+ $record = wordfenceMalwareScanFile::fileForPath($file);
431
$dataForFile = $this->dataForFile($file, $this->path . $file);
432
433
foreach($hresults as $result){
445
}
446
447
if($result['badList'] == 'goog-malware-shavar'){
448
+ $this->addResult(array(
449
+ 'type' => 'file',
450
+ 'severity' => 1,
451
+ 'ignoreP' => $this->path . $file,
452
+ 'ignoreC' => md5_file($this->path . $file),
453
+ 'shortMsg' => "File contains suspected malware URL: " . esc_html($this->path . $file),
454
+ 'longMsg' => "This file contains a suspected malware URL listed on Google's list of malware sites. Wordfence decodes " . esc_html($this->patterns['word3']) . " when scanning files so the URL may not be visible if you view this file. The URL is: " . esc_html($result['URL']) . " - More info available at <a href=\"http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=" . urlencode($result['URL']) . "&client=googlechrome&hl=en-US\" target=\"_blank\">Google Safe Browsing diagnostic page</a>.",
455
+ 'data' => array_merge(array(
456
+ 'file' => $file,
457
+ 'shac' => $record->SHAC,
458
+ 'badURL' => $result['URL'],
459
+ 'gsb' => 'goog-malware-shavar'
460
+ ), $dataForFile),
461
+ ));
462
} else if($result['badList'] == 'googpub-phish-shavar'){
463
+ $this->addResult(array(
464
+ 'type' => 'file',
465
+ 'severity' => 1,
466
+ 'ignoreP' => $this->path . $file,
467
+ 'ignoreC' => md5_file($this->path . $file),
468
+ 'shortMsg' => "File contains suspected phishing URL: " . esc_html($this->path . $file),
469
+ 'longMsg' => "This file contains a URL that is a suspected phishing site that is currently listed on Google's list of known phishing sites. The URL is: " . esc_html($result['URL']),
470
+ 'data' => array_merge(array(
471
+ 'file' => $file,
472
+ 'shac' => $record->SHAC,
473
+ 'badURL' => $result['URL'],
474
+ 'gsb' => 'googpub-phish-shavar'
475
+ ), $dataForFile),
476
+ ));
477
}
478
}
479
}
480
wfUtils::endProcessingFile();
481
+
482
+ wordfence::status(4, 'info', "Finalizing malware scan results");
483
+ $hashesToCheck = array();
484
+ foreach ($this->results as $r) {
485
+ $hashesToCheck[] = $r['data']['shac'];
486
+ }
487
+
488
+ if (count($hashesToCheck) > 0) {
489
+ $safeFiles = $this->isSafeFile($hashesToCheck);
490
+ foreach ($this->results as $index => $value) {
491
+ if (in_array($value['data']['shac'], $safeFiles)) {
492
+ unset($this->results[$index]);
493
+ }
494
+ }
495
+ }
496
497
return $this->results;
498
}
513
//We don't have a results for this file so append
514
$this->results[] = $result;
515
}
516
+
517
+ /**
518
+ * Queries the is_safe_file endpoint. If provided an array, it does a bulk check and returns an array containing the
519
+ * hashes that were marked as safe. If provided a string, it returns a boolean to indicate the safeness of the file.
520
+ *
521
+ * @param string|array $shac
522
+ * @return array|bool
523
+ */
524
+ private function isSafeFile($shac) {
525
if(! $this->api){
526
$this->api = new wfAPI($this->apiKey, $this->wordpressVersion);
527
}
528
+
529
+ if (is_array($shac)) {
530
+ $result = $this->api->call('is_safe_file', array(), array('multipleSHAC' => json_encode($shac)));
531
+ if (isset($result['isSafe'])) {
532
+ return $result['isSafe'];
533
+ }
534
+ return array();
535
+ }
536
+
537
+ $result = $this->api->call('is_safe_file', array(), array('shac' => strtoupper($shac)));
538
if(isset($result['isSafe']) && $result['isSafe'] == 1){
539
return true;
540
}
610
* @property string $filename
611
* @property string $filenameMD5
612
* @property string $newMD5
613
+ * @property string $SHAC
614
* @property string $stoppedOnSignature
615
* @property string $stoppedOnPosition
616
+ * @property string $isSafeFile
617
*/
618
class wordfenceMalwareScanFile {
619
protected $_filename;
620
protected $_filenameMD5;
621
protected $_newMD5;
622
+ protected $_shac;
623
protected $_stoppedOnSignature;
624
protected $_stoppedOnPosition;
625
+ protected $_isSafeFile;
626
627
protected static function getDB() {
628
static $db = null;
639
640
public static function files($limit = 500) {
641
$db = self::getDB();
642
+ $result = $db->querySelect("SELECT filename, filenameMD5, HEX(newMD5) AS newMD5, HEX(SHAC) AS SHAC, stoppedOnSignature, stoppedOnPosition, isSafeFile FROM " . wfDB::networkPrefix() . "wfFileMods WHERE oldMD5 != newMD5 AND knownFile = 0 limit %d", $limit);
643
$files = array();
644
foreach ($result as $row) {
645
+ $files[] = new wordfenceMalwareScanFile($row['filename'], $row['filenameMD5'], $row['newMD5'], $row['SHAC'], $row['stoppedOnSignature'], $row['stoppedOnPosition'], $row['isSafeFile']);
646
}
647
return $files;
648
}
649
650
+ public static function fileForPath($file) {
651
+ $db = self::getDB();
652
+ $row = $db->querySingleRec("SELECT filename, filenameMD5, HEX(newMD5) AS newMD5, HEX(SHAC) AS SHAC, stoppedOnSignature, stoppedOnPosition, isSafeFile FROM " . wfDB::networkPrefix() . "wfFileMods WHERE filename = '%s'", $file);
653
+ return new wordfenceMalwareScanFile($row['filename'], $row['filenameMD5'], $row['newMD5'], $row['SHAC'], $row['stoppedOnSignature'], $row['stoppedOnPosition'], $row['isSafeFile']);
654
+ }
655
+
656
+ public function __construct($filename, $filenameMD5, $newMD5, $shac, $stoppedOnSignature, $stoppedOnPosition, $isSafeFile) {
657
$this->_filename = $filename;
658
$this->_filenameMD5 = $filenameMD5;
659
$this->_newMD5 = $newMD5;
660
+ $this->_shac = strtoupper($shac);
661
$this->_stoppedOnSignature = $stoppedOnSignature;
662
$this->_stoppedOnPosition = $stoppedOnPosition;
663
+ $this->_isSafeFile = $isSafeFile;
664
}
665
666
public function __get($key) {
671
return $this->_filenameMD5;
672
case 'newMD5':
673
return $this->_newMD5;
674
+ case 'SHAC':
675
+ return $this->_shac;
676
case 'stoppedOnSignature':
677
return $this->_stoppedOnSignature;
678
case 'stoppedOnPosition':
679
return $this->_stoppedOnPosition;
680
+ case 'isSafeFile':
681
+ return $this->_isSafeFile;
682
}
683
}
684
697
$db = self::getDB();
698
$db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET stoppedOnSignature = '%s', stoppedOnPosition = %d WHERE filenameMD5 = '%s'", $this->stoppedOnSignature, $this->stoppedOnPosition, $this->filenameMD5);
699
}
700
+
701
+ public function markSafe() {
702
+ $db = self::getDB();
703
+ $db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET isSafeFile = '1' WHERE filenameMD5 = '%s'", $this->filenameMD5);
704
+ $this->isSafeFile = '1';
705
+ }
706
+
707
+ public function markUnsafe() {
708
+ $db = self::getDB();
709
+ $db->queryWrite("UPDATE " . wfDB::networkPrefix() . "wfFileMods SET isSafeFile = '0' WHERE filenameMD5 = '%s'", $this->filenameMD5);
710
+ $this->isSafeFile = '0';
711
+ }
712
}
713
714
?>
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.7.3
6
- Stable tag: 6.3.5
7
8
Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
@@ -42,8 +42,7 @@ Wordfence Security is Multi-Site compatible and includes Cellphone Sign-in which
42
* Checks the strength of all user and admin passwords to enhance login security.
43
* Includes login security to lock out brute force hacks and to stop WordPress from revealing info that will compromise WordPress security.
44
45
- = Security Scanning =
46
- * Scans for the HeartBleed vulnerability - included in the free scan for all users.
47
* Scans core files, themes and plugins against WordPress.org repository versions to check their integrity. Verify security of your source.
48
* See how files have changed. Optionally repair changed files that are security threats.
49
* Scans for signatures of over 44,000 known malware variants that are known WordPress security threats.
@@ -151,16 +150,29 @@ Designed for every skill level, [The WordPress Security Learning Center](https:/
151
152
Secure your website with Wordfence.
153
154
- 1. The dashboard of Wordfene Security where you can get a quick overview of any important notifications and attacks your site has been protected from.
155
- 2. The Web Application Firewall of Wordfence Security where you can configure your protection level and view which vulnerabilities you're protected from.
156
- 3. The scan page of Wordfence Security where you can see a summary, manage security issues and do a manual security scan.
157
- 4. The Live Traffic view of Wordfence Security where you can see real-time activity on your site.
158
- 5. The "Blocked IPs" page where you can manage blocked IPs, locked out IPs and see recently throttled IPs that violated security rules.
159
- 6. The basic view of Wordfence Security options. There is very little to configure other than your alert email address and security level.
160
- 7. If you're technically minded, this is the under-the-hood view of Wordfence Security options where you can fine-tune your security settings.
161
162
== Changelog ==
163
164
= 6.3.5 =
165
* Improvement: Sites can now specify a list of trusted proxies when using X-Forwarded-For for IP resolution.
166
* Improvement: Added options to customize which dashboard notifications are shown.
@@ -737,7 +749,7 @@ Secure your website with Wordfence.
737
* Fixed issue that caused litespeed users to receive multiple warnings about the noabort issue.
738
* Added detection for 5 new malware variants. Thanks to Dave M. and others for the samples. Keep them coming folks!
739
* Updated Wordfence server API to version 2.12.
740
- * Added facility at bottom of Wordfence options page to send a test email from your WordPress sytem to check if email sending is working.
741
* Suppress LOCK_EX flock() warnings in falcon engine that were being generated by sites that use NFS and don't support flock() or reliable file locking.
742
* Updated to the October 2014 version of the Geo IP country DB. (newest edition)
743
@@ -1486,7 +1498,7 @@ Secure your website with Wordfence.
1486
* Added ability for admin's to unlock login and unblock their IP addresses if they're accidentally locked out by the firewall or login security. Uses two security tokens to prevent abuse.
1487
* Admins can now also disable firewall and login security from the unlock-me email, just in case of emergency.
1488
* Made advanced security options visible so you know they exist.
1489
- * Fixed dns_get_record() function not existing bug on Windows sytems pre PHP 5.3.0. Was causing scans to hang.
1490
* Increased login lockout defaults to be much higher which still protects against brute force hacks.
1491
* Removed CURLOPT_MAXREDIRS in curl to avoid safe mode warnings.
1492
* Fixed ability to view and diff files on blogs installed in subdirectories.
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.7.3
6
+ Stable tag: 6.3.6
7
8
Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
42
* Checks the strength of all user and admin passwords to enhance login security.
43
* Includes login security to lock out brute force hacks and to stop WordPress from revealing info that will compromise WordPress security.
44
45
+ = Security Scanning =
46
* Scans core files, themes and plugins against WordPress.org repository versions to check their integrity. Verify security of your source.
47
* See how files have changed. Optionally repair changed files that are security threats.
48
* Scans for signatures of over 44,000 known malware variants that are known WordPress security threats.
150
151
Secure your website with Wordfence.
152
153
+ 1. The dashboard gives you an overview of your site's security including notifications, attack statistics and Wordfence feature status.
154
+ 2. The Web Application Firewall protects your site from common types of attacks and known security vulnerabilities.
155
+ 3. The Wordfence Malware Scanner lets you know if your site has been compromised and alerts you to other security issues that need to be addressed.
156
+ 4. The Wordfence Security Live Traffic view shows you real-time activity on your site including bot traffic and exploit attempts.
157
+ 5. Block IPs that are known to be malicious, manage IPs that have been locked out and see recently throttled IPs that violated security rules.
158
+ 6. The Wordfence Options page is where you manage high-level Wordfence features and upgrade your license to Premium.
159
+ 7. The Advanced Options page allows technically-minded users fine-tune their security settings.
160
161
== Changelog ==
162
163
+ = 6.3.6 =
164
+ * Improvement: Optimized the malware signature scan to reduce memory usage.
165
+ * Improvement: Optimized the overall scan to make fewer network calls.
166
+ * Improvement: Running an update now automatically dismisses the corresponding scan issue if present.
167
+ * Improvement: Added a time limit to the live activity status so only current messages are shown.
168
+ * Improvement: WAF configuration files are now excluded by default from the recently modified files list in the activity report.
169
+ * Improvement: Background pausing for live activity and traffic may now be disabled.
170
+ * Improvement: Added additional WAF support to allow us to more easily address false positives.
171
+ * Improvement: Blocking pages presented by Wordfence now indicate the source and contain information to help diagnose caching problems.
172
+ * Fix: All external URLs in the tour are now https.
173
+ * Fix: Corrected a typo in the unlock email template.
174
+ * Fix: Fixed the target of a label on the options page.
175
+
176
= 6.3.5 =
177
* Improvement: Sites can now specify a list of trusted proxies when using X-Forwarded-For for IP resolution.
178
* Improvement: Added options to customize which dashboard notifications are shown.
749
* Fixed issue that caused litespeed users to receive multiple warnings about the noabort issue.
750
* Added detection for 5 new malware variants. Thanks to Dave M. and others for the samples. Keep them coming folks!
751
* Updated Wordfence server API to version 2.12.
752
+ * Added facility at bottom of Wordfence options page to send a test email from your WordPress system to check if email sending is working.
753
* Suppress LOCK_EX flock() warnings in falcon engine that were being generated by sites that use NFS and don't support flock() or reliable file locking.
754
* Updated to the October 2014 version of the Geo IP country DB. (newest edition)
755
1498
* Added ability for admin's to unlock login and unblock their IP addresses if they're accidentally locked out by the firewall or login security. Uses two security tokens to prevent abuse.
1499
* Admins can now also disable firewall and login security from the unlock-me email, just in case of emergency.
1500
* Made advanced security options visible so you know they exist.
1501
+ * Fixed dns_get_record() function not existing bug on Windows systems pre PHP 5.3.0. Was causing scans to hang.
1502
* Increased login lockout defaults to be much higher which still protects against brute force hacks.
1503
* Removed CURLOPT_MAXREDIRS in curl to avoid safe mode warnings.
1504
* Fixed ability to view and diff files on blogs installed in subdirectories.
vendor/wordfence/wf-waf/src/init.php CHANGED
@@ -4,7 +4,7 @@ define('WFWAF_VERSION', '1.0.3');
4
define('WFWAF_PATH', dirname(__FILE__) . '/');
5
define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
- define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.5/');
8
if (!defined('WFWAF_DEBUG')) {
9
define('WFWAF_DEBUG', false);
10
}
4
define('WFWAF_PATH', dirname(__FILE__) . '/');
5
define('WFWAF_LIB_PATH', WFWAF_PATH . 'lib/');
6
define('WFWAF_VIEW_PATH', WFWAF_PATH . 'views/');
7
+ define('WFWAF_API_URL_SEC', 'https://noc4.wordfence.com/v1.6/');
8
if (!defined('WFWAF_DEBUG')) {
9
define('WFWAF_DEBUG', false);
10
}
vendor/wordfence/wf-waf/src/lib/parser/parser.php CHANGED
@@ -367,6 +367,14 @@ class wfWAFRuleParser extends wfWAFBaseParser {
367
} else if ($token->getValue() === 'rules') {
368
$rules = $this->expectLiteral();
369
$urlParam->setRules($rules);
370
}
371
372
break;
@@ -601,6 +609,14 @@ class wfWAFRuleParserURLParam {
601
* @var null
602
*/
603
private $rules;
604
605
/**
606
* @param string $param
@@ -617,24 +633,28 @@ class wfWAFRuleParserURLParam {
617
* @param string $param
618
* @param null $rules
619
*/
620
- public function __construct($url = null, $param = null, $rules = null) {
621
$this->url = $url;
622
$this->param = $param;
623
$this->rules = $rules;
624
}
625
626
/**
627
* Return format:
628
- * blacklistParam(url='/\/uploadify\.php#x2F;i', param=request.fileNames.Filedata, rules=[3, 14])
629
*
630
* @param string $action
631
* @return string
632
*/
633
public function renderRule($action) {
634
- return sprintf('%s(url=%s, param=%s%s)', $action,
635
wfWAFRule::exportString($this->getUrl()),
636
$this->renderParam($this->getParam()),
637
- $this->getRules() ? ', rules=[' . join(', ', array_map('intval', $this->getRules())) . ']' : '');
638
}
639
640
/**
@@ -700,6 +720,34 @@ class wfWAFRuleParserURLParam {
700
public function setRules($rules) {
701
$this->rules = $rules;
702
}
703
}
704
705
class wfWAFRuleParserSyntaxError extends wfWAFParserSyntaxError {
367
} else if ($token->getValue() === 'rules') {
368
$rules = $this->expectLiteral();
369
$urlParam->setRules($rules);
370
+ } else if ($token->getValue() === 'conditional') {
371
+ $this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_OPEN_PARENTHESIS);
372
+ $conditional = $this->parseConditional();
373
+ $this->expectTokenTypeEquals($this->expectNextToken(), wfWAFRuleLexer::T_CLOSE_PARENTHESIS);
374
+ $urlParam->setConditional($conditional);
375
+ } else if ($token->getValue() === 'minVersion') {
376
+ $minVersion = $this->expectLiteral();
377
+ $urlParam->setMinVersion($minVersion);
378
}
379
380
break;
609
* @var null
610
*/
611
private $rules;
612
+ /**
613
+ * @var null
614
+ */
615
+ private $conditional;
616
+ /**
617
+ * @var float
618
+ */
619
+ private $minVersion;
620
621
/**
622
* @param string $param
633
* @param string $param
634
* @param null $rules
635
*/
636
+ public function __construct($url = null, $param = null, $rules = null, $conditional = null, $minVersion = null) {
637
$this->url = $url;
638
$this->param = $param;
639
$this->rules = $rules;
640
+ $this->conditional = $conditional;
641
+ $this->minVersion = $minVersion;
642
}
643
644
/**
645
* Return format:
646
+ * blacklistParam(url='/\/uploadify\.php#x2F;i', param=request.fileNames.Filedata, rules=[3, 14], conditional=(match('1', request.body.field)))
647
*
648
* @param string $action
649
* @return string
650
*/
651
public function renderRule($action) {
652
+ return sprintf('%s(url=%s, param=%s%s%s)', $action,
653
wfWAFRule::exportString($this->getUrl()),
654
$this->renderParam($this->getParam()),
655
+ $this->getRules() ? ', rules=[' . join(', ', array_map('intval', $this->getRules())) . ']' : '',
656
+ $this->getConditional() ? ', conditional=(' . $this->getConditional()->renderRule() . ')' : '');
657
+ //minVersion not included in re-rendering
658
}
659
660
/**
720
public function setRules($rules) {
721
$this->rules = $rules;
722
}
723
+
724
+ /**
725
+ * @return null
726
+ */
727
+ public function getConditional() {
728
+ return $this->conditional;
729
+ }
730
+
731
+ /**
732
+ * @param null $conditional
733
+ */
734
+ public function setConditional($conditional) {
735
+ $this->conditional = $conditional;
736
+ }
737
+
738
+ /**
739
+ * @return float|null
740
+ */
741
+ public function getMinVersion() {
742
+ return $this->minVersion;
743
+ }
744
+
745
+ /**
746
+ * @param float $minVersion
747
+ */
748
+ public function setMinVersion($minVersion) {
749
+ $this->minVersion = $minVersion;
750
+ }
751
}
752
753
class wfWAFRuleParserSyntaxError extends wfWAFParserSyntaxError {
vendor/wordfence/wf-waf/src/lib/request.php CHANGED
@@ -443,7 +443,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
443
* @param string|null $baseKey The base key used when recursing.
444
* @return string
445
*/
446
- public function getCookieString($cookies = null, $baseKey = null) {
447
if ($cookies == null) {
448
$cookies = $this->getCookies();
449
}
@@ -465,7 +465,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
465
$cookieString .= $nestedCookies;
466
}
467
else {
468
- if (strpos($resolvedName, 'wordpress_') === 0) {
469
$cookieValue = '<redacted>';
470
}
471
@@ -548,7 +548,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
548
* @return string
549
*/
550
public function highlightFailedParams($failedParams = array(), $highlightParamFormat = '[param]%s[/param]',
551
- $highlightMatchFormat = '[match]%s[/match]') {
552
$highlights = array();
553
554
// Cap at 47.5kb
@@ -622,7 +622,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
622
switch (wfWAFUtils::strtolower($header)) {
623
case 'cookie':
624
// TODO: Hook up highlights to cookies
625
- $request .= 'Cookie: ' . trim($this->getCookieString()) . "\n";
626
break;
627
628
case 'host':
@@ -632,7 +632,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
632
case 'authorization':
633
$hasAuth = true;
634
if ($auth) {
635
- $request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n";
636
}
637
break;
638
@@ -644,7 +644,7 @@ class wfWAFRequest implements wfWAFRequestInterface {
644
}
645
646
if (!$hasAuth && $auth) {
647
- $request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n";
648
}
649
650
$body = $this->getBody();
443
* @param string|null $baseKey The base key used when recursing.
444
* @return string
445
*/
446
+ public function getCookieString($cookies = null, $baseKey = null, $preventRedaction = false) {
447
if ($cookies == null) {
448
$cookies = $this->getCookies();
449
}
465
$cookieString .= $nestedCookies;
466
}
467
else {
468
+ if (strpos($resolvedName, 'wordpress_') === 0 && !$preventRedaction) {
469
$cookieValue = '<redacted>';
470
}
471
548
* @return string
549
*/
550
public function highlightFailedParams($failedParams = array(), $highlightParamFormat = '[param]%s[/param]',
551
+ $highlightMatchFormat = '[match]%s[/match]', $preventRedaction = false) {
552
$highlights = array();
553
554
// Cap at 47.5kb
622
switch (wfWAFUtils::strtolower($header)) {
623
case 'cookie':
624
// TODO: Hook up highlights to cookies
625
+ $request .= 'Cookie: ' . trim($this->getCookieString(null, null, $preventRedaction)) . "\n";
626
break;
627
628
case 'host':
632
case 'authorization':
633
$hasAuth = true;
634
if ($auth) {
635
+ $request .= 'Authorization: Basic ' . ($preventRedaction ? base64_encode($auth['user'] . ':' . $auth['password']) : '<redacted>') . "\n";
636
}
637
break;
638
644
}
645
646
if (!$hasAuth && $auth) {
647
+ $request .= 'Authorization: Basic ' . ($preventRedaction ? base64_encode($auth['user'] . ':' . $auth['password']) : '<redacted>') . "\n";
648
}
649
650
$body = $this->getBody();
vendor/wordfence/wf-waf/src/lib/storage/file.php CHANGED
@@ -3,6 +3,7 @@
3
class wfWAFStorageFile implements wfWAFStorageInterface {
4
5
const LOG_FILE_HEADER = "<?php exit('Access denied'); __halt_compiler(); ?>\n";
6
const IP_BLOCK_RECORD_SIZE = 24;
7
8
public static function atomicFilePutContents($file, $content, $prefix = 'config') {
@@ -349,7 +350,7 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
349
350
$files = array(
351
array($this->getIPCacheFile(), 'ipCacheFileHandle', self::LOG_FILE_HEADER),
352
- array($this->getConfigFile(), 'configFileHandle', self::LOG_FILE_HEADER . serialize($this->getDefaultConfiguration())),
353
);
354
foreach ($files as $file) {
355
list($filePath, $fileHandle, $defaultContents) = $file;
@@ -506,6 +507,9 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
506
while (!feof($this->configFileHandle)) {
507
$serializedData .= fread($this->configFileHandle, 1024);
508
}
509
$this->data = @unserialize($serializedData);
510
511
if ($this->data === false) {
@@ -533,9 +537,9 @@ class wfWAFStorageFile implements wfWAFStorageInterface {
533
if (WFWAF_IS_WINDOWS) {
534
self::lock($this->configFileHandle, LOCK_UN);
535
fclose($this->configFileHandle);
536
- file_put_contents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data), LOCK_EX);
537
} else {
538
- wfWAFStorageFile::atomicFilePutContents($this->getConfigFile(), self::LOG_FILE_HEADER . serialize($this->data));
539
}
540
541
if (WFWAF_IS_WINDOWS) {
3
class wfWAFStorageFile implements wfWAFStorageInterface {
4
5
const LOG_FILE_HEADER = "<?php exit('Access denied'); __halt_compiler(); ?>\n";
6
+ const LOG_INFO_HEADER = "******************************************************************\nThis file is used by the Wordfence Web Application Firewall. Read \nmore at https://docs.wordfence.com/en/Web_Application_Firewall_FAQ\n******************************************************************\n";
7
const IP_BLOCK_RECORD_SIZE = 24;
8
9
public static function atomicFilePutContents($file, $content, $prefix = 'config') {
350
351
$files = array(
352
array($this->getIPCacheFile(), 'ipCacheFileHandle', self::LOG_FILE_HEADER),
353
+ array($this->getConfigFile(), 'configFileHandle', self::LOG_FILE_HEADER . self::LOG_INFO_HEADER . serialize($this->getDefaultConfiguration())),
354
);
355
foreach ($files as $file) {
356
list($filePath, $fileHandle, $defaultContents) = $file;
507
while (!feof($this->configFileHandle)) {
508
$serializedData .= fread($this->configFileHandle, 1024);
509
}
510
+ if (wfWAFUtils::substr($serializedData, 0, 1) == '*') {
511
+ $serializedData = wfWAFUtils::substr($serializedData, wfWAFUtils::strlen(self::LOG_INFO_HEADER));
512
+ }
513
$this->data = @unserialize($serializedData);
514
515
if ($this->data === false) {
537
if (WFWAF_IS_WINDOWS) {
538
self::lock($this->configFileHandle, LOCK_UN);
539
fclose($this->configFileHandle);
540
+ file_put_contents($this->getConfigFile(), self::LOG_FILE_HEADER . self::LOG_INFO_HEADER . serialize($this->data), LOCK_EX);
541
} else {
542
+ wfWAFStorageFile::atomicFilePutContents($this->getConfigFile(), self::LOG_FILE_HEADER . self::LOG_INFO_HEADER . serialize($this->data));
543
}
544
545
if (WFWAF_IS_WINDOWS) {
vendor/wordfence/wf-waf/src/lib/utils.php CHANGED
@@ -767,4 +767,25 @@ class wfWAFUtils {
767
}
768
return $data;
769
}
770
}
767
}
768
return $data;
769
}
770
+
771
+ /**
772
+ * Returns the current timestamp, adjusted as needed to get close to what we consider a true timestamp. We use this
773
+ * because a significant number of servers are using a drastically incorrect time.
774
+ *
775
+ * @return int
776
+ */
777
+ public static function normalizedTime() {
778
+ $offset = 0;
779
+ try {
780
+ $offset = wfWAF::getInstance()->getStorageEngine()->getConfig('timeoffset_ntp', false);
781
+ if ($offset === false) {
782
+ $offset = wfWAF::getInstance()->getStorageEngine()->getConfig('timeoffset_wf', false);
783
+ if ($offset === false) { $offset = 0; }
784
+ }
785
+ }
786
+ catch (Exception $e) {
787
+ //Ignore
788
+ }
789
+ return time() + $offset;
790
+ }
791
}
vendor/wordfence/wf-waf/src/lib/waf.php CHANGED
@@ -571,17 +571,26 @@ PHP
571
if (isset($rules[$key]) && is_array($rules[$key])) {
572
/** @var wfWAFRuleParserURLParam $urlParam */
573
foreach ($rules[$key] as $urlParam) {
574
- if ($urlParam->getRules()) {
575
- $url = array(
576
- 'url' => $urlParam->getUrl(),
577
- 'rules' => $urlParam->getRules(),
578
- );
579
- } else {
580
- $url = $urlParam->getUrl();
581
}
582
-
583
- $exportedCode .= sprintf("\$this->{$key}[%s][] = %s;\n", var_export($urlParam->getParam(), true),
584
- var_export($url, true));
585
}
586
$exportedCode .= "\n";
587
}
@@ -969,6 +978,9 @@ HTML
969
if (!in_array($ruleID, $urlRegex['rules'])) {
970
continue;
971
}
972
$urlRegex = $urlRegex['url'];
973
}
974
if (preg_match($urlRegex, $urlPath)) {
@@ -1103,6 +1115,9 @@ HTML
1103
if (!in_array($ruleID, $urlRegex['rules'])) {
1104
continue;
1105
}
1106
$urlRegex = $urlRegex['url'];
1107
}
1108
if (preg_match($urlRegex, $urlPath)) {
571
if (isset($rules[$key]) && is_array($rules[$key])) {
572
/** @var wfWAFRuleParserURLParam $urlParam */
573
foreach ($rules[$key] as $urlParam) {
574
+ if ($urlParam->getConditional()) {
575
+
576
+ $exportedCode .= sprintf("\$this->{$key}[%s][] = array(\n%s => %s,\n%s => %s,\n%s => %s\n);\n", var_export($urlParam->getParam(), true),
577
+ var_export('url', true), var_export($urlParam->getUrl(), true),
578
+ var_export('rules', true), var_export($urlParam->getRules(), true),
579
+ var_export('conditional', true), $urlParam->getConditional()->render());
580
+ }
581
+ else {
582
+ if ($urlParam->getRules()) {
583
+ $url = array(
584
+ 'url' => $urlParam->getUrl(),
585
+ 'rules' => $urlParam->getRules(),
586
+ );
587
+ } else {
588
+ $url = $urlParam->getUrl();
589
+ }
590
+
591
+ $exportedCode .= sprintf("\$this->{$key}[%s][] = %s;\n", var_export($urlParam->getParam(), true),
592
+ var_export($url, true));
593
}
594
}
595
$exportedCode .= "\n";
596
}
978
if (!in_array($ruleID, $urlRegex['rules'])) {
979
continue;
980
}
981
+ if (isset($urlRegex['conditional']) && !$urlRegex['conditional']->evaluate()) {
982
+ continue;
983
+ }
984
$urlRegex = $urlRegex['url'];
985
}
986
if (preg_match($urlRegex, $urlPath)) {
1115
if (!in_array($ruleID, $urlRegex['rules'])) {
1116
continue;
1117
}
1118
+ if (isset($urlRegex['conditional']) && !$urlRegex['conditional']->evaluate()) {
1119
+ continue;
1120
+ }
1121
$urlRegex = $urlRegex['url'];
1122
}
1123
if (preg_match($urlRegex, $urlPath)) {
vendor/wordfence/wf-waf/src/views/403-blacklist.php CHANGED
@@ -209,5 +209,7 @@ $payload = "-----BEGIN REPORT-----\n" . implode("\n", str_split($message, 60)) .
209
210
<p><a href="#" id="reportButton" class="btn disabled" target="_blank">Report Problem</a></p>
211
212
</body>
213
</html>
209
210
<p><a href="#" id="reportButton" class="btn disabled" target="_blank">Report Problem</a></p>
211
212
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfWAFUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
213
+
214
</body>
215
</html>
vendor/wordfence/wf-waf/src/views/403-roadblock.php CHANGED
@@ -113,5 +113,7 @@ foreach ($waf->getFailedRules() as $paramKey => $categories) {
113
114
<?php endif ?>
115
116
</body>
117
</html>
113
114
<?php endif ?>
115
116
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfWAFUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
117
+
118
</body>
119
</html>
vendor/wordfence/wf-waf/src/views/403.php CHANGED
@@ -10,5 +10,7 @@
10
11
<p>A potentially unsafe operation has been detected in your request to this site.</p>
12
13
</body>
14
</html>
10
11
<p>A potentially unsafe operation has been detected in your request to this site.</p>
12
13
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfWAFUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
14
+
15
</body>
16
</html>
vendor/wordfence/wf-waf/src/views/503-lockout.php CHANGED
@@ -28,7 +28,7 @@ if (!empty($homeURL) && !empty($nonce)) : ?>
28
</form>
29
<?php endif; ?>
30
</p>
31
- <p style="font-style: italic;">Generated by Wordfence.</p>
32
</body>
33
</html>
34
<?php exit(); ?>
28
</form>
29
<?php endif; ?>
30
</p>
31
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfWAFUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
32
</body>
33
</html>
34
<?php exit(); ?>
vendor/wordfence/wf-waf/src/views/503.php CHANGED
@@ -31,5 +31,5 @@ If you are a site administrator and have been accidentally locked out, please en
31
<?php endif; ?>
32
<br /><br />
33
34
- <address>This response was generated by Wordfence.</address>
35
</body></html>
31
<?php endif; ?>
32
<br /><br />
33
34
+ <p style="color: #999999;margin-top: 2rem;"><em>Generated by Wordfence at <?php echo gmdate('D, j M Y G:i:s T', wfWAFUtils::normalizedTime()); ?>.<br>Your computer's time: <script type="application/javascript">document.write(new Date().toUTCString());</script>.</em></p>
35
</body></html>
views/reports/activity-report-email-inline.php CHANGED
@@ -331,6 +331,8 @@ h6 a:visited { color: purple !important; }
331
</tbody>
332
</table>
333
334
<?php wfHelperString::cycle(); ?>
335
336
<h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Updates Needed</h2>
331
</tbody>
332
</table>
333
334
+ <div style="font-size: 12px; font-style: italic; vertical-align: baseline; clear: both; margin: 8px 0 4px; padding: 0; border: 0;">This list may include WordPress core/plugin/theme updates, error logs, cache files, and other normal changes.</div>
335
+
336
<?php wfHelperString::cycle(); ?>
337