update wordfence
authorSteve Sutton <steve@gaslightmedia.com>
Mon, 22 Sep 2014 19:25:41 +0000 (15:25 -0400)
committerSteve Sutton <steve@gaslightmedia.com>
Mon, 22 Sep 2014 19:25:41 +0000 (15:25 -0400)
17 files changed:
wp-content/plugins/wordfence/lib/IPTraf.php
wp-content/plugins/wordfence/lib/diffResult.php
wp-content/plugins/wordfence/lib/menu_countryBlocking.php
wp-content/plugins/wordfence/lib/menu_options.php
wp-content/plugins/wordfence/lib/menu_rangeBlocking.php
wp-content/plugins/wordfence/lib/menu_whois.php
wp-content/plugins/wordfence/lib/viewFullActivityLog.php
wp-content/plugins/wordfence/lib/wfCache.php
wp-content/plugins/wordfence/lib/wfConfig.php
wp-content/plugins/wordfence/lib/wfLog.php
wp-content/plugins/wordfence/lib/wfSchema.php
wp-content/plugins/wordfence/lib/wfUnlockMsg.php
wp-content/plugins/wordfence/lib/wfUtils.php
wp-content/plugins/wordfence/lib/wfViewResult.php
wp-content/plugins/wordfence/lib/wordfenceClass.php
wp-content/plugins/wordfence/readme.txt
wp-content/plugins/wordfence/wordfence.php

index 7ddf1b0..5813772 100644 (file)
@@ -4,7 +4,7 @@
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <link rel='stylesheet' id='wordfence-main-style-css'  href='<?php echo wfUtils::getBaseURL(); ?>/css/iptraf.css?ver=<?php echo WORDFENCE_VERSION; ?>' type='text/css' media='all' />
 <body>
-<h1>Wordfence: All recent hits for IP address <?php echo htmlspecialchars($IP, ENT_QUOTES, 'UTF-8'); if($reverseLookup){ echo '[' . htmlspecialchars($reverseLookup, ENT_QUOTES, 'UTF-8') . ']'; } ?></h1>
+<h1>Wordfence: All recent hits for IP address <?php echo wp_kses($IP, array()); if($reverseLookup){ echo '[' . wp_kses($reverseLookup, array()) . ']'; } ?></h1>
 <table border="0" cellpadding="2" cellspacing="0" style="width: 900px;">
 <?php foreach($results as $key => $v){ ?>
 <tr><th>Time:</th><td><?php echo $v['timeAgo'] ?> ago -- <?php echo date(DATE_RFC822, $v['ctime']); ?> -- <?php echo $v['ctime']; ?> in Unixtime</td></tr>
 <?php if(wfUtils::hasXSS($v['URL'])){ ?>
 <tr><th>URL:</th><td><span style="color: #F00;">Possible XSS code filtered out for your security</span></td></tr>
 <?php } else { ?>
-<tr><th>URL:</th><td><a href="<?php echo $v['URL']; ?>" target="_blank"><?php echo $v['URL']; ?></a></td></tr>
+<tr><th>URL:</th><td><a href="<?php echo wp_kses($v['URL'], array()); ?>" target="_blank"><?php echo $v['URL']; ?></a></td></tr>
 <?php } ?>
 <tr><th>Type:</th><td><?php if($v['type'] == 'hit'){ echo 'Normal request'; } else if($v['type'] == '404'){ echo '<span style="color: #F00;">Page not found</span>'; } ?></td></tr>
 <?php if($v['referer']){ ?><tr><th>Referrer:</th><td><a href="<?php echo $v['referer']; ?>" target="_blank"><?php echo $v['referer']; ?></a></td></tr><?php } ?>
-<tr><th>Full Browser ID:</th><td><?php echo esc_html($v['UA']); ?></td></tr>
+<tr><th>Full Browser ID:</th><td><?php echo wp_kses($v['UA'], array()); ?></td></tr>
 <?php if($v['user']){ ?>
 <tr><th>User:</th><td><a href="<?php echo $v['user']['editLink']; ?>" target="_blank"><?php echo $v['user']['avatar'] . ' ' . $v['user']['display_name']; ?></a></td></tr>
 <?php } ?>
index 17619ff..ddbaf04 100644 (file)
        ignore this file the next time Wordfence scans your system.
 </p>
 <table border="0" style="margin: 0 0 20px 0;" class="summary">
-<tr><td>Filename:</td><td><?php echo htmlentities($_GET['file']); ?></td></tr>
+<tr><td>Filename:</td><td><?php echo wp_kses($_GET['file'], array()); ?></td></tr>
 <tr><td>File type:</td><td><?php 
        $cType = $_GET['cType'];
        if($cType == 'core'){
                echo "WordPress Core File</td></tr>";
        } else if($cType == 'theme'){
-               echo "Theme File</td></tr><tr><td>Theme Name:</td><td>" . htmlentities($_GET['cName']) . "</td></tr><tr><td>Theme Version:</td><td>" . htmlentities($_GET['cVersion']) . "</td></tr>";
+               echo "Theme File</td></tr><tr><td>Theme Name:</td><td>" . wp_kses($_GET['cName'], array()) . "</td></tr><tr><td>Theme Version:</td><td>" . wp_kses($_GET['cVersion'], array()) . "</td></tr>";
        } else if($cType == 'plugin'){
-               echo "Plugin File</td></tr><tr><td>Plugin Name:</td><td>" . htmlentities($_GET['cName']) . "</td></tr><tr><td>Plugin Version:</td><td>" . htmlentities($_GET['cVersion']) . "</td></tr>";
+               echo "Plugin File</td></tr><tr><td>Plugin Name:</td><td>" . wp_kses($_GET['cName'], array()) . "</td></tr><tr><td>Plugin Version:</td><td>" . wp_kses($_GET['cVersion'], array()) . "</td></tr>";
        } else {
                echo "Unknown Type</td></tr>";
        }
index 1a5aaa2..0e4bc1b 100644 (file)
@@ -38,20 +38,20 @@ WFAD.countryMap = <?php echo json_encode($wfBulkCountries); ?>;
                                <option value="redir"<?php if(wfConfig::get('cbl_action') == 'redir'){ echo ' selected'; } ?>>Redirect to the URL below</option>
                        </select>
                        </td></tr>
-               <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" value="<?php if(wfConfig::get('cbl_redirURL')){ echo htmlspecialchars(wfConfig::get('cbl_redirURL')); } ?>" /></td></tr>
+               <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" value="<?php if(wfConfig::get('cbl_redirURL')){ echo wp_kses(wfConfig::get('cbl_redirURL'), array()); } ?>" /></td></tr>
                <tr><th>Block countries even if they are logged in:</th><td><input type="checkbox" id="wfLoggedInBlocked" value="1" <?php if(wfConfig::get('cbl_loggedInBlocked')){ echo 'checked'; } ?> /></td></tr>
                <tr><th>Block access to the login form:</th><td><input type="checkbox" id="wfLoginFormBlocked" value="1" <?php if(wfConfig::get('cbl_loginFormBlocked')){ echo 'checked'; } ?> /></td></tr>
                <tr><th>Block access to the rest of the site (outside the login form):</th><td><input type="checkbox" id="wfRestOfSiteBlocked" value="1" <?php if(wfConfig::get('cbl_restOfSiteBlocked')){ echo 'checked'; } ?> /></td></tr>
                <tr><td colspan="2"><h2>Advanced Country Blocking Options</h2></td></tr>
                <tr><th colspan="2">
                        If user hits the URL 
-                       <input type="text" id="wfBypassRedirURL" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURL', "")); ?>" size="20" /> 
+                       <input type="text" id="wfBypassRedirURL" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirURL', ""), array()); ?>" size="20" /> 
                        then redirect that user to 
-                       <input type="text" id="wfBypassRedirDest" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURLRedir', "")); ?>" size="20" /> and set a cookie that will bypass all country blocking. 
+                       <input type="text" id="wfBypassRedirDest" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirDest', ""), array()); ?>" size="20" /> and set a cookie that will bypass all country blocking. 
                        </th></tr>
                <tr><th colspan="2">
                        If user who is allowed to access the site views the URL 
-                       <input type="text" id="wfBypassViewURL" value="<?php echo htmlspecialchars(wfConfig::get('cbl_cblBypassURL', "")); ?>" size="20" /> 
+                       <input type="text" id="wfBypassViewURL" value="<?php echo wp_kses(wfConfig::get('cbl_bypassViewURL', ""), array()); ?>" size="20" /> 
                        then set a cookie that will bypass country blocking in future in case that user hits the site from a blocked country. 
                        </th></tr>
 
index 0a0fd19..ca8c203 100644 (file)
@@ -47,7 +47,12 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
        <tr><td colspan="2">&nbsp;</td></tr>
        <tr><th class="wfConfigEnable">Enable automatic scheduled scans</th><td><input type="checkbox" id="scheduledScansEnabled" class="wfConfigElem" name="scheduledScansEnabled" value="1" <?php $w->cb('scheduledScansEnabled'); ?> />&nbsp;Regular scans ensure your site stays secure.</td></tr>
        <tr><td colspan="2">&nbsp;</td></tr>
-       <tr><th class="wfConfigEnable">Update Wordfence automatically when a new version is released?</th><td><input type="checkbox" id="autoUpdate" class="wfConfigElem" name="autoUpdate" value="1" <?php $w->cb('autoUpdate'); ?> />&nbsp;Automatically updates Wordfence to the newest version within 24 hours of a new release.</td></tr>
+       <tr><th class="wfConfigEnable">Update Wordfence automatically when a new version is released?</th><td><input type="checkbox" id="autoUpdate" class="wfConfigElem" name="autoUpdate" value="1" <?php $w->cb('autoUpdate'); ?> />&nbsp;Automatically updates Wordfence to the newest version within 24 hours of a new release.<br />
+               <?php if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){ ?>
+               <span style="color: #F00;">Warning: </span>You are running LiteSpeed web server and you don't have the "noabort" variable set in your .htaccess.<br />
+               <a href="https://support.wordfence.com/solution/articles/1000129050-running-wordfence-under-litespeed-web-server-and-preventing-process-killing-or" target="_blank">Please read this article in our FAQ to make an important change that will ensure your site stability during an update.<br />
+               <?php } ?>
+       </td></tr>
        <tr><td colspan="2">&nbsp;</td></tr>
 
        <tr><th>Where to email alerts:</th><td><input type="text" id="alertEmails" name="alertEmails" value="<?php $w->f('alertEmails'); ?>" size="50" />&nbsp;<span class="wfTipText">Separate multiple emails with commas</span></td></tr>
@@ -64,11 +69,11 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
                </td></tr>
        <tr><th>How does Wordfence get IPs:</th><td>
                <select id="howGetIPs" name="howGetIPs">
-                       <option value="">Set this option if you're seeing visitors from fake IP addresses or who appear to be from your internal network but aren't.</option>
-                       <option value="REMOTE_ADDR"<?php $w->sel('howGetIPs', 'REMOTE_ADDR'); ?>>Use PHP's built in REMOTE_ADDR. Use this if you're not using Nginx or any separate front-end proxy or firewall. Try this first.</option>
-                       <option value="HTTP_X_REAL_IP"<?php $w->sel('howGetIPs', 'HTTP_X_REAL_IP'); ?>>Use the X-Real-IP HTTP header which my Nginx, firewall or front-end proxy is setting. Try this next.</option>
-                       <option value="HTTP_X_FORWARDED_FOR"<?php $w->sel('howGetIPs', 'HTTP_X_FORWARDED_FOR'); ?>>Use the X-Forwarded-For HTTP header which my Nginx, firewall or front-end proxy is setting.</option>
-                       <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel('howGetIPs', 'HTTP_CF_CONNECTING_IP'); ?>>I'm using Cloudflare so use the "CF-Connecting-IP" HTTP header to get a visitor IP</option>
+                       <option value="">Let Wordfence use the most secure method to get visitor IP addresses. Prevents spoofing and works with most sites.</option>
+                       <option value="REMOTE_ADDR"<?php $w->sel('howGetIPs', 'REMOTE_ADDR'); ?>>Use PHP's built in REMOTE_ADDR and don't use anything else. Very secure if this is compatible with your site.</option>
+                       <option value="HTTP_X_FORWARDED_FOR"<?php $w->sel('howGetIPs', 'HTTP_X_FORWARDED_FOR'); ?>>Use the X-Forwarded-For HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
+                       <option value="HTTP_X_REAL_IP"<?php $w->sel('howGetIPs', 'HTTP_X_REAL_IP'); ?>>Use the X-Real-IP HTTP header. Only use if you have a front-end proxy or spoofing may result.</option>
+                       <option value="HTTP_CF_CONNECTING_IP"<?php $w->sel('howGetIPs', 'HTTP_CF_CONNECTING_IP'); ?>>Use the Cloudflare "CF-Connecting-IP" HTTP header to get a visitor IP. Only use if you're using Cloudflare.</option>
                </select>
                </td></tr>
        </table>
@@ -261,7 +266,9 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
        <tr><th colspan="2" style="color: #999;">Whitelisted IP's must be separated by commas. You can specify ranges using the following format: 123.23.34.[1-50]<br />Wordfence automatically whitelists <a href="http://en.wikipedia.org/wiki/Private_network" target="_blank">private networks</a> because these are not routable on the public Internet.<br /><br /></th></tr>
 
        <tr><th>Immediately block IP's that access these URLs:</th><td><input type="text" name="bannedURLs" id="bannedURLs" value="<?php echo $w->getHTML('bannedURLs'); ?>" size="40" /></td></tr>
-       <tr><th colspan="2" style="color: #999;">Separate multiple URL's with commas. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them.<br /><br /></th></tr>
+       <tr><th colspan="2" style="color: #999;">Separate multiple URL's with commas. If you see an attacker repeatedly probing your site for a known vulnerability you can use this to immediately block them.<br />
+               All URL's must start with a '/' without quotes and must be relative. e.g. /badURLone/, /bannedPage.html, /dont-access/this/URL/
+       <br /><br /></th></tr>
 
        <tr><th>Hide WordPress version</th><td><input type="checkbox" id="other_hideWPVersion" class="wfConfigElem" name="other_hideWPVersion" value="1" <?php $w->cb('other_hideWPVersion'); ?> /></td></tr>
        <tr><th>Hold anonymous comments using member emails for moderation</th><td><input type="checkbox" id="other_noAnonMemberComments" class="wfConfigElem" name="other_noAnonMemberComments" value="1" <?php $w->cb('other_noAnonMemberComments'); ?> /></td></tr>
index 94a6821..d3e6fe9 100644 (file)
@@ -16,7 +16,7 @@
                        </ul>
                        </div>
                        <table class="wfConfigForm">
-                               <tr><th>Block anyone that has an IP address in this range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php if( isset( $_GET['wfBlockRange'] ) && $_GET['wfBlockRange']){ echo htmlentities($_GET['wfBlockRange']); } ?>" onkeyup="WFAD.calcRangeTotal();">&nbsp;<span id="wfShowRangeTotal"></span></td></tr>
+                               <tr><th>Block anyone that has an IP address in this range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php if( isset( $_GET['wfBlockRange'] ) && $_GET['wfBlockRange']){ echo wp_kses($_GET['wfBlockRange'], array()); } ?>" onkeyup="WFAD.calcRangeTotal();">&nbsp;<span id="wfShowRangeTotal"></span></td></tr>
                                <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> 192.168.200.200 - 192.168.200.220</td></tr>
                                <tr><th>...you can also enter a User-Agent (browser) that matches:</th><td><input id="uaRange" type="text" size="30" maxlength="255" >&nbsp;(Case insensitive)</td></tr>
                                <tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> *badRobot*, AnotherBadRobot*, *someKindOfSuffix</td></tr>
index c52967a..7149326 100644 (file)
@@ -28,7 +28,7 @@ if(! function_exists('fsockopen')){
                <?php if( isset( $_GET['wfnetworkblock'] ) && $_GET['wfnetworkblock']){ ?>
                <h2>How to block a network</h2>
                <p style="width: 600px;">
-                       You've chosen to block the network that <span style="color: #F00;"><?php echo htmlentities($_GET['whoisval']); ?></span> is part of.
+                       You've chosen to block the network that <span style="color: #F00;"><?php echo wp_kses($_GET['whoisval'], array()); ?></span> is part of.
                        We've marked the networks we found that this IP address belongs to in red below.
                        Make sure you read all the WHOIS information so that you see all networks this IP belongs to. We recommend blocking the network with the lowest number of addresses.
                        You may find this is listed at the end as part of the 'rWHOIS' query which contacts
@@ -51,7 +51,7 @@ if(! function_exists('fsockopen')){
 </div>
 </script>
 <script type="text/javascript">
-var whoisval = "<?php if( isset( $_GET['whoisval'] ) ) { echo htmlentities($_GET['whoisval']); } ?>";
+var whoisval = "<?php if( isset( $_GET['whoisval'] ) ) { echo wp_kses($_GET['whoisval'], array()); } ?>";
 if(whoisval){
        jQuery(function(){
                jQuery('#wfwhois').val(whoisval);
index ecf23d8..85b3f24 100644 (file)
@@ -17,7 +17,7 @@ $q = $db->querySelect("select ctime, level, type, msg from $table order by ctime
 $timeOffset = 3600 * get_option('gmt_offset');
 foreach($q as $r){
        if($r['level'] < 4 || $debugOn){
-               echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
+               echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . wp_kses($r['msg'], array()) . "</div>\n";
        }
 }
 ?>
index cdbf2bb..c4a0427 100644 (file)
@@ -154,8 +154,8 @@ class wfCache {
                        $append .= "Time created on server: " . date('Y-m-d H:i:s T') . ". ";
                        $append .= "Is HTTPS page: " . (self::isHTTPSPage() ? 'HTTPS' : 'no') . ". ";
                        $append .= "Page size: " . strlen($buffer) . " bytes. ";
-                       $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? htmlentities($_SERVER['HTTP_HOST']) : htmlentities($_SERVER['SERVER_NAME'])) . ". ";
-                       $append .= "Request URI: " . htmlentities($_SERVER['REQUEST_URI']) . " ";
+                       $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? wp_kses($_SERVER['HTTP_HOST'], array()) : wp_kses($_SERVER['SERVER_NAME'], array())) . ". ";
+                       $append .= "Request URI: " . wp_kses($_SERVER['REQUEST_URI'], array()) . " ";
                        $appendGzip = $append . " Encoding: GZEncode -->\n";
                        $append .= " Encoding: Uncompressed -->\n";
                }
@@ -193,7 +193,7 @@ class wfCache {
        public static function makeDirIfNeeded($file){
                $file = preg_replace('/\/[^\/]*$/', '', $file);
                if(! is_dir($file)){
-                       mkdir($file, 0755, true);
+                       @mkdir($file, 0755, true);
                }
        }
        public static function logout(){
index 3e711bc..5fb06ac 100644 (file)
@@ -464,7 +464,7 @@ class wfConfig {
                self::$cache = array();
        }
        public static function getHTML($key){
-               return htmlspecialchars(self::get($key));
+               return wp_kses(self::get($key), array());
        }
        public static function inc($key){
                $val = self::get($key, false);
@@ -751,6 +751,16 @@ class wfConfig {
        }
        public static function autoUpdate(){
                try {
+                       if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){
+                                wordfence::alert("Wordfence Upgrade not run. Please modify your .htaccess", "To preserve the integrity of your website we are not running Wordfence auto-update.\n" .
+                                       "You are running the LiteSpeed web server which has been known to cause a problem with Wordfence auto-update.\n" .
+                                       "Please go to your website now and make a minor change to your .htaccess to fix this.\n" .
+                                       "You can find out how to make this change at:\n" .
+                                       "https://support.wordfence.com/solution/articles/1000129050-running-wordfence-under-litespeed-web-server-and-preventing-process-killing-or\n" .
+                                       "\nAlternatively you can disable auto-update on your website to stop receiving this message and upgrade Wordfence manually.\n"
+                                       );
+                               return;
+                       }
                        require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
                        require_once(ABSPATH . 'wp-admin/includes/misc.php');
                        /* We were creating show_message here so that WP did not write to STDOUT. This had the strange effect of throwing an error about redeclaring show_message function, but only when a crawler hit the site and triggered the cron job. Not a human. So we're now just require'ing misc.php which does generate output, but that's OK because it is a loopback cron request.  
index 1ee0910..1106859 100644 (file)
@@ -164,6 +164,11 @@ class wfLog {
                if(wfUtils::isPrivateAddress($IP)){
                        return true;
                }
+               //These belong to sucuri's scanning servers which will get blocked by Wordfence as a false positive if you try a scan. So we whitelisted them.
+               $externalWhite = array('97.74.127.171','69.164.203.172','173.230.128.135','66.228.34.49','66.228.40.185','50.116.36.92','50.116.36.93','50.116.3.171','198.58.96.212','50.116.63.221','192.155.92.112','192.81.128.31','198.58.106.244','192.155.95.139','23.239.9.227','198.58.112.103','192.155.94.43','162.216.16.33','173.255.233.124','173.255.233.124','192.155.90.179','50.116.41.217','192.81.129.227','198.58.111.80');
+               if(in_array($IP, $externalWhite)){
+                       return true;
+               }
                $list = wfConfig::get('whitelisted');
                if(! $list){ return false; }
                $list = explode(',', $list);
@@ -235,7 +240,7 @@ class wfLog {
                        }
                        $blockDat = explode('|', $elem['blockString']);
                        $elem['ipPattern'] = "";
-                       $haveIPBLock = false;
+                       $haveIPBlock = false;
                        $haveBrowserBlock = false;
                        if($blockDat[0]){
                                $haveIPBlock = true;
@@ -862,6 +867,7 @@ class wfLog {
                foreach($results as &$rec){
                        //$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
                        $rec['date'] = date('M d H:i:s', $rec['ctime'] + $timeOffset);
+                       $rec['msg'] = wp_kses_data( (string) $rec['msg']);
                }
                return $results;
        }
index c9e4488..aae2d28 100644 (file)
@@ -122,6 +122,7 @@ class wfSchema {
        KEY k2(endTime)
 ) default charset=utf8",
 "wfStatus" => "(
+       id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY,
        ctime DOUBLE(17,6) UNSIGNED NOT NULL,
        level tinyint UNSIGNED NOT NULL,
        type char(5) NOT NULL,
index d0a79f6..c0a87ff 100644 (file)
@@ -1,6 +1,7 @@
 If you are a site administrator and have been accidentally locked out, please enter your email in the box below and click "Send". If the email address you enter belongs to a known site administrator or someone set to receive Wordfence alerts, we will send you an email to help you regain access. <a href="http://www.wordfence.com/docs/frequently-asked-questions/#3" target="_blank">Please read our FAQ if this does not work.</a>
 <br /><br />
 <form method="POST" action="<?php echo wfUtils::getSiteBaseURL(); ?>?_wfsf=unlockEmail">
+<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('wf-form'); ?>" />
 <input type="text" size="50" name="email" value="" maxlength="255" />&nbsp;<input type="submit" name="s" value="Send me an unlock email" />
 </form>
 <br /><br />
index af49305..ab5072f 100644 (file)
@@ -106,21 +106,11 @@ class wfUtils {
                //return ABSPATH . 'wp-content/plugins/';
        }
        public static function defaultGetIP(){
-               $IP = 0;
-               if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
-                       $IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
-                       if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
-               }
-               if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['HTTP_X_REAL_IP'])){
-                       $IP = $_SERVER['HTTP_X_REAL_IP'];
-                       if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
-               }
-               if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['REMOTE_ADDR'])){
-                       $IP = $_SERVER['REMOTE_ADDR'];
-                       if(is_array($IP) && isset($IP[0])){ $IP = $IP[0]; } //It seems that some hosts may modify _SERVER vars into arrays.
-               }
                return $IP;
        }
+       public static function makeRandomIP(){
+               return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
+       }
        public static function isPrivateAddress($addr){
                $num = self::inet_aton($addr);
                foreach(self::$privateAddrs as $a){
@@ -130,80 +120,78 @@ class wfUtils {
                }
                return false;
        }
-       public static function makeRandomIP(){
-               return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
-       }
-       public static function getIP(){
-               //You can use the following examples to force Wordfence to think a visitor has a certain IP if you're testing. Remember to re-comment this out or you will break Wordfence badly. 
-               //return '1.2.33.57';
-               //return '4.22.23.114';
-               //return self::makeRandomIP();
-
-               $howGet = wfConfig::get('howGetIPs', false);
-               if($howGet){
-                       $IP = $_SERVER[$howGet];
-                       if( $howGet == "HTTP_CF_CONNECTING_IP" && (! self::isValidIP($IP)) ){
-                               $IP = $_SERVER['REMOTE_ADDR'];
-                       }
-               } else {
-                       $IP = wfUtils::defaultGetIP();
-               }
-               if(preg_match('/,/', $IP)){
-                       $parts = explode(',', $IP); //Some users have "unknown,100.100.100.100" for example so we take the first thing that looks like an IP.
-                       foreach($parts as $part){
-                               if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $part) && (! self::isPrivateAddress($part)) ){
-                                       $IP = trim($part);
-                                       break;
+       private static function getCleanIP($arr){ //Expects an array of items. The items are either IP's or IP's separated by comma, space or tab. Or an array of IP's.
+                                               //  We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found.
+               $privates = array(); //Store private addrs until end as last resort. 
+               for($i = 0; $i < count($arr); $i++){ 
+                       $item = $arr[$i];
+                       if(is_array($item)){ 
+                               foreach($item as $j){
+                                       $j = preg_replace('/:\d+$/', '', $j); //Strip off port
+                                       if(self::isValidIP($j)){
+                                               if(self::isPrivateAddress($j)){
+                                                       $privates[] = $j;
+                                               } else {
+                                                       return $j;
+                                               }
+                                       }
                                }
+                               continue; //This was an array so we can skip to the next item
                        }
-               } else if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)\s+(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)){
-                       $parts = explode(' ', $IP); //Some users have "unknown 100.100.100.100" for example so we take the first thing that looks like an IP.
-                       foreach($parts as $part){
-                               if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $part) && (! self::isPrivateAddress($part)) ){
-                                       $IP = trim($part);
+                       $skipToNext = false;
+                       foreach(array(',', ' ', "\t") as $char){
+                               if(strpos($item, $char) !== false){ 
+                                       $sp = explode($char, $item);
+                                       foreach($sp as $j){
+                                               $j = preg_replace('/:\d+$/', '', $j); //Strip off port
+                                               if(self::isValidIP($j)){
+                                                       if(self::isPrivateAddress($j)){
+                                                               $privates[] = $j;
+                                                       } else {
+                                                               return $j;
+                                                       }
+                                               }
+                                       }
+                                       $skipToNext = true;
                                        break;
                                }
                        }
-                       
-               }
-               if(preg_match('/:\d+$/', $IP)){
-                       $IP = preg_replace('/:\d+$/', '', $IP);
-               }
-               if(self::isValidIP($IP)){
-                       if(wfConfig::get('IPGetFail', false)){
-                               if(self::isPrivateAddress($IP) ){
-                                       wordfence::status(1, 'error', "Wordfence is receiving IP addresses, but we received an internal IP of $IP so your config may still be incorrect.");
+                       if($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything.
+
+                       $item = preg_replace('/:\d+$/', '', $item); //Strip off port
+                       if(self::isValidIP($item)){
+                               if(self::isPrivateAddress($item)){
+                                       $privates[] = $item;
                                } else {
-                                       wordfence::status(1, 'error', "Wordfence is now receiving IP addresses correctly. We received $IP from a visitor.");
+                                       return $item;
                                }
-                               wfConfig::set('IPGetFail', '');
                        }
-                       return $IP;
+               }
+               if(sizeof($privates) > 0){
+                       return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function.
                } else {
-                       $xFor = "";
-                       if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) ){
-                               $xFor = $_SERVER['HTTP_X_FORWARDED_FOR'];
-                       }
-                       $msg = "Wordfence can't get the IP of clients and therefore can't operate. We received IP: $IP. X-Forwarded-For was: " . $xFor . " REMOTE_ADDR was: " . $_SERVER['REMOTE_ADDR'];
-                       $possible = array();
-                       foreach($_SERVER as $key => $val){
-                               if(is_string($val) && preg_match('/^\d+\.\d+\.\d+\.\d+/', $val) && strlen($val) < 255){
-                                       if($val != '127.0.0.1'){
-                                               $possible[$key] = $val;
-                                       }
-                               }
-                       }
-                       if(sizeof($possible) > 0){
-                               $msg .= "  Headers that may contain the client IP: ";
-                               foreach($possible as $key => $val){
-                                       $msg .= "$key => $val   ";
-                               }
-                       }
-                       wordfence::status(1, 'error', $msg);
-                       wfConfig::set('IPGetFail', 1);
                        return false;
                }
        }
+       public static function getIP(){
+               //For debugging. 
+               //return '105.2.33.57';
+               //return self::makeRandomIP();
+               $howGet = wfConfig::get('howGetIPs', false);
+               if($howGet){
+                       if($howGet == 'REMOTE_ADDR'){
+                               $IP = self::getCleanIP(array($_SERVER['REMOTE_ADDR']));
+                       } else {
+                               $IP = self::getCleanIP(array($_SERVER[$howGet], $_SERVER['REMOTE_ADDR']));
+                       }
+               } else {
+                       $IPs = array($_SERVER['REMOTE_ADDR']);
+                       if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ $IPs[] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
+                       if(isset($_SERVER['HTTP_X_REAL_IP'])){ $IPs[] = $_SERVER['HTTP_X_REAL_IP']; }
+                       $IP = self::getCleanIP($IPs); 
+               }
+               return $IP; //Returns a valid IP or false. 
+       }
        public static function isValidIP($IP){
                if(preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $IP, $m)){
                        if(
index 7f3787b..84dd47c 100644 (file)
@@ -6,7 +6,7 @@
 <body>
 <h1>Wordfence: File Viewer</h1>
 <table border="0" style="margin: 0 0 20px 0;" class="summary">
-<tr><td>Filename:</td><td><?php echo htmlspecialchars($localFile, ENT_QUOTES, 'UTF-8'); ?></td></tr>
+<tr><td>Filename:</td><td><?php echo wp_kses($localFile, array()); ?></td></tr>
 <tr><td>File Size:</td><td><?php echo $fileSize; ?></td></tr>
 <tr><td>File last modified:</td><td><?php echo $fileMTime; ?></td></tr>
 </table>
index d676eba..2510b05 100644 (file)
@@ -298,6 +298,8 @@ class wordfence {
                $db->queryWriteIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
                $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileQueue");
                $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileChanges");
+               //Adding primary key to this table because some backup apps use primary key during backup. 
+               $db->queryWriteIgnoreError("alter table wp_wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
 
                $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_options'");
                if($optScanEnabled != '0' && $optScanEnabled != '1'){
@@ -533,7 +535,7 @@ class wordfence {
                }
                        
                if(! is_array($returnArr)){
-                       error_log("Function $func did not return an array and did not generate an error.");
+                       error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error.");
                        $returnArr = array();
                }
                if(isset($returnArr['nonce'])){
@@ -615,7 +617,7 @@ class wordfence {
                $user = get_user_by('email', $_POST['user_login']);
                if($user){
                        if(wfConfig::get('alertOn_lostPasswdForm')){
-                               wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email", $IP);
+                               wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: " . wp_kses($email, array()), $IP);
                        }
                }
                if(wfConfig::get('loginSecurityEnabled')){
@@ -647,6 +649,9 @@ class wordfence {
        public static function veryFirstAction(){
                $wfFunc = @$_GET['_wfsf'];
                if($wfFunc == 'unlockEmail'){
+                       if(! wp_verify_nonce(@$_POST['nonce'], 'wf-form')){
+                               die("Sorry but your browser sent an invalid security token when trying to use this form.");
+                       }
                        $numTries = get_transient('wordfenceUnlockTries');
                        if($numTries > 10){
                                echo "<html><body><h1>Please wait 3 minutes and try again</h1><p>You have used this form too much. Please wait 3 minutes and try again.</p></body></html>";
@@ -689,7 +694,7 @@ class wordfence {
                                        ));
                                wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
                        }
-                       echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"" . htmlspecialchars($email, ENT_QUOTES, 'UTF-8') . "\" 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>";
+                       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>";
                        exit();
                } else if($wfFunc == 'unlockAccess'){
                        if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', get_transient('wfunlock_' . $_GET['key']))){
@@ -1009,7 +1014,7 @@ class wordfence {
                                throw new Exception("We could not fetch a core WordPress file from the Wordfence API.");
                        }
                } catch (Exception $e){
-                       return array('errorMsg' => htmlentities($e->getMessage()));
+                       return array('errorMsg' => wp_kses($e->getMessage(), array()));
                }
        }
        public static function ajax_loadAvgSitePerf_callback(){
@@ -1024,8 +1029,8 @@ class wordfence {
                if(! wfConfig::get('isPaid')){
                        return array('errorMsg' => 'Cellphone Sign-in is only available to paid members. <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click here to upgrade now.</a>');
                }
-               $username = $_POST['username'];
-               $phone = $_POST['phone'];
+               $username = sanitize_text_field($_POST['username']);
+               $phone = sanitize_text_field($_POST['phone']);
                $user = get_user_by('login', $username);
                if(! $user){
                        return array('errorMsg' => "The username you specified does not exist.");
@@ -1037,12 +1042,12 @@ class wordfence {
                try {
                        $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $phone));
                } catch(Exception $e){
-                       return array('errorMsg' => "Could not contact Wordfence servers to generate a verification code: " . htmlentities($e->getMessage()) );
+                       return array('errorMsg' => "Could not contact Wordfence servers to generate a verification code: " . wp_kses($e->getMessage(), array()) );
                }
                if(isset($codeResult['ok']) && $codeResult['ok']){
                        $code = $codeResult['code'];
                } else if(isset($codeResult['errorMsg']) && $codeResult['errorMsg']){
-                       return array('errorMsg' => htmlentities($codeResult['errorMsg']));
+                       return array('errorMsg' => wp_kses($codeResult['errorMsg'], array()));
                } else {
                        return array('errorMsg' => "We could not generate a verification code.");
                }
@@ -1055,8 +1060,8 @@ class wordfence {
                        );
        }
        public static function ajax_twoFacActivate_callback(){
-               $userID = $_POST['userID'];
-               $code = $_POST['code'];
+               $userID = sanitize_text_field($_POST['userID']);
+               $code = sanitize_text_field($_POST['code']);
                $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
                if(! is_array($twoFactorUsers)){
                        $twoFactorUsers = array();
@@ -1071,7 +1076,7 @@ class wordfence {
                                        $user = $twoFactorUsers[$i];
                                        break;
                                } else {
-                                       return array('errorMsg' => "That is not the correct code. Please look for an SMS containing an activation code on the phone with number: " . htmlentities($twoFactorUsers[$i][1]) );
+                                       return array('errorMsg' => "That is not the correct code. Please look for an SMS containing an activation code on the phone with number: " . wp_kses($twoFactorUsers[$i][1], array()) );
                                }
                        }
                }
@@ -1281,7 +1286,7 @@ class wordfence {
                        if($r['type'] == 'error'){
                                $content .= "\n";
                        }
-                       $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . $r['msg'] . "\n";
+                       $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
                }
                $content .= "\n\n";
                
@@ -1312,7 +1317,7 @@ class wordfence {
                                throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
                        }
                } catch(Exception $e){
-                       return array('errorMsg' => "Could not fetch free API key from Wordfence: " . htmlentities($e->getMessage()));
+                       return array('errorMsg' => "Could not fetch free API key from Wordfence: " . wp_kses($e->getMessage(), array()));
                }
                return array('ok' => 1);
        }
@@ -1622,7 +1627,7 @@ class wordfence {
                                }
                        }
                        if(sizeof($badEmails) > 0){
-                               return array('errorMsg' => "The following emails are invalid: " . htmlentities(implode(', ', $badEmails)) );
+                               return array('errorMsg' => "The following emails are invalid: " . wp_kses(implode(', ', $badEmails), array()) );
                        }
                        $opts['alertEmails'] = implode(',', $emails);
                } else {
@@ -1643,7 +1648,7 @@ class wordfence {
                                }
                        }
                        if(sizeof($badWhiteIPs) > 0){
-                               return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . htmlentities(implode(', ', $badWhiteIPs)) );
+                               return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . wp_kses(implode(', ', $badWhiteIPs), array()) );
                        }
                        $opts['whitelisted'] = implode(',', $whiteIPs);
                } else {
@@ -1680,7 +1685,7 @@ class wordfence {
                }
 
                if(sizeof($invalidUsers) > 0){
-                       return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . htmlentities(implode(', ', $invalidUsers)) );
+                       return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . wp_kses(implode(', ', $invalidUsers), array()) );
                }
                if(sizeof($validUsers) > 0){
                        $opts['liveTraf_ignoreUsers'] = implode(',', $validUsers);
@@ -1700,7 +1705,7 @@ class wordfence {
                        }
                }
                if(sizeof($invalidIPs) > 0){
-                       return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . htmlentities(implode(', ', $invalidIPs)) );
+                       return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . wp_kses(implode(', ', $invalidIPs), array()) );
                }
                if(sizeof($validIPs) > 0){
                        $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
@@ -1757,7 +1762,7 @@ class wordfence {
                                        throw new Exception("We could not understand the Wordfence server's response because it did not contain an 'ok' and 'apiKey' element.");
                                }
                        } catch(Exception $e){
-                               return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . htmlentities($e->getMessage()) );
+                               return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . wp_kses($e->getMessage(), array()) );
                        }
                } else if($opts['apiKey'] != wfConfig::get('apiKey')){
                        $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
@@ -1774,7 +1779,7 @@ class wordfence {
                                        throw new Exception("We could not understand the Wordfence API server reply when updating your API key.");
                                }
                        } catch (Exception $e){
-                               return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . htmlentities($e->getMessage()) );
+                               return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . wp_kses($e->getMessage(), array()) );
                        }
                } else {
                        $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
@@ -1853,7 +1858,7 @@ class wordfence {
                        }
                        $clientIP = wfUtils::inet_aton(wfUtils::getIP());
                        if($ip1 <= $clientIP && $ip2 >= $clientIP){
-                               return array('err' => 1, 'errorMsg' => "You are trying to block yourself. Your IP address is " . htmlentities(wfUtils::getIP()) . " which falls into the range " . htmlentities($ipRange) . ". This blocking action has been cancelled so that you don't block yourself from your website.");
+                               return array('err' => 1, 'errorMsg' => "You are trying to block yourself. Your IP address is " . wp_kses(wfUtils::getIP(), array()) . " which falls into the range " . wp_kses($ipRange, array()) . ". This blocking action has been cancelled so that you don't block yourself from your website.");
                        }
                        $ipRange = $ip1 . '-' . $ip2;
                }
@@ -1881,7 +1886,7 @@ class wordfence {
                        return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
                }
                if(self::getLog()->isWhitelisted($IP)){
-                       return array('err' => 1, 'errorMsg' => "The IP address " . htmlentities($IP) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
+                       return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
                }
                if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google 
                        if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){
@@ -1956,7 +1961,7 @@ class wordfence {
                $issues = new wfIssues();
                $jsonData = array(
                        'serverTime' => $serverTime,
-                       'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1")
+                       'msg' => wp_kses_data( (string) $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1"))
                        );
                $events = array();
                $alsoGet = $_POST['alsoGet'];
@@ -2001,13 +2006,14 @@ class wordfence {
                return array('ok' => 1, 'email' => $email);
        }
        public static function ajax_bulkOperation_callback(){
-               $op = $_POST['op'];
+               $op = sanitize_text_field($_POST['op']);
                if($op == 'del' || $op == 'repair'){
                        $ids = $_POST['ids'];
                        $filesWorkedOn = 0;
                        $errors = array();
                        $issues = new wfIssues();
                        foreach($ids as $id){
+                               $id = intval($id); //Make sure input is a number. 
                                $issue = $issues->getIssueByID($id);
                                if(! $issue){
                                        $errors[] = "Could not delete one of the files because we could not find the issue. Perhaps it's been resolved?";
@@ -2017,7 +2023,7 @@ class wordfence {
                                $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
                                $localFile = realpath($localFile);
                                if(strpos($localFile, ABSPATH) !== 0){
-                                       $errors[] = "An invalid file was requested: " . htmlentities($file);
+                                       $errors[] = "An invalid file was requested: " . wp_kses($file, arra());
                                        continue;
                                }
                                if($op == 'del'){
@@ -2026,7 +2032,7 @@ class wordfence {
                                                $filesWorkedOn++;
                                        } else {
                                                $err = error_get_last();
-                                               $errors[] = "Could not delete file " . htmlentities($file) . ". Error was: " . htmlentities($err['message']);
+                                               $errors[] = "Could not delete file " . wp_kses($file, array()) . ". Error was: " . wp_kses($err['message'], array());
                                        }
                                } else if($op == 'repair'){
                                        $dat = $issue['data'];  
@@ -2035,21 +2041,21 @@ class wordfence {
                                                $errors[] = $result['cerrorMsg'];
                                                continue;
                                        } else if(! $result['fileContent']){
-                                               $errors[] = "We could not get the original file of " . htmlentities($file) . " to do a repair.";
+                                               $errors[] = "We could not get the original file of " . wp_kses($file, array()) . " to do a repair.";
                                                continue;
                                        }
                                        
                                        if(preg_match('/\.\./', $file)){
-                                               $errors[] = "An invalid file " . htmlentities($file) . " was specified for repair.";
+                                               $errors[] = "An invalid file " . wp_kses($file, array()) . " was specified for repair.";
                                                continue;
                                        }
                                        $fh = fopen($localFile, 'w');
                                        if(! $fh){
                                                $err = error_get_last();
                                                if(preg_match('/Permission denied/i', $err['message'])){
-                                                       $errMsg = "You don't have permission to repair " . htmlentities($file) . ". You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
+                                                       $errMsg = "You don't have permission to repair " . wp_kses($file, array()) . ". You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
                                                } else {
-                                                       $errMsg = "We could not write to " . htmlentities($file) . ". The error was: " . $err['message'];
+                                                       $errMsg = "We could not write to " . wp_kses($file, array()) . ". The error was: " . $err['message'];
                                                }
                                                $errors[] = $errMsg;
                                                continue;
@@ -2059,7 +2065,7 @@ class wordfence {
                                        flock($fh, LOCK_UN);
                                        fclose($fh);
                                        if($bytes < 1){
-                                               $errors[] = "We could not write to " . htmlentities($file) . ". ($bytes bytes written) You may not have permission to modify files on your WordPress server.";
+                                               $errors[] = "We could not write to " . wp_kses($file, array()) . ". ($bytes bytes written) You may not have permission to modify files on your WordPress server.";
                                                continue;
                                        }
                                        $filesWorkedOn++;
@@ -2090,7 +2096,7 @@ class wordfence {
                }
        }
        public static function ajax_deleteFile_callback(){
-               $issueID = $_POST['issueID'];
+               $issueID = intval($_POST['issueID']);
                $wfIssues = new wfIssues();
                $issue = $wfIssues->getIssueByID($issueID);
                if(! $issue){
@@ -2114,11 +2120,11 @@ class wordfence {
                                );
                } else {
                        $err = error_get_last();
-                       return array('errorMsg' => "Could not delete file " . htmlentities($file) . ". The error was: " . htmlentities($err['message']));
+                       return array('errorMsg' => "Could not delete file " . wp_kses($file, array()) . ". The error was: " . wp_kses($err['message'], array()));
                }
        }
        public static function ajax_restoreFile_callback(){
-               $issueID = $_POST['issueID'];
+               $issueID = intval($_POST['issueID']);
                $wfIssues = new wfIssues();
                $issue = $wfIssues->getIssueByID($issueID);
                if(! $issue){
@@ -2164,7 +2170,7 @@ class wordfence {
                self::status(4, 'info', "Ajax request received to start scan.");
                $err = wfScanEngine::startScan();
                if($err){
-                       return array('errorMsg' => htmlentities($err));
+                       return array('errorMsg' => wp_kses($err, array()));
                } else {
                        return array("ok" => 1);
                }
@@ -2343,7 +2349,7 @@ EOL;
                return ($a['ctime'] < $b['ctime']) ? -1 : 1;
        }
        public static function wfFunc_view(){
-               $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', $_GET['file']);
+               $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', sanitize_text_field($_GET['file']));
                if(strpos($localFile, '..') !== false){
                        echo "Invalid file requested. (Relative paths not allowed)";
                        exit();
@@ -2386,7 +2392,7 @@ EOL;
 
                $result = self::getWPFileContent($_GET['file'], $_GET['cType'], $_GET['cName'], $_GET['cVersion']);
                if( isset( $result['errorMsg'] ) && $result['errorMsg']){
-                       echo htmlentities($result['errorMsg']);
+                       echo wp_kses($result['errorMsg'], array());
                        exit(0);
                } else if(! $result['fileContent']){
                        echo "We could not get the contents of the original file to do a comparison.";
@@ -2483,7 +2489,7 @@ EOL;
                        $activationError = substr($activationError, 0, 400) . '...[output truncated]';
                }
                if($activationError){
-                       echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Wordfence generated an error on activation. The output we received during activation was:</strong> ' . htmlspecialchars($activationError) . '</p></div>';
+                       echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Wordfence generated an error on activation. The output we received during activation was:</strong> ' . wp_kses($activationError, array()) . '</p></div>';
                }
                delete_option('wf_plugin_act_error');
        }
index 34b8e58..391ff1c 100644 (file)
@@ -3,7 +3,7 @@ Contributors: mmaunder
 Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching
 Requires at least: 3.3.1
 Tested up to: 4.0
-Stable tag: 5.2.4
+Stable tag: 5.2.5
 
 Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure. 
 
@@ -163,6 +163,23 @@ cause a security hole on your site.
 
 == Changelog ==
 
+= 5.2.5 =
+* Security release. Update immediately. Thanks to Julio Potier. 
+* Code hardening including improved sanitization and an additional nonce for unlock email form. Special thanks to Ryan Satterfield for the hard work.
+* Stability of auto-update improved for LiteSpeed customers. We auto-detect if you don't have E=noabort:1 in your .htaccess and give you instructions. 
+* Auto-update also disabled now for LiteSpeed customers who don't have E=noabort:1 and you will get an email alert with an explanation.
+* Fixed a bug that may cause you to have advanced blocking patterns disabled with falcon engine enabled that should not be disabled. 
+* Removed a benign warning in wfCache.php. 
+* Added clarity to the banned URL option on the options page. All URL's must be relative.
+* Added a primary key to the wp_wfStatus table which is required for certain incremental backup plugins and utilities.
+* Fixed advanced country blocking which was not correctly displaying advanced options. 
+* Migrated to using wp_kses() for sanitization. 
+* Prevent IP spoofing in default Wordfence IP configuration. 
+* Change explanations of how Wordfence gets IP's to make it clear which to use to prevent spoofing. 
+* Make it clear that the option to have IP's immediately blocked when they access a URL requires relative URL's starting with a forward slash. 
+* Whitelist Sucuri's scanning IP addresses which were getting blocked because they triggered Wordfence blocking during a scan.
+* Improved Wordfence's code that acquires the visitor IP to block certain spoofing attacks, be more platform agnostic and deal with visits from private IP's more elegantly.
+
 = 5.2.4 =
 * Security release. Upgrade immediately. 
 * This release fixes an XSS vunlerability on Wordfence "view all traffic from IP" page. 
index 5ae43a2..af1fc6c 100644 (file)
@@ -4,13 +4,13 @@ Plugin Name: Wordfence Security
 Plugin URI: http://www.wordfence.com/
 Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
 Author: Wordfence
-Version: 5.2.4
+Version: 5.2.5
 Author URI: http://www.wordfence.com/
 */
 if(defined('WP_INSTALLING') && WP_INSTALLING){
        return;
 }
-define('WORDFENCE_VERSION', '5.2.4');
+define('WORDFENCE_VERSION', '5.2.5');
 if(get_option('wordfenceActivated') != 1){
        add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error',  ob_get_contents()); }
 }