Version Description
- Feature: IPv6 fully supported. This includes whois, range blocking, IPv6 city lookup in live traffic, country blocking and all other security functions. See www.wordfence.com/blog/ for more info.
- Feature: New scanning routine examines the wp_options table for executable code based on a new infection we are seeing that is well hidden.
- Improvement: Prevent Googlebot from being blocked if user has configured a banned URL and Google tries to crawl it.
- Improvement: Improved detection for additional Google crawlers especially if an IP PTR resolves to a .googlebot.com domain.
- Fix: Fixed bug with https:// URLs not allowed in country blocking.
- Fix: Fixed typos.
Download this release
Release Info
Developer | mmaunder |
Plugin | ![]() |
Version | 6.0.1 |
Comparing to | |
See all releases |
Code changes from version 5.3.12 to 6.0.1
- css/main.css +24 -14
- js/admin.js +119 -7
- lib/GeoIP.dat +0 -0
- lib/dashboard.php +1 -0
- lib/menu_options.php +8 -0
- lib/menu_rangeBlocking.php +3 -3
- lib/menu_scan.php +51 -0
- lib/wfActivityReport.php +6 -4
- lib/wfCache.php +18 -3
- lib/wfConfig.php +5 -0
- lib/wfCrawl.php +11 -2
- lib/wfHelperBin.php +78 -0
- lib/wfIssues.php +9 -3
- lib/wfLog.php +242 -88
- lib/wfScanEngine.php +50 -5
- lib/wfUtils.php +220 -77
- lib/wordfenceClass.php +140 -77
- lib/wordfenceConstants.php +2 -1
- lib/wordfenceHash.php +9 -0
- lib/wordfenceScanner.php +63 -14
- readme.txt +19 -4
- views/reports/activity-report-email-inline.php +1 -1
- views/reports/activity-report-email.php +1 -1
- views/reports/activity-report.php +1 -1
- wordfence.php +2 -2
css/main.css
CHANGED
@@ -144,17 +144,17 @@ table.wfSummaryParent { font-family: sans-serif; font-size: 14px; color: #000; z
|
|
144 |
table.wfSummaryParent td { vertical-align: top; padding: 0; margin: 0; }
|
145 |
table.wfSummaryParent table.wfSummaryChild th { font-weight: bold; text-align: right; font-family: Georgia, serif; color: #000; padding: 5px 10px 5px 0; border-top: 1px solid #CCC; }
|
146 |
table.wfSummaryParent table.wfSummaryChild td { font-weight: normal; text-align: left; padding: 5px 0 5px 0; border-top: 1px solid #CCC; }
|
147 |
-
table.wfSummaryParent table.wfSC1 td { width: 300px; padding:
|
148 |
table.wfSummaryParent table.wfSC2 th { width: 80px; }
|
149 |
table.wfSummaryParent table.wfSC2 td { width: 100px; }
|
150 |
table.wfSummaryParent table.wfSC3 th { width: 80px; }
|
151 |
table.wfSummaryParent table.wfSC3 td { width: 250px; }
|
152 |
-
table.wfSummaryParent th.wfHead { font-size: 22px; font-family: Georgia, serif; font-style: italic; color: #555; font-weight: bold; text-align: left; padding: 20px 0 20px
|
153 |
|
154 |
-
div.wfIssue table.wfIssue td { padding: 2px; margin: 0; border-width:
|
155 |
div.wfIssue table.wfIssue th { padding: 2px; margin: 0; font-weight: bold; text-align: left; color: #777; }
|
156 |
-
div.wfIssue h2 { margin:
|
157 |
-
div.wfIssue table.wfIssueLinks td { border-width:
|
158 |
.wfIssueOptions {
|
159 |
border-top: 1px solid #CCC;
|
160 |
padding: 10px;
|
@@ -210,7 +210,7 @@ table th.wfSubheading { font-weight: bold; padding-top: 10px; }
|
|
210 |
padding: 0 0 0 18px;
|
211 |
margin: 0;
|
212 |
right: 10px;
|
213 |
-
top:
|
214 |
background-repeat: no-repeat;
|
215 |
font-weight: normal;
|
216 |
}
|
@@ -338,7 +338,7 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
338 |
.wfFalcon {
|
339 |
width: 63px;
|
340 |
height: 63px;
|
341 |
-
border-width:
|
342 |
background-color: transparent;
|
343 |
background-image: url(../images/wordfenceFalcon.png);
|
344 |
background-position: 0 0;
|
@@ -350,7 +350,7 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
350 |
.wfFalconImage {
|
351 |
width: 63px;
|
352 |
height: 63px;
|
353 |
-
border-width:
|
354 |
background-color: transparent;
|
355 |
background-image: url(../images/wordfenceFalcon.png);
|
356 |
background-position: 0 0;
|
@@ -360,13 +360,13 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
360 |
.wfSmallFalcon {
|
361 |
width: 33px;
|
362 |
height: 16px;
|
363 |
-
border-width:
|
364 |
background-color: transparent;
|
365 |
background-image: url(../images/wordfenceFalconSmall.png);
|
366 |
background-position: 0 0;
|
367 |
background-repeat: no-repeat;
|
368 |
-
margin:
|
369 |
-
padding:
|
370 |
display: inline;
|
371 |
float: right;
|
372 |
}
|
@@ -394,7 +394,7 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
394 |
font-size: 14px !important ; color: white !important ; font-family: Trebuchet, Arial, sans-serif !important ; font-weight: bold !important ;
|
395 |
-moz-box-sizing: border-box !important ; -webkit-box-sizing: border-box !important ; box-sizing: border-box !important ;
|
396 |
border-radius: 19px !important ;
|
397 |
-
box-shadow:
|
398 |
}
|
399 |
.wfOnOffSwitch-inner:before {
|
400 |
content: "ON" !important ;
|
@@ -410,7 +410,7 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
410 |
border-radius: 0 19px 19px 0 !important ;
|
411 |
}
|
412 |
.wfOnOffSwitch-switch {
|
413 |
-
width: 19px !important ; margin:
|
414 |
background: #FFFFFF !important ;
|
415 |
border: 2px solid #999999 !important ; border-radius: 19px !important ;
|
416 |
position: absolute !important ; top: 0 !important ; bottom: 0 !important ; right: 46px !important ;
|
@@ -426,7 +426,7 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
|
|
426 |
margin-left: 0 !important ;
|
427 |
}
|
428 |
.wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
|
429 |
-
right:
|
430 |
}
|
431 |
#wordfenceConfigWarning {
|
432 |
clear: left;
|
@@ -461,3 +461,13 @@ table.wf-table tbody tr:nth-child(2n) td {
|
|
461 |
table.wf-table tbody tr:hover td {
|
462 |
background-color: #fffbd8;
|
463 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
table.wfSummaryParent td { vertical-align: top; padding: 0; margin: 0; }
|
145 |
table.wfSummaryParent table.wfSummaryChild th { font-weight: bold; text-align: right; font-family: Georgia, serif; color: #000; padding: 5px 10px 5px 0; border-top: 1px solid #CCC; }
|
146 |
table.wfSummaryParent table.wfSummaryChild td { font-weight: normal; text-align: left; padding: 5px 0 5px 0; border-top: 1px solid #CCC; }
|
147 |
+
table.wfSummaryParent table.wfSC1 td { width: 300px; padding: 0 25px 10px 0; }
|
148 |
table.wfSummaryParent table.wfSC2 th { width: 80px; }
|
149 |
table.wfSummaryParent table.wfSC2 td { width: 100px; }
|
150 |
table.wfSummaryParent table.wfSC3 th { width: 80px; }
|
151 |
table.wfSummaryParent table.wfSC3 td { width: 250px; }
|
152 |
+
table.wfSummaryParent th.wfHead { font-size: 22px; font-family: Georgia, serif; font-style: italic; color: #555; font-weight: bold; text-align: left; padding: 20px 0 20px 0; -webkit-font-smoothing: antialiased; }
|
153 |
|
154 |
+
div.wfIssue table.wfIssue td { padding: 2px; margin: 0; border-width: 0; text-align: left; }
|
155 |
div.wfIssue table.wfIssue th { padding: 2px; margin: 0; font-weight: bold; text-align: left; color: #777; }
|
156 |
+
div.wfIssue h2 { margin: 0 0 5px 0; padding: 0; }
|
157 |
+
div.wfIssue table.wfIssueLinks td { border-width: 0; text-align: left; padding-right: 10px; }
|
158 |
.wfIssueOptions {
|
159 |
border-top: 1px solid #CCC;
|
160 |
padding: 10px;
|
210 |
padding: 0 0 0 18px;
|
211 |
margin: 0;
|
212 |
right: 10px;
|
213 |
+
top: 0;
|
214 |
background-repeat: no-repeat;
|
215 |
font-weight: normal;
|
216 |
}
|
338 |
.wfFalcon {
|
339 |
width: 63px;
|
340 |
height: 63px;
|
341 |
+
border-width: 0;
|
342 |
background-color: transparent;
|
343 |
background-image: url(../images/wordfenceFalcon.png);
|
344 |
background-position: 0 0;
|
350 |
.wfFalconImage {
|
351 |
width: 63px;
|
352 |
height: 63px;
|
353 |
+
border-width: 0;
|
354 |
background-color: transparent;
|
355 |
background-image: url(../images/wordfenceFalcon.png);
|
356 |
background-position: 0 0;
|
360 |
.wfSmallFalcon {
|
361 |
width: 33px;
|
362 |
height: 16px;
|
363 |
+
border-width: 0;
|
364 |
background-color: transparent;
|
365 |
background-image: url(../images/wordfenceFalconSmall.png);
|
366 |
background-position: 0 0;
|
367 |
background-repeat: no-repeat;
|
368 |
+
margin: 0;
|
369 |
+
padding: 0;
|
370 |
display: inline;
|
371 |
float: right;
|
372 |
}
|
394 |
font-size: 14px !important ; color: white !important ; font-family: Trebuchet, Arial, sans-serif !important ; font-weight: bold !important ;
|
395 |
-moz-box-sizing: border-box !important ; -webkit-box-sizing: border-box !important ; box-sizing: border-box !important ;
|
396 |
border-radius: 19px !important ;
|
397 |
+
box-shadow: 0 9.5px 0 rgba(0,0,0,0.08) inset !important ;
|
398 |
}
|
399 |
.wfOnOffSwitch-inner:before {
|
400 |
content: "ON" !important ;
|
410 |
border-radius: 0 19px 19px 0 !important ;
|
411 |
}
|
412 |
.wfOnOffSwitch-switch {
|
413 |
+
width: 19px !important ; margin: 0 !important ;
|
414 |
background: #FFFFFF !important ;
|
415 |
border: 2px solid #999999 !important ; border-radius: 19px !important ;
|
416 |
position: absolute !important ; top: 0 !important ; bottom: 0 !important ; right: 46px !important ;
|
426 |
margin-left: 0 !important ;
|
427 |
}
|
428 |
.wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
|
429 |
+
right: 0 !important ;
|
430 |
}
|
431 |
#wordfenceConfigWarning {
|
432 |
clear: left;
|
461 |
table.wf-table tbody tr:hover td {
|
462 |
background-color: #fffbd8;
|
463 |
}
|
464 |
+
|
465 |
+
table.block-ranges-table {
|
466 |
+
border-collapse: collapse;
|
467 |
+
margin: 10px 0 0;
|
468 |
+
}
|
469 |
+
table.block-ranges-table tr td {
|
470 |
+
border: 1px solid #CCC;
|
471 |
+
border-width: 1px 0;
|
472 |
+
padding: 10px 0 12px 0;
|
473 |
+
}
|
js/admin.js
CHANGED
@@ -938,6 +938,27 @@
|
|
938 |
});
|
939 |
}
|
940 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
941 |
restoreFile: function(issueID) {
|
942 |
var self = this;
|
943 |
this.ajax('wordfence_restoreFile', {
|
@@ -1240,6 +1261,9 @@
|
|
1240 |
makeViewFileLink: function(file) {
|
1241 |
return WordfenceAdminVars.siteBaseURL + '?_wfsf=view&nonce=' + this.nonce + '&file=' + encodeURIComponent(file);
|
1242 |
},
|
|
|
|
|
|
|
1243 |
makeTimeAgo: function(t) {
|
1244 |
var months = Math.floor(t / (86400 * 30));
|
1245 |
var days = Math.floor(t / 86400);
|
@@ -1341,13 +1365,16 @@
|
|
1341 |
|
1342 |
function wfm21(str, ipRange, offset, totalStr) {
|
1343 |
var ips = ipRange.split(/\s*\-\s*/);
|
1344 |
-
var
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
|
|
|
|
|
|
1348 |
}
|
1349 |
|
1350 |
-
res.result.rawdata[i] = res.result.rawdata[i].replace(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} - \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3
|
1351 |
rawhtml += res.result.rawdata[i] + "<br />";
|
1352 |
}
|
1353 |
jQuery('#wfrawhtml').html(rawhtml);
|
@@ -1360,9 +1387,16 @@
|
|
1360 |
this.colorbox('300px', "Please specify a reason", "You forgot to include a reason you're blocking this IP range. We ask you to include this for your own record keeping.");
|
1361 |
return;
|
1362 |
}
|
1363 |
-
ipRange = ipRange.replace(/ /g,
|
1364 |
if (ipRange) {
|
1365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1366 |
this.colorbox('300px', 'Specify a valid IP range', "Please specify a valid IP address range in the form of \"1.2.3.4 - 1.2.3.5\" without quotes. Make sure the dash between the IP addresses in a normal dash (a minus sign on your keyboard) and not another character that looks like a dash.");
|
1367 |
return;
|
1368 |
}
|
@@ -1845,6 +1879,84 @@
|
|
1845 |
}
|
1846 |
return d;
|
1847 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1848 |
removeCacheExclusion: function(id) {
|
1849 |
this.ajax('wordfence_removeCacheExclusion', {id: id}, function(res) {
|
1850 |
window.location.reload(true);
|
938 |
});
|
939 |
}
|
940 |
},
|
941 |
+
deleteDatabaseOption: function(issueID) {
|
942 |
+
var self = this;
|
943 |
+
this.ajax('wordfence_deleteDatabaseOption', {
|
944 |
+
issueID: issueID
|
945 |
+
}, function(res) {
|
946 |
+
self.doneDeleteDatabaseOption(res);
|
947 |
+
});
|
948 |
+
},
|
949 |
+
doneDeleteDatabaseOption: function(res) {
|
950 |
+
var cb = false;
|
951 |
+
var self = this;
|
952 |
+
if (res.ok) {
|
953 |
+
this.loadIssues(function() {
|
954 |
+
self.colorbox('400px', "Success removing option", "The option " + res.option_name + " was successfully removed.");
|
955 |
+
});
|
956 |
+
} else if (res.cerrorMsg) {
|
957 |
+
this.loadIssues(function() {
|
958 |
+
self.colorbox('400px', 'An error occurred', res.cerrorMsg);
|
959 |
+
});
|
960 |
+
}
|
961 |
+
},
|
962 |
restoreFile: function(issueID) {
|
963 |
var self = this;
|
964 |
this.ajax('wordfence_restoreFile', {
|
1261 |
makeViewFileLink: function(file) {
|
1262 |
return WordfenceAdminVars.siteBaseURL + '?_wfsf=view&nonce=' + this.nonce + '&file=' + encodeURIComponent(file);
|
1263 |
},
|
1264 |
+
makeViewOptionLink: function(option, siteID) {
|
1265 |
+
return WordfenceAdminVars.siteBaseURL + '?_wfsf=viewOption&nonce=' + this.nonce + '&option=' + encodeURIComponent(option) + '&site_id=' + encodeURIComponent(siteID);
|
1266 |
+
},
|
1267 |
makeTimeAgo: function(t) {
|
1268 |
var months = Math.floor(t / (86400 * 30));
|
1269 |
var days = Math.floor(t / 86400);
|
1365 |
|
1366 |
function wfm21(str, ipRange, offset, totalStr) {
|
1367 |
var ips = ipRange.split(/\s*\-\s*/);
|
1368 |
+
var totalIPs = NaN;
|
1369 |
+
if (ips[0].indexOf(':') < 0) {
|
1370 |
+
var ip1num = self.inet_aton(ips[0]);
|
1371 |
+
var ip2num = self.inet_aton(ips[1]);
|
1372 |
+
totalIPs = ip2num - ip1num + 1;
|
1373 |
+
}
|
1374 |
+
return "<a href=\"admin.php?page=WordfenceRangeBlocking&wfBlockRange=" + ipRange + "\"" + redStyle + ">" + ipRange + " [" + (!isNaN(totalIPs) ? "<strong>" + totalIPs + "</strong> addresses in this network." : "") + "Click to block this network]<\/a>";
|
1375 |
}
|
1376 |
|
1377 |
+
res.result.rawdata[i] = res.result.rawdata[i].replace(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} - \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[a-f0-9:.]{3,} - [a-f0-9:.]{3,})/i, wfm21);
|
1378 |
rawhtml += res.result.rawdata[i] + "<br />";
|
1379 |
}
|
1380 |
jQuery('#wfrawhtml').html(rawhtml);
|
1387 |
this.colorbox('300px', "Please specify a reason", "You forgot to include a reason you're blocking this IP range. We ask you to include this for your own record keeping.");
|
1388 |
return;
|
1389 |
}
|
1390 |
+
ipRange = ipRange.replace(/ /g, '').toLowerCase();
|
1391 |
if (ipRange) {
|
1392 |
+
var range = ipRange.split('-'),
|
1393 |
+
validRange = false;
|
1394 |
+
if (range[0].match(':')) {
|
1395 |
+
validRange = this.inet_pton(range[0]) !== false && this.inet_pton(range[1]) !== false;
|
1396 |
+
} else if (range[0].match('.')) {
|
1397 |
+
validRange = this.inet_aton(range[0]) !== false && this.inet_aton(range[1]) !== false;
|
1398 |
+
}
|
1399 |
+
if (!validRange) {
|
1400 |
this.colorbox('300px', 'Specify a valid IP range', "Please specify a valid IP address range in the form of \"1.2.3.4 - 1.2.3.5\" without quotes. Make sure the dash between the IP addresses in a normal dash (a minus sign on your keyboard) and not another character that looks like a dash.");
|
1401 |
return;
|
1402 |
}
|
1879 |
}
|
1880 |
return d;
|
1881 |
},
|
1882 |
+
|
1883 |
+
inet_pton: function(a) {
|
1884 |
+
// discuss at: http://phpjs.org/functions/inet_pton/
|
1885 |
+
// original by: Theriault
|
1886 |
+
// example 1: inet_pton('::');
|
1887 |
+
// returns 1: '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
|
1888 |
+
// example 2: inet_pton('127.0.0.1');
|
1889 |
+
// returns 2: '\x7F\x00\x00\x01'
|
1890 |
+
|
1891 |
+
var r, m, x, i, j, f = String.fromCharCode;
|
1892 |
+
m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/); // IPv4
|
1893 |
+
if (m) {
|
1894 |
+
m = m[0].split('.');
|
1895 |
+
m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
|
1896 |
+
// Return if 4 bytes, otherwise false.
|
1897 |
+
return m.length === 4 ? m : false;
|
1898 |
+
}
|
1899 |
+
r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})#x2F;;
|
1900 |
+
m = a.match(r); // IPv6
|
1901 |
+
if (m) {
|
1902 |
+
// Translate each hexadecimal value.
|
1903 |
+
for (j = 1; j < 4; j++) {
|
1904 |
+
// Indice 2 is :: and if no length, continue.
|
1905 |
+
if (j === 2 || m[j].length === 0) {
|
1906 |
+
continue;
|
1907 |
+
}
|
1908 |
+
m[j] = m[j].split(':');
|
1909 |
+
for (i = 0; i < m[j].length; i++) {
|
1910 |
+
m[j][i] = parseInt(m[j][i], 16);
|
1911 |
+
// Would be NaN if it was blank, return false.
|
1912 |
+
if (isNaN(m[j][i])) {
|
1913 |
+
return false; // Invalid IP.
|
1914 |
+
}
|
1915 |
+
m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
|
1916 |
+
}
|
1917 |
+
m[j] = m[j].join('');
|
1918 |
+
}
|
1919 |
+
x = m[1].length + m[3].length;
|
1920 |
+
if (x === 16) {
|
1921 |
+
return m[1] + m[3];
|
1922 |
+
} else if (x < 16 && m[2].length > 0) {
|
1923 |
+
return m[1] + (new Array(16 - x + 1))
|
1924 |
+
.join('\x00') + m[3];
|
1925 |
+
}
|
1926 |
+
}
|
1927 |
+
return false; // Invalid IP.
|
1928 |
+
},
|
1929 |
+
inet_ntop: function(a) {
|
1930 |
+
// discuss at: http://phpjs.org/functions/inet_ntop/
|
1931 |
+
// original by: Theriault
|
1932 |
+
// example 1: inet_ntop('\x7F\x00\x00\x01');
|
1933 |
+
// returns 1: '127.0.0.1'
|
1934 |
+
// example 2: inet_ntop('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1');
|
1935 |
+
// returns 2: '::1'
|
1936 |
+
|
1937 |
+
var i = 0,
|
1938 |
+
m = '',
|
1939 |
+
c = [];
|
1940 |
+
a += '';
|
1941 |
+
if (a.length === 4) { // IPv4
|
1942 |
+
return [
|
1943 |
+
a.charCodeAt(0), a.charCodeAt(1), a.charCodeAt(2), a.charCodeAt(3)].join('.');
|
1944 |
+
} else if (a.length === 16) { // IPv6
|
1945 |
+
for (i = 0; i < 16; i++) {
|
1946 |
+
c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i))
|
1947 |
+
.toString(16));
|
1948 |
+
}
|
1949 |
+
return c.join(':')
|
1950 |
+
.replace(/((^|:)0(?=:|$))+:?/g, function(t) {
|
1951 |
+
m = (t.length > m.length) ? t : m;
|
1952 |
+
return t;
|
1953 |
+
})
|
1954 |
+
.replace(m || ' ', '::');
|
1955 |
+
} else { // Invalid length
|
1956 |
+
return false;
|
1957 |
+
}
|
1958 |
+
},
|
1959 |
+
|
1960 |
removeCacheExclusion: function(id) {
|
1961 |
this.ajax('wordfence_removeCacheExclusion', {id: id}, function(res) {
|
1962 |
window.location.reload(true);
|
lib/GeoIP.dat
CHANGED
Binary file
|
lib/dashboard.php
CHANGED
@@ -32,6 +32,7 @@
|
|
32 |
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
33 |
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
34 |
<?php if(wfConfig::get('scansEnabled_fileContents')){ ?><tr><td style="padding-right: 20px;">Scan Other Files:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
|
|
35 |
<?php if(wfConfig::get('scansEnabled_posts')){ ?><tr><td style="padding-right: 20px;">Scan Posts:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
36 |
<?php if(wfConfig::get('scansEnabled_comments')){ ?><tr><td style="padding-right: 20px;">Scan Comments:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
37 |
<?php if(wfConfig::get('scansEnabled_oldVersions')){ ?><tr><td style="padding-right: 20px;">Scan for Old Software:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
32 |
<?php if(wfConfig::get('scansEnabled_themes')){ ?><tr><td style="padding-right: 20px;">Scan Themes:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
33 |
<?php if(wfConfig::get('scansEnabled_plugins')){ ?><tr><td style="padding-right: 20px;">Scan Plugins:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
34 |
<?php if(wfConfig::get('scansEnabled_fileContents')){ ?><tr><td style="padding-right: 20px;">Scan Other Files:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
35 |
+
<?php if(wfConfig::get('scansEnabled_database')){ ?><tr><td style="padding-right: 20px;">Scan Database:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
36 |
<?php if(wfConfig::get('scansEnabled_posts')){ ?><tr><td style="padding-right: 20px;">Scan Posts:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
37 |
<?php if(wfConfig::get('scansEnabled_comments')){ ?><tr><td style="padding-right: 20px;">Scan Comments:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
38 |
<?php if(wfConfig::get('scansEnabled_oldVersions')){ ?><tr><td style="padding-right: 20px;">Scan for Old Software:</td><td style="color: #0F0;">✔</td></tr> <?php } ?>
|
lib/menu_options.php
CHANGED
@@ -462,6 +462,14 @@ $w = new wfConfig();
|
|
462 |
name="scansEnabled_fileContents"
|
463 |
value="1" <?php $w->cb( 'scansEnabled_fileContents' ); ?>/></td>
|
464 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
465 |
<tr>
|
466 |
<th>Scan posts for known dangerous URLs and suspicious content<a
|
467 |
href="http://docs.wordfence.com/en/Wordfence_options#Scan_posts_for_known_dangerous_URLs_and_suspicious_content"
|
462 |
name="scansEnabled_fileContents"
|
463 |
value="1" <?php $w->cb( 'scansEnabled_fileContents' ); ?>/></td>
|
464 |
</tr>
|
465 |
+
<tr>
|
466 |
+
<th>Scan database for backdoors, trojans and suspicious code<a
|
467 |
+
href="http://docs.wordfence.com/en/Wordfence_options#Scan_database_for_backdoors.2C_trojans_and_suspicious_code"
|
468 |
+
target="_blank" class="wfhelp"></a></th>
|
469 |
+
<td><input type="checkbox" id="scansEnabled_database" class="wfConfigElem"
|
470 |
+
name="scansEnabled_database"
|
471 |
+
value="1" <?php $w->cb( 'scansEnabled_database' ); ?>/></td>
|
472 |
+
</tr>
|
473 |
<tr>
|
474 |
<th>Scan posts for known dangerous URLs and suspicious content<a
|
475 |
href="http://docs.wordfence.com/en/Wordfence_options#Scan_posts_for_known_dangerous_URLs_and_suspicious_content"
|
lib/menu_rangeBlocking.php
CHANGED
@@ -7,7 +7,7 @@
|
|
7 |
<?php if(! wfConfig::get('firewallEnabled')){ ?><div style="color: #F00; font-weight: bold;">Firewall is disabled. You can enable it on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options page</a> at the top.</div><br /><?php } ?>
|
8 |
<table class="wfConfigForm">
|
9 |
<tr><th>IP address range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php
|
10 |
-
if( isset( $_GET['wfBlockRange'] ) && preg_match('/^[\
|
11 |
?>" onkeyup="WFAD.calcRangeTotal();"> <span id="wfShowRangeTotal"></span></td></tr>
|
12 |
<tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> 192.168.200.200 - 192.168.200.220</td></tr>
|
13 |
<tr><th>User-Agent (browser) that matches:</th><td><input id="uaRange" type="text" size="30" maxlength="255" > (Case insensitive)</td></tr>
|
@@ -29,8 +29,8 @@
|
|
29 |
</div>
|
30 |
<script type="text/x-jquery-template" id="wfBlockedRangesTmpl">
|
31 |
<div>
|
32 |
-
<div style="
|
33 |
-
<table border="0" style="width: 100
|
34 |
{{each(idx, elem) results}}
|
35 |
<tr><td>
|
36 |
{{if patternDisabled}}
|
7 |
<?php if(! wfConfig::get('firewallEnabled')){ ?><div style="color: #F00; font-weight: bold;">Firewall is disabled. You can enable it on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options page</a> at the top.</div><br /><?php } ?>
|
8 |
<table class="wfConfigForm">
|
9 |
<tr><th>IP address range:</th><td><input id="ipRange" type="text" size="30" maxlength="255" value="<?php
|
10 |
+
if( isset( $_GET['wfBlockRange'] ) && preg_match('/^[\da-f\.\s\t\-:]+#x2F;i', $_GET['wfBlockRange']) ){ echo wp_kses($_GET['wfBlockRange'], array()); }
|
11 |
?>" onkeyup="WFAD.calcRangeTotal();"> <span id="wfShowRangeTotal"></span></td></tr>
|
12 |
<tr><td></td><td style="padding-bottom: 15px;"><strong>Examples:</strong> 192.168.200.200 - 192.168.200.220</td></tr>
|
13 |
<tr><th>User-Agent (browser) that matches:</th><td><input id="uaRange" type="text" size="30" maxlength="255" > (Case insensitive)</td></tr>
|
29 |
</div>
|
30 |
<script type="text/x-jquery-template" id="wfBlockedRangesTmpl">
|
31 |
<div>
|
32 |
+
<div style="padding-bottom: 10px; margin-bottom: 10px;">
|
33 |
+
<table border="0" style="width: 100%" class="block-ranges-table">
|
34 |
{{each(idx, elem) results}}
|
35 |
<tr><td>
|
36 |
{{if patternDisabled}}
|
lib/menu_scan.php
CHANGED
@@ -556,6 +556,57 @@
|
|
556 |
</div>
|
557 |
</div>
|
558 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
559 |
<script type="text/x-jquery-template" id="issueTmpl_pubBadURLs">
|
560 |
<div>
|
561 |
<div class="wfIssue">
|
556 |
</div>
|
557 |
</div>
|
558 |
</script>
|
559 |
+
<script type="text/x-jquery-template" id="issueTmpl_database">
|
560 |
+
<div>
|
561 |
+
<div class="wfIssue">
|
562 |
+
<h2>${shortMsg}</h2>
|
563 |
+
<p>
|
564 |
+
<table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
|
565 |
+
<tr><th>Option Name:</th><td>${data.option_name}</td></tr>
|
566 |
+
{{if ((typeof data.badURL !== 'undefined') && data.badURL)}}
|
567 |
+
<tr><th>Bad URL:</th><td><strong class="wfWarn">${data.badURL}</strong></td></tr>
|
568 |
+
{{/if}}
|
569 |
+
<tr><th>Issue first detected:</th><td>${timeAgo} ago.</td></tr>
|
570 |
+
<tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
|
571 |
+
<tr><th>Status</th><td>
|
572 |
+
{{if status == 'new' }}New{{/if}}
|
573 |
+
{{if status == 'ignoreP' }}Permanently ignoring this option{{/if}}
|
574 |
+
{{if status == 'ignoreC' }}Ignoring this option until it changes{{/if}}
|
575 |
+
</td></tr>
|
576 |
+
</table>
|
577 |
+
</p>
|
578 |
+
<p>
|
579 |
+
{{html longMsg}}
|
580 |
+
</p>
|
581 |
+
<div class="wfIssueOptions">
|
582 |
+
<strong>Tools:</strong>
|
583 |
+
{{if data.optionExists}}
|
584 |
+
<a target="_blank" href="${WFAD.makeViewOptionLink(data.option_name, data.site_id)}">View this option.</a>
|
585 |
+
{{/if}}
|
586 |
+
{{if data.canDelete}}
|
587 |
+
<a href="#" onclick="WFAD.deleteDatabaseOption('${id}'); return false;">Delete this option from the database (can't be undone).</a>
|
588 |
+
<br /> <input type="checkbox" class="wfdelCheckbox" value="${id}" /> Select for bulk delete
|
589 |
+
{{/if}}
|
590 |
+
</div>
|
591 |
+
<div class="wfIssueOptions">
|
592 |
+
{{if status == 'new'}}
|
593 |
+
<strong>Resolve:</strong>
|
594 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
|
595 |
+
{{if data.optionExists}}
|
596 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore until the option changes.</a>
|
597 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreP'); return false;">Always ignore this option.</a>
|
598 |
+
{{else}}
|
599 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreC'); return false;">Ignore missing option.</a>
|
600 |
+
{{/if}}
|
601 |
+
|
602 |
+
{{/if}}
|
603 |
+
{{if status == 'ignoreC' || status == 'ignoreP'}}
|
604 |
+
<a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue.</a>
|
605 |
+
{{/if}}
|
606 |
+
</div>
|
607 |
+
</div>
|
608 |
+
</div>
|
609 |
+
</script>
|
610 |
<script type="text/x-jquery-template" id="issueTmpl_pubBadURLs">
|
611 |
<div>
|
612 |
<div class="wfIssue">
|
lib/wfActivityReport.php
CHANGED
@@ -339,13 +339,15 @@ SQL
|
|
339 |
|
340 |
/**
|
341 |
* @param mixed $ip_address
|
342 |
-
* @param null
|
343 |
*/
|
344 |
public static function logBlockedIP($ip_address, $unixday = null) {
|
|
|
345 |
global $wpdb;
|
346 |
|
347 |
-
|
348 |
-
|
|
|
349 |
}
|
350 |
|
351 |
$blocked_table = "{$wpdb->base_prefix}wfBlockedIPLog";
|
@@ -355,7 +357,7 @@ SQL
|
|
355 |
$unixday_insert = absint($unixday);
|
356 |
}
|
357 |
|
358 |
-
$country = wfUtils::IP2Country
|
359 |
|
360 |
$wpdb->query($wpdb->prepare(<<<SQL
|
361 |
INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday)
|
339 |
|
340 |
/**
|
341 |
* @param mixed $ip_address
|
342 |
+
* @param int|null $unixday
|
343 |
*/
|
344 |
public static function logBlockedIP($ip_address, $unixday = null) {
|
345 |
+
/** @var wpdb $wpdb */
|
346 |
global $wpdb;
|
347 |
|
348 |
+
$is_bin_ip = !wfUtils::isValidIP($ip_address);
|
349 |
+
if (!$is_bin_ip) {
|
350 |
+
$ip_address = wfUtils::inet_pton($ip_address);
|
351 |
}
|
352 |
|
353 |
$blocked_table = "{$wpdb->base_prefix}wfBlockedIPLog";
|
357 |
$unixday_insert = absint($unixday);
|
358 |
}
|
359 |
|
360 |
+
$country = wfUtils::IP2Country($is_bin_ip ? wfUtils::inet_ntop($ip_address) : $ip_address);
|
361 |
|
362 |
$wpdb->query($wpdb->prepare(<<<SQL
|
363 |
INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday)
|
lib/wfCache.php
CHANGED
@@ -578,6 +578,11 @@ EOT;
|
|
578 |
self::updateBlockedIPs('add'); //Fail silently if .htaccess is not readable. Will fall back to old blocking via WP
|
579 |
wp_schedule_single_event(time() + 300, 'wordfence_update_blocked_IPs');
|
580 |
}
|
|
|
|
|
|
|
|
|
|
|
581 |
public static function updateBlockedIPs($action){ //'add' or 'remove'
|
582 |
if(wfConfig::get('cacheType') != 'falcon'){ return; }
|
583 |
|
@@ -635,9 +640,19 @@ EOT;
|
|
635 |
|
636 |
if($range){
|
637 |
if($browser || $referer){ continue; } //We don't allow combos in falcon
|
638 |
-
|
639 |
-
$
|
640 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
641 |
if(sizeof($cidrs) > 0){
|
642 |
$lines[] = '#Start of blocking code for IP range: ' . $hIPs . "\n";
|
643 |
foreach($cidrs as $c){
|
578 |
self::updateBlockedIPs('add'); //Fail silently if .htaccess is not readable. Will fall back to old blocking via WP
|
579 |
wp_schedule_single_event(time() + 300, 'wordfence_update_blocked_IPs');
|
580 |
}
|
581 |
+
|
582 |
+
/**
|
583 |
+
* @param $action
|
584 |
+
* @return bool|string|void
|
585 |
+
*/
|
586 |
public static function updateBlockedIPs($action){ //'add' or 'remove'
|
587 |
if(wfConfig::get('cacheType') != 'falcon'){ return; }
|
588 |
|
640 |
|
641 |
if($range){
|
642 |
if($browser || $referer){ continue; } //We don't allow combos in falcon
|
643 |
+
|
644 |
+
list($start_range, $end_range) = explode('-', $range);
|
645 |
+
if (preg_match('/[\.:]/', $start_range)) {
|
646 |
+
$start_range = wfUtils::inet_pton($start_range);
|
647 |
+
$end_range = wfUtils::inet_pton($end_range);
|
648 |
+
} else {
|
649 |
+
$start_range = wfUtils::inet_pton(long2ip($start_range));
|
650 |
+
$end_range = wfUtils::inet_pton(long2ip($end_range));
|
651 |
+
}
|
652 |
+
|
653 |
+
$cidrs = wfUtils::rangeToCIDRs($start_range, $end_range);
|
654 |
+
|
655 |
+
$hIPs = wfUtils::inet_ntop($start_range) . ' - ' . wfUtils::inet_ntop($end_range);
|
656 |
if(sizeof($cidrs) > 0){
|
657 |
$lines[] = '#Start of blocking code for IP range: ' . $hIPs . "\n";
|
658 |
foreach($cidrs as $c){
|
lib/wfConfig.php
CHANGED
@@ -34,6 +34,7 @@ class wfConfig {
|
|
34 |
"scansEnabled_plugins" => false,
|
35 |
"scansEnabled_malware" => false,
|
36 |
"scansEnabled_fileContents" => false,
|
|
|
37 |
"scansEnabled_posts" => false,
|
38 |
"scansEnabled_comments" => false,
|
39 |
"scansEnabled_passwds" => false,
|
@@ -121,6 +122,7 @@ class wfConfig {
|
|
121 |
"scansEnabled_plugins" => false,
|
122 |
"scansEnabled_malware" => true,
|
123 |
"scansEnabled_fileContents" => true,
|
|
|
124 |
"scansEnabled_posts" => true,
|
125 |
"scansEnabled_comments" => true,
|
126 |
"scansEnabled_passwds" => true,
|
@@ -208,6 +210,7 @@ class wfConfig {
|
|
208 |
"scansEnabled_plugins" => false,
|
209 |
"scansEnabled_malware" => true,
|
210 |
"scansEnabled_fileContents" => true,
|
|
|
211 |
"scansEnabled_posts" => true,
|
212 |
"scansEnabled_comments" => true,
|
213 |
"scansEnabled_passwds" => true,
|
@@ -295,6 +298,7 @@ class wfConfig {
|
|
295 |
"scansEnabled_plugins" => false,
|
296 |
"scansEnabled_malware" => true,
|
297 |
"scansEnabled_fileContents" => true,
|
|
|
298 |
"scansEnabled_posts" => true,
|
299 |
"scansEnabled_comments" => true,
|
300 |
"scansEnabled_passwds" => true,
|
@@ -382,6 +386,7 @@ class wfConfig {
|
|
382 |
"scansEnabled_plugins" => false,
|
383 |
"scansEnabled_malware" => true,
|
384 |
"scansEnabled_fileContents" => true,
|
|
|
385 |
"scansEnabled_posts" => true,
|
386 |
"scansEnabled_comments" => true,
|
387 |
"scansEnabled_passwds" => true,
|
34 |
"scansEnabled_plugins" => false,
|
35 |
"scansEnabled_malware" => false,
|
36 |
"scansEnabled_fileContents" => false,
|
37 |
+
"scansEnabled_database" => false,
|
38 |
"scansEnabled_posts" => false,
|
39 |
"scansEnabled_comments" => false,
|
40 |
"scansEnabled_passwds" => false,
|
122 |
"scansEnabled_plugins" => false,
|
123 |
"scansEnabled_malware" => true,
|
124 |
"scansEnabled_fileContents" => true,
|
125 |
+
"scansEnabled_database" => true,
|
126 |
"scansEnabled_posts" => true,
|
127 |
"scansEnabled_comments" => true,
|
128 |
"scansEnabled_passwds" => true,
|
210 |
"scansEnabled_plugins" => false,
|
211 |
"scansEnabled_malware" => true,
|
212 |
"scansEnabled_fileContents" => true,
|
213 |
+
"scansEnabled_database" => true,
|
214 |
"scansEnabled_posts" => true,
|
215 |
"scansEnabled_comments" => true,
|
216 |
"scansEnabled_passwds" => true,
|
298 |
"scansEnabled_plugins" => false,
|
299 |
"scansEnabled_malware" => true,
|
300 |
"scansEnabled_fileContents" => true,
|
301 |
+
"scansEnabled_database" => true,
|
302 |
"scansEnabled_posts" => true,
|
303 |
"scansEnabled_comments" => true,
|
304 |
"scansEnabled_passwds" => true,
|
386 |
"scansEnabled_plugins" => false,
|
387 |
"scansEnabled_malware" => true,
|
388 |
"scansEnabled_fileContents" => true,
|
389 |
+
"scansEnabled_database" => true,
|
390 |
"scansEnabled_posts" => true,
|
391 |
"scansEnabled_comments" => true,
|
392 |
"scansEnabled_passwds" => true,
|
lib/wfCrawl.php
CHANGED
@@ -12,7 +12,7 @@ class wfCrawl {
|
|
12 |
public static function verifyCrawlerPTR($hostPattern, $IP){
|
13 |
global $wpdb; $table = $wpdb->base_prefix . 'wfCrawlers';
|
14 |
$db = new wfDB();
|
15 |
-
$IPn = wfUtils::
|
16 |
$status = $db->querySingle("select status from $table where IP=%s and patternSig=UNHEX(MD5('%s')) and lastUpdate > unix_timestamp() - %d", $IPn, $hostPattern, WORDFENCE_CRAWLER_VERIFY_CACHE_TIME);
|
17 |
if($status){
|
18 |
if($status == 'verified'){
|
@@ -27,7 +27,7 @@ class wfCrawl {
|
|
27 |
return false;
|
28 |
}
|
29 |
if(preg_match($hostPattern, $host)){
|
30 |
-
$resultIPs =
|
31 |
$addrsMatch = false;
|
32 |
foreach($resultIPs as $resultIP){
|
33 |
if($resultIP == $IP){
|
@@ -89,5 +89,14 @@ class wfCrawl {
|
|
89 |
'@^Google$@'
|
90 |
);
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
93 |
?>
|
12 |
public static function verifyCrawlerPTR($hostPattern, $IP){
|
13 |
global $wpdb; $table = $wpdb->base_prefix . 'wfCrawlers';
|
14 |
$db = new wfDB();
|
15 |
+
$IPn = wfUtils::inet_pton($IP);
|
16 |
$status = $db->querySingle("select status from $table where IP=%s and patternSig=UNHEX(MD5('%s')) and lastUpdate > unix_timestamp() - %d", $IPn, $hostPattern, WORDFENCE_CRAWLER_VERIFY_CACHE_TIME);
|
17 |
if($status){
|
18 |
if($status == 'verified'){
|
27 |
return false;
|
28 |
}
|
29 |
if(preg_match($hostPattern, $host)){
|
30 |
+
$resultIPs = wfUtils::resolveDomainName($host);
|
31 |
$addrsMatch = false;
|
32 |
foreach($resultIPs as $resultIP){
|
33 |
if($resultIP == $IP){
|
89 |
'@^Google$@'
|
90 |
);
|
91 |
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Has correct user agent and PTR record points to .googlebot.com domain.
|
95 |
+
*
|
96 |
+
* @return bool
|
97 |
+
*/
|
98 |
+
public static function isVerifiedGoogleCrawler() {
|
99 |
+
return self::isGoogleCrawler() && self::verifyCrawlerPTR(wordfence::getLog()->getGooglePattern(), wfUtils::getIP());
|
100 |
+
}
|
101 |
}
|
102 |
?>
|
lib/wfHelperBin.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class wfHelperBin {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* @param string $bin1
|
7 |
+
* @param string $bin2
|
8 |
+
* @return mixed
|
9 |
+
*/
|
10 |
+
public static function addbin2bin($bin1, $bin2) {
|
11 |
+
if (strlen($bin1) % 4 != 0) {
|
12 |
+
$bin1 = str_repeat("\0", 4 - (strlen($bin1) % 4)) . $bin1;
|
13 |
+
}
|
14 |
+
if (strlen($bin2) % 4 != 0) {
|
15 |
+
$bin2 = str_repeat("\0", 4 - (strlen($bin2) % 4)) . $bin2;
|
16 |
+
}
|
17 |
+
|
18 |
+
$bin1_ints = array_reverse(array_values(unpack('N*', $bin1)));
|
19 |
+
$bin2_ints = array_reverse(array_values(unpack('N*', $bin2)));
|
20 |
+
$return = array();
|
21 |
+
$carries = 0;
|
22 |
+
for ($i=0; $i < max(count($bin1_ints), count($bin2_ints)); $i++) {
|
23 |
+
$int1 = array_key_exists($i, $bin1_ints) ? $bin1_ints[$i] : 0;
|
24 |
+
$int2 = array_key_exists($i, $bin2_ints) ? $bin2_ints[$i] : 0;
|
25 |
+
$val = $int1 + $int2 + $carries;
|
26 |
+
if ($carries > 0) {
|
27 |
+
$carries = 0;
|
28 |
+
}
|
29 |
+
if ($val >= 0x100000000) {
|
30 |
+
$val -= 0x100000000;
|
31 |
+
$carries++;
|
32 |
+
}
|
33 |
+
$return[] = $val;
|
34 |
+
}
|
35 |
+
if ($carries) {
|
36 |
+
$return[] += $carries;
|
37 |
+
}
|
38 |
+
$return = array_reverse($return);
|
39 |
+
array_unshift($return, 'N*');
|
40 |
+
$return = call_user_func_array('pack', $return);
|
41 |
+
$return = ltrim($return, "\x00");
|
42 |
+
return strlen($return) == 0 ? "\x00" : $return;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Convert binary string to the 10101's representation.
|
47 |
+
*
|
48 |
+
* @param string $string
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public static function bin2str($string) {
|
52 |
+
$return = '';
|
53 |
+
for ($i = 0; $i < strlen($string); $i++) {
|
54 |
+
$return .= str_pad(decbin(ord($string[$i])), 8, '0', STR_PAD_LEFT);
|
55 |
+
}
|
56 |
+
$return = ltrim($return, '0');
|
57 |
+
return strlen($return) == 0 ? '0' : $return;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Convert 10101's representation back to the binary data.
|
62 |
+
*
|
63 |
+
* @param string $string
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public static function str2bin($string) {
|
67 |
+
if (strlen($string) % 32 > 0) {
|
68 |
+
$string = str_repeat('0', 32 - (strlen($string) % 32)) . $string;
|
69 |
+
}
|
70 |
+
$ints = str_split($string, 32);
|
71 |
+
$return = '';
|
72 |
+
foreach ($ints as $int) {
|
73 |
+
$return .= pack('N', bindec($int));
|
74 |
+
}
|
75 |
+
$return = ltrim($return, "\0");
|
76 |
+
return strlen($return) == 0 ? "\0" : $return;
|
77 |
+
}
|
78 |
+
}
|
lib/wfIssues.php
CHANGED
@@ -26,14 +26,14 @@ class wfIssues {
|
|
26 |
$ignoreC, /* some piece of data used for md5 for ignoring until something changes */
|
27 |
$shortMsg, $longMsg, $templateData
|
28 |
){
|
29 |
-
|
30 |
|
31 |
$ignoreP = md5($ignoreP);
|
32 |
$ignoreC = md5($ignoreC);
|
33 |
$rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from " . $this->issuesTable . " where (ignoreP='%s' OR ignoreC='%s')", $ignoreP, $ignoreC);
|
34 |
if($rec){
|
35 |
if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){
|
36 |
-
if($type != 'file'){ //Filter out duplicate new issues but not infected files because we want to see all infections even if file contents are identical
|
37 |
return false;
|
38 |
}
|
39 |
}
|
@@ -172,7 +172,9 @@ class wfIssues {
|
|
172 |
$rec['data'] = unserialize($rec['data']);
|
173 |
return $rec;
|
174 |
}
|
175 |
-
public function getIssues(){
|
|
|
|
|
176 |
$ret = array(
|
177 |
'new' => array(),
|
178 |
'ignored' => array()
|
@@ -200,6 +202,10 @@ class wfIssues {
|
|
200 |
$issueList[$i]['data']['fileExists'] = '';
|
201 |
}
|
202 |
}
|
|
|
|
|
|
|
|
|
203 |
$issueList[$i]['issueIDX'] = $i;
|
204 |
}
|
205 |
}
|
26 |
$ignoreC, /* some piece of data used for md5 for ignoring until something changes */
|
27 |
$shortMsg, $longMsg, $templateData
|
28 |
){
|
29 |
+
|
30 |
|
31 |
$ignoreP = md5($ignoreP);
|
32 |
$ignoreC = md5($ignoreC);
|
33 |
$rec = $this->getDB()->querySingleRec("select status, ignoreP, ignoreC from " . $this->issuesTable . " where (ignoreP='%s' OR ignoreC='%s')", $ignoreP, $ignoreC);
|
34 |
if($rec){
|
35 |
if($rec['status'] == 'new' && ($rec['ignoreC'] == $ignoreC || $rec['ignoreP'] == $ignoreP)){
|
36 |
+
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
|
37 |
return false;
|
38 |
}
|
39 |
}
|
172 |
$rec['data'] = unserialize($rec['data']);
|
173 |
return $rec;
|
174 |
}
|
175 |
+
public function getIssues(){
|
176 |
+
/** @var wpdb $wpdb */
|
177 |
+
global $wpdb;
|
178 |
$ret = array(
|
179 |
'new' => array(),
|
180 |
'ignored' => array()
|
202 |
$issueList[$i]['data']['fileExists'] = '';
|
203 |
}
|
204 |
}
|
205 |
+
if ($issueList[$i]['type'] == 'database') {
|
206 |
+
$prefix = $wpdb->get_blog_prefix($issueList[$i]['data']['site_id']);
|
207 |
+
$issueList[$i]['data']['optionExists'] = $wpdb->get_var($wpdb->prepare("SELECT count(*) FROM {$prefix}options WHERE option_name = %s", $issueList[$i]['data']['option_name'])) > 0;
|
208 |
+
}
|
209 |
$issueList[$i]['issueIDX'] = $i;
|
210 |
}
|
211 |
}
|
lib/wfLog.php
CHANGED
@@ -26,7 +26,7 @@ class wfLog {
|
|
26 |
$this->perfTable = $wpdb->base_prefix . 'wfPerfLog';
|
27 |
}
|
28 |
public function logPerf($IP, $UA, $URL, $data){
|
29 |
-
$IP = wfUtils::
|
30 |
$this->getDB()->queryWrite("insert into " . $this->perfTable . " (IP, userID, UA, URL, ctime, fetchStart, domainLookupStart, domainLookupEnd, connectStart, connectEnd, requestStart, responseStart, responseEnd, domReady, loaded) values (%s, %d, '%s', '%s', unix_timestamp(), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
|
31 |
$IP,
|
32 |
$this->getCurrentUserID(),
|
@@ -67,7 +67,7 @@ class wfLog {
|
|
67 |
$action,
|
68 |
$username,
|
69 |
$userID,
|
70 |
-
wfUtils::
|
71 |
(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '')
|
72 |
);
|
73 |
}
|
@@ -79,10 +79,17 @@ class wfLog {
|
|
79 |
if(wfConfig::get('firewallEnabled')){
|
80 |
//Moved the following block into the "is fw enabled section" for optimization.
|
81 |
$IP = wfUtils::getIP();
|
82 |
-
$IPnum = wfUtils::
|
83 |
if($this->isWhitelisted($IP)){
|
84 |
return;
|
85 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
if($type == '404'){
|
87 |
$table = $this->scanTable;
|
88 |
} else if($type == 'hit'){
|
@@ -91,7 +98,7 @@ class wfLog {
|
|
91 |
wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): $type");
|
92 |
return;
|
93 |
}
|
94 |
-
$this->getDB()->queryWrite("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::
|
95 |
$hitsPerMinute = $this->getDB()->querySingle("select @wfcurrenthits");
|
96 |
//end block moved into "is fw enabled" section
|
97 |
|
@@ -126,7 +133,7 @@ class wfLog {
|
|
126 |
if($pat){
|
127 |
$URL = wfUtils::getRequestedURL();
|
128 |
if(preg_match($pat, $URL)){
|
129 |
-
$this->getDB()->queryWrite("insert IGNORE into $p"."wfVulnScanners (IP, ctime, hits) values
|
130 |
if(wfConfig::get('maxScanHits') != 'DISABLED'){
|
131 |
if( empty($_SERVER['HTTP_REFERER'] )){
|
132 |
$this->getDB()->queryWrite("insert into " . $this->badLeechersTable . " (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfblcurrenthits := hits + 1, hits + 1, hits + 1)", $IPnum);
|
@@ -159,42 +166,36 @@ class wfLog {
|
|
159 |
}
|
160 |
}
|
161 |
}
|
162 |
-
|
163 |
-
|
164 |
-
|
|
|
|
|
|
|
|
|
|
|
165 |
return true;
|
166 |
}
|
167 |
//We now whitelist all private addrs
|
168 |
-
if(wfUtils::isPrivateAddress($IP
|
169 |
return true;
|
170 |
}
|
171 |
//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.
|
172 |
-
$externalWhite = array('97.74.127.171
|
173 |
-
if(in_array($IP, $externalWhite
|
174 |
return true;
|
175 |
}
|
176 |
$list = wfConfig::get('whitelisted');
|
177 |
-
if
|
|
|
|
|
178 |
$list = explode(',', $list);
|
179 |
-
if(sizeof($list) < 1
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
for($i = 0; $i <= 3; $i++){
|
186 |
-
if(preg_match('/^\[(\d+)\-(\d+)\]#x2F;', $whiteParts[$i], $m)){
|
187 |
-
if($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]){
|
188 |
-
$mismatch = true;
|
189 |
-
}
|
190 |
-
} else if($whiteParts[$i] != $IPparts[$i]){
|
191 |
-
$mismatch = true;
|
192 |
-
}
|
193 |
-
}
|
194 |
-
if($mismatch === false){
|
195 |
-
return true; //Is whitelisted because we did not get a mismatch
|
196 |
-
}
|
197 |
-
} else if($whiteIP == $IP){
|
198 |
return true;
|
199 |
}
|
200 |
}
|
@@ -221,7 +222,7 @@ class wfLog {
|
|
221 |
}
|
222 |
|
223 |
// 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.
|
224 |
-
$white_listed_ips = array_merge($white_listed_ips, array_map(array('wfUtils', '
|
225 |
|
226 |
if ($user_whitelisted === null) {
|
227 |
$user_whitelisted = wfConfig::get('whitelisted');
|
@@ -245,13 +246,21 @@ class wfLog {
|
|
245 |
$this->getDB()->queryWrite("delete from " . $this->lockOutTable);
|
246 |
}
|
247 |
public function unblockIP($IP){
|
248 |
-
$this->getDB()->queryWrite("delete from " . $this->blocksTable . " where IP=%s", wfUtils::
|
249 |
wfCache::updateBlockedIPs('add');
|
250 |
}
|
251 |
public function unblockRange($id){
|
252 |
$this->getDB()->queryWrite("delete from " . $this->ipRangesTable . " where id=%d", $id);
|
253 |
wfCache::updateBlockedIPs('add');
|
254 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
public function blockRange($blockType, $range, $reason){
|
256 |
$this->getDB()->queryWrite("insert IGNORE into " . $this->ipRangesTable . " (blockType, blockString, ctime, reason, totalBlocked, lastBlocked) values ('%s', '%s', unix_timestamp(), '%s', 0, 0)", $blockType, $range, $reason);
|
257 |
wfCache::updateBlockedIPs('add');
|
@@ -284,8 +293,12 @@ class wfLog {
|
|
284 |
$numBlockElements = 0;
|
285 |
if($blockDat[0]){
|
286 |
$numBlockElements++;
|
287 |
-
$
|
288 |
-
|
|
|
|
|
|
|
|
|
289 |
} else {
|
290 |
$elem['ipPattern'] = 'Allow all IP addresses';
|
291 |
}
|
@@ -316,7 +329,7 @@ class wfLog {
|
|
316 |
if($permanent){
|
317 |
//Insert permanent=1 or update existing perm or non-per block to be permanent
|
318 |
$this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d, permanent=%d",
|
319 |
-
wfUtils::
|
320 |
$timeBlockOccurred,
|
321 |
$reason,
|
322 |
$wfsn,
|
@@ -329,7 +342,7 @@ class wfLog {
|
|
329 |
} else {
|
330 |
//insert perm=0 but don't update and make perm blocks non-perm.
|
331 |
$this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d",
|
332 |
-
wfUtils::
|
333 |
$timeBlockOccurred,
|
334 |
$reason,
|
335 |
$wfsn,
|
@@ -349,7 +362,7 @@ class wfLog {
|
|
349 |
public function lockOutIP($IP, $reason){
|
350 |
if($this->isWhitelisted($IP)){ return false; }
|
351 |
$this->getDB()->queryWrite("insert into " . $this->lockOutTable . " (IP, blockedTime, reason) values (%s, unix_timestamp(), '%s') ON DUPLICATE KEY update blockedTime=unix_timestamp(), reason='%s'",
|
352 |
-
wfUtils::
|
353 |
$reason,
|
354 |
$reason
|
355 |
);
|
@@ -360,11 +373,11 @@ class wfLog {
|
|
360 |
return true;
|
361 |
}
|
362 |
public function unlockOutIP($IP){
|
363 |
-
$this->getDB()->queryWrite("delete from " . $this->lockOutTable . " where IP=%s", wfUtils::
|
364 |
}
|
365 |
public function isIPLockedOut($IP){
|
366 |
-
if($this->getDB()->querySingle("select IP from " . $this->lockOutTable . " where IP=%s and blockedTime + %s > unix_timestamp()", wfUtils::
|
367 |
-
$this->getDB()->queryWrite("update " . $this->lockOutTable . " set blockedHits = blockedHits + 1, lastAttempt = unix_timestamp() where IP=%s", wfUtils::
|
368 |
return true;
|
369 |
} else {
|
370 |
return false;
|
@@ -378,7 +391,7 @@ class wfLog {
|
|
378 |
}
|
379 |
$this->resolveIPs($results);
|
380 |
foreach($results as &$elem){
|
381 |
-
$elem['IP'] = wfUtils::
|
382 |
}
|
383 |
return $results;
|
384 |
}
|
@@ -391,15 +404,15 @@ class wfLog {
|
|
391 |
}
|
392 |
$this->resolveIPs($results);
|
393 |
foreach($results as &$elem){
|
394 |
-
$elem['IP'] = wfUtils::
|
395 |
}
|
396 |
return $results;
|
397 |
}
|
398 |
public function getBlockedIPsAddrOnly(){
|
399 |
-
$results = $this->getDB()->querySelect("select
|
400 |
$ret = array();
|
401 |
foreach($results as $elem){
|
402 |
-
$ret[] =
|
403 |
}
|
404 |
return $ret;
|
405 |
}
|
@@ -431,7 +444,7 @@ class wfLog {
|
|
431 |
$this->resolveIPs($results);
|
432 |
foreach($results as &$elem){
|
433 |
$elem['blocked'] = 1;
|
434 |
-
$elem['IP'] = wfUtils::
|
435 |
}
|
436 |
return $results;
|
437 |
}
|
@@ -450,7 +463,7 @@ class wfLog {
|
|
450 |
$elem['timeAgo'] = wfUtils::makeTimeAgo($this->getDB()->querySingle("select unix_timestamp() - (eMin * 60) from $table where IP=%s", $elem['IP']));
|
451 |
$elem['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and ((blockedTime + %s > unix_timestamp()) OR permanent = 1)", $elem['IP'], wfConfig::get('blockedTime'));
|
452 |
//take action
|
453 |
-
$elem['IP'] = wfUtils::
|
454 |
}
|
455 |
return $results;
|
456 |
}
|
@@ -466,7 +479,7 @@ class wfLog {
|
|
466 |
sprintf('%.6f', microtime(true)),
|
467 |
(is_404() ? 1 : 0),
|
468 |
(wfCrawl::isGoogleCrawler() ? 1 : 0),
|
469 |
-
wfUtils::
|
470 |
$this->getCurrentUserID(),
|
471 |
(wordfence::$newVisit ? 1 : 0),
|
472 |
wfUtils::getRequestedURL(),
|
@@ -482,7 +495,7 @@ class wfLog {
|
|
482 |
$browscap = new wfBrowscap();
|
483 |
foreach($results as &$res){
|
484 |
$res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']);
|
485 |
-
$res['IP'] = wfUtils::
|
486 |
$res['browser'] = false;
|
487 |
if($res['UA']){
|
488 |
$b = $browscap->getBrowser($res['UA']);
|
@@ -516,8 +529,8 @@ class wfLog {
|
|
516 |
$serverTime = $this->getDB()->querySingle("select unix_timestamp()");
|
517 |
$IPSQL = "";
|
518 |
if($IP){
|
519 |
-
$IPSQL = " and IP
|
520 |
-
$sqlArgs = array($afterTime,
|
521 |
} else {
|
522 |
$sqlArgs = array($afterTime, $limit);
|
523 |
}
|
@@ -559,7 +572,7 @@ class wfLog {
|
|
559 |
$res['type'] = $type;
|
560 |
$res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']);
|
561 |
$res['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and (permanent = 1 OR (blockedTime + %s > unix_timestamp()))", $res['IP'], wfConfig::get('blockedTime'));
|
562 |
-
$res['IP'] = wfUtils::
|
563 |
$res['extReferer'] = false;
|
564 |
if(isset( $res['referer'] ) && $res['referer']){
|
565 |
if(wfUtils::hasXSS($res['referer'] )){ //filtering out XSS
|
@@ -642,8 +655,9 @@ class wfLog {
|
|
642 |
$IPLocs = wfUtils::getIPsGeo($IPs); //Creates an array with IP as key and data as value
|
643 |
|
644 |
foreach($results as &$res){
|
645 |
-
|
646 |
-
|
|
|
647 |
} else {
|
648 |
$res['loc'] = false;
|
649 |
}
|
@@ -696,7 +710,7 @@ class wfLog {
|
|
696 |
if($this->isWhitelisted($IP)){
|
697 |
return;
|
698 |
}
|
699 |
-
$IPnum = wfUtils::
|
700 |
|
701 |
//New range and UA pattern blocking:
|
702 |
$r1 = $this->getDB()->querySelect("select id, blockType, blockString from " . $this->ipRangesTable);
|
@@ -711,8 +725,16 @@ class wfLog {
|
|
711 |
$uaPattern = $bDat[1];
|
712 |
$refPattern = isset($bDat[2]) ? $bDat[2] : '';
|
713 |
if($ipRange){
|
714 |
-
$
|
715 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
716 |
$ipRangeBlocked = true;
|
717 |
}
|
718 |
}
|
@@ -872,7 +894,7 @@ class wfLog {
|
|
872 |
wordfence::status(2, 'info', "Blocking IP $IP. $reason");
|
873 |
} else if($action == 'throttle'){
|
874 |
$IP = wfUtils::getIP();
|
875 |
-
$this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::
|
876 |
wordfence::status(2, 'info', "Throttling IP $IP. $reason");
|
877 |
wfConfig::inc('totalIPsThrottled');
|
878 |
$secsToGo = 60;
|
@@ -907,16 +929,11 @@ class wfLog {
|
|
907 |
} else if($nb == 'neverBlockUA' || $nb == 'neverBlockVerified'){
|
908 |
if(wfCrawl::isGoogleCrawler()){ //Check the UA using regex
|
909 |
if($nb == 'neverBlockVerified'){
|
910 |
-
if(wfCrawl::
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
self::$gbSafeCache[$cacheKey] = true; //This is a crawler claiming to be Google but it did not verify
|
915 |
-
}
|
916 |
-
} else { //UA isGoogleCrawler, but is not Googlebot itself. E.g. feedreader, google-site-verification, etc.
|
917 |
-
self::$gbSafeCache[$cacheKey] = false; //This is a crawler with a google UA, but it's not Googlebot, so we don't block for safety. We can't verify these because they don't have a PTR record. e.g. Feedreader.
|
918 |
}
|
919 |
-
|
920 |
} else { //neverBlockUA
|
921 |
self::$gbSafeCache[$cacheKey] = false; //User configured us to only do a UA check and this claims to be google so don't block
|
922 |
}
|
@@ -966,12 +983,17 @@ class wfLog {
|
|
966 |
return array_reverse($results);
|
967 |
}
|
968 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
969 |
}
|
970 |
|
971 |
/**
|
972 |
-
* @todo Add IPv6 support
|
973 |
*
|
974 |
-
* Class wfUserIPRange
|
975 |
*/
|
976 |
class wfUserIPRange {
|
977 |
|
@@ -990,30 +1012,62 @@ class wfUserIPRange {
|
|
990 |
/**
|
991 |
* Check if the supplied IP address is within the user supplied range.
|
992 |
*
|
993 |
-
* @param $ip
|
994 |
* @return bool
|
995 |
*/
|
996 |
public function isIPInRange($ip) {
|
997 |
$ip_string = $this->getIPString();
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
$
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1005 |
$mismatch = true;
|
1006 |
}
|
1007 |
-
} else if ($whiteParts[$i] != $IPparts[$i]) {
|
1008 |
-
$mismatch = true;
|
1009 |
}
|
|
|
|
|
|
|
|
|
|
|
1010 |
}
|
1011 |
-
|
1012 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1013 |
}
|
1014 |
-
} else if ($ip_string == $ip) {
|
1015 |
-
return true;
|
1016 |
}
|
|
|
1017 |
return false;
|
1018 |
}
|
1019 |
|
@@ -1027,23 +1081,123 @@ class wfUserIPRange {
|
|
1027 |
/** @var wpdb $wpdb */
|
1028 |
global $wpdb;
|
1029 |
$ip_string = $this->getIPString();
|
1030 |
-
|
1031 |
-
|
1032 |
$whiteParts = explode('.', $ip_string);
|
|
|
|
|
1033 |
for ($i = 0, $j = 24; $i <= 3; $i++, $j -= 8) {
|
|
|
|
|
1034 |
if (preg_match('/^\[(\d+)\-(\d+)\]#x2F;', $whiteParts[$i], $m)) {
|
1035 |
-
$sql .= $wpdb->prepare("$
|
1036 |
} else {
|
1037 |
-
$sql .= $wpdb->prepare("$
|
1038 |
}
|
1039 |
$sql .= ' AND ';
|
1040 |
}
|
1041 |
$sql = substr($sql, 0, -5) . ')';
|
1042 |
return $sql;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1043 |
}
|
1044 |
-
|
|
|
1045 |
}
|
1046 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1047 |
/**
|
1048 |
* @return string|null
|
1049 |
*/
|
26 |
$this->perfTable = $wpdb->base_prefix . 'wfPerfLog';
|
27 |
}
|
28 |
public function logPerf($IP, $UA, $URL, $data){
|
29 |
+
$IP = wfUtils::inet_pton($IP);
|
30 |
$this->getDB()->queryWrite("insert into " . $this->perfTable . " (IP, userID, UA, URL, ctime, fetchStart, domainLookupStart, domainLookupEnd, connectStart, connectEnd, requestStart, responseStart, responseEnd, domReady, loaded) values (%s, %d, '%s', '%s', unix_timestamp(), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
|
31 |
$IP,
|
32 |
$this->getCurrentUserID(),
|
67 |
$action,
|
68 |
$username,
|
69 |
$userID,
|
70 |
+
wfUtils::inet_pton(wfUtils::getIP()),
|
71 |
(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '')
|
72 |
);
|
73 |
}
|
79 |
if(wfConfig::get('firewallEnabled')){
|
80 |
//Moved the following block into the "is fw enabled section" for optimization.
|
81 |
$IP = wfUtils::getIP();
|
82 |
+
$IPnum = wfUtils::inet_pton($IP);
|
83 |
if($this->isWhitelisted($IP)){
|
84 |
return;
|
85 |
}
|
86 |
+
if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) {
|
87 |
+
return;
|
88 |
+
}
|
89 |
+
if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) {
|
90 |
+
return;
|
91 |
+
}
|
92 |
+
|
93 |
if($type == '404'){
|
94 |
$table = $this->scanTable;
|
95 |
} else if($type == 'hit'){
|
98 |
wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): $type");
|
99 |
return;
|
100 |
}
|
101 |
+
$this->getDB()->queryWrite("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_pton($IP));
|
102 |
$hitsPerMinute = $this->getDB()->querySingle("select @wfcurrenthits");
|
103 |
//end block moved into "is fw enabled" section
|
104 |
|
133 |
if($pat){
|
134 |
$URL = wfUtils::getRequestedURL();
|
135 |
if(preg_match($pat, $URL)){
|
136 |
+
$this->getDB()->queryWrite("insert IGNORE into $p"."wfVulnScanners (IP, ctime, hits) values (%s, unix_timestamp(), 1) ON DUPLICATE KEY UPDATE ctime = unix_timestamp(), hits = hits + 1", wfUtils::inet_pton($IP));
|
137 |
if(wfConfig::get('maxScanHits') != 'DISABLED'){
|
138 |
if( empty($_SERVER['HTTP_REFERER'] )){
|
139 |
$this->getDB()->queryWrite("insert into " . $this->badLeechersTable . " (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfblcurrenthits := hits + 1, hits + 1, hits + 1)", $IPnum);
|
166 |
}
|
167 |
}
|
168 |
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @param string $IP Should be in dot or colon notation (127.0.0.1 or ::1)
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
public function isWhitelisted($IP) {
|
175 |
+
$wfIPBlock = new wfUserIPRange('69.46.36.[1-32]');
|
176 |
+
if ($wfIPBlock->isIPInRange($IP)) { //IP is in Wordfence's IP block which would prevent our scanning server manually kicking off scans that are stuck
|
177 |
return true;
|
178 |
}
|
179 |
//We now whitelist all private addrs
|
180 |
+
if (wfUtils::isPrivateAddress($IP)) {
|
181 |
return true;
|
182 |
}
|
183 |
//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.
|
184 |
+
$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');
|
185 |
+
if (in_array($IP, $externalWhite)) {
|
186 |
return true;
|
187 |
}
|
188 |
$list = wfConfig::get('whitelisted');
|
189 |
+
if (!$list) {
|
190 |
+
return false;
|
191 |
+
}
|
192 |
$list = explode(',', $list);
|
193 |
+
if (sizeof($list) < 1) {
|
194 |
+
return false;
|
195 |
+
}
|
196 |
+
foreach ($list as $whiteIP) {
|
197 |
+
$white_ip_block = new wfUserIPRange($whiteIP);
|
198 |
+
if ($white_ip_block->isIPInRange($IP)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
return true;
|
200 |
}
|
201 |
}
|
222 |
}
|
223 |
|
224 |
// 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.
|
225 |
+
$white_listed_ips = array_merge($white_listed_ips, array_map(array('wfUtils', 'inet_pton'), 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')));
|
226 |
|
227 |
if ($user_whitelisted === null) {
|
228 |
$user_whitelisted = wfConfig::get('whitelisted');
|
246 |
$this->getDB()->queryWrite("delete from " . $this->lockOutTable);
|
247 |
}
|
248 |
public function unblockIP($IP){
|
249 |
+
$this->getDB()->queryWrite("delete from " . $this->blocksTable . " where IP=%s", wfUtils::inet_pton($IP));
|
250 |
wfCache::updateBlockedIPs('add');
|
251 |
}
|
252 |
public function unblockRange($id){
|
253 |
$this->getDB()->queryWrite("delete from " . $this->ipRangesTable . " where id=%d", $id);
|
254 |
wfCache::updateBlockedIPs('add');
|
255 |
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
*
|
259 |
+
* @param string $blockType
|
260 |
+
* @param string $range
|
261 |
+
* @param string $reason
|
262 |
+
* @return bool
|
263 |
+
*/
|
264 |
public function blockRange($blockType, $range, $reason){
|
265 |
$this->getDB()->queryWrite("insert IGNORE into " . $this->ipRangesTable . " (blockType, blockString, ctime, reason, totalBlocked, lastBlocked) values ('%s', '%s', unix_timestamp(), '%s', 0, 0)", $blockType, $range, $reason);
|
266 |
wfCache::updateBlockedIPs('add');
|
293 |
$numBlockElements = 0;
|
294 |
if($blockDat[0]){
|
295 |
$numBlockElements++;
|
296 |
+
list($start_range, $end_range) = explode('-', $blockDat[0]);
|
297 |
+
if (!preg_match('/[\.:]/', $start_range)) {
|
298 |
+
$start_range = long2ip($start_range);
|
299 |
+
$end_range = long2ip($end_range);
|
300 |
+
}
|
301 |
+
$elem['ipPattern'] = "Block visitors with IP addresses in the range: " . $start_range . ' - ' . $end_range;
|
302 |
} else {
|
303 |
$elem['ipPattern'] = 'Allow all IP addresses';
|
304 |
}
|
329 |
if($permanent){
|
330 |
//Insert permanent=1 or update existing perm or non-per block to be permanent
|
331 |
$this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d, permanent=%d",
|
332 |
+
wfUtils::inet_pton($IP),
|
333 |
$timeBlockOccurred,
|
334 |
$reason,
|
335 |
$wfsn,
|
342 |
} else {
|
343 |
//insert perm=0 but don't update and make perm blocks non-perm.
|
344 |
$this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d",
|
345 |
+
wfUtils::inet_pton($IP),
|
346 |
$timeBlockOccurred,
|
347 |
$reason,
|
348 |
$wfsn,
|
362 |
public function lockOutIP($IP, $reason){
|
363 |
if($this->isWhitelisted($IP)){ return false; }
|
364 |
$this->getDB()->queryWrite("insert into " . $this->lockOutTable . " (IP, blockedTime, reason) values (%s, unix_timestamp(), '%s') ON DUPLICATE KEY update blockedTime=unix_timestamp(), reason='%s'",
|
365 |
+
wfUtils::inet_pton($IP),
|
366 |
$reason,
|
367 |
$reason
|
368 |
);
|
373 |
return true;
|
374 |
}
|
375 |
public function unlockOutIP($IP){
|
376 |
+
$this->getDB()->queryWrite("delete from " . $this->lockOutTable . " where IP=%s", wfUtils::inet_pton($IP));
|
377 |
}
|
378 |
public function isIPLockedOut($IP){
|
379 |
+
if($this->getDB()->querySingle("select IP from " . $this->lockOutTable . " where IP=%s and blockedTime + %s > unix_timestamp()", wfUtils::inet_pton($IP), wfConfig::get('loginSec_lockoutMins') * 60)){
|
380 |
+
$this->getDB()->queryWrite("update " . $this->lockOutTable . " set blockedHits = blockedHits + 1, lastAttempt = unix_timestamp() where IP=%s", wfUtils::inet_pton($IP));
|
381 |
return true;
|
382 |
} else {
|
383 |
return false;
|
391 |
}
|
392 |
$this->resolveIPs($results);
|
393 |
foreach($results as &$elem){
|
394 |
+
$elem['IP'] = wfUtils::inet_ntop($elem['IP']);
|
395 |
}
|
396 |
return $results;
|
397 |
}
|
404 |
}
|
405 |
$this->resolveIPs($results);
|
406 |
foreach($results as &$elem){
|
407 |
+
$elem['IP'] = wfUtils::inet_ntop($elem['IP']);
|
408 |
}
|
409 |
return $results;
|
410 |
}
|
411 |
public function getBlockedIPsAddrOnly(){
|
412 |
+
$results = $this->getDB()->querySelect("select IP from " . $this->blocksTable . " where (permanent=1 OR (blockedTime + %s > unix_timestamp()))", wfConfig::get('blockedTime'), wfConfig::get('blockedTime'));
|
413 |
$ret = array();
|
414 |
foreach($results as $elem){
|
415 |
+
$ret[] = wfUtils::inet_ntop($elem['IP']);
|
416 |
}
|
417 |
return $ret;
|
418 |
}
|
444 |
$this->resolveIPs($results);
|
445 |
foreach($results as &$elem){
|
446 |
$elem['blocked'] = 1;
|
447 |
+
$elem['IP'] = wfUtils::inet_ntop($elem['IP']);
|
448 |
}
|
449 |
return $results;
|
450 |
}
|
463 |
$elem['timeAgo'] = wfUtils::makeTimeAgo($this->getDB()->querySingle("select unix_timestamp() - (eMin * 60) from $table where IP=%s", $elem['IP']));
|
464 |
$elem['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and ((blockedTime + %s > unix_timestamp()) OR permanent = 1)", $elem['IP'], wfConfig::get('blockedTime'));
|
465 |
//take action
|
466 |
+
$elem['IP'] = wfUtils::inet_ntop($elem['IP']);
|
467 |
}
|
468 |
return $results;
|
469 |
}
|
479 |
sprintf('%.6f', microtime(true)),
|
480 |
(is_404() ? 1 : 0),
|
481 |
(wfCrawl::isGoogleCrawler() ? 1 : 0),
|
482 |
+
wfUtils::inet_pton(wfUtils::getIP()),
|
483 |
$this->getCurrentUserID(),
|
484 |
(wordfence::$newVisit ? 1 : 0),
|
485 |
wfUtils::getRequestedURL(),
|
495 |
$browscap = new wfBrowscap();
|
496 |
foreach($results as &$res){
|
497 |
$res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']);
|
498 |
+
$res['IP'] = wfUtils::inet_ntop($res['IP']);
|
499 |
$res['browser'] = false;
|
500 |
if($res['UA']){
|
501 |
$b = $browscap->getBrowser($res['UA']);
|
529 |
$serverTime = $this->getDB()->querySingle("select unix_timestamp()");
|
530 |
$IPSQL = "";
|
531 |
if($IP){
|
532 |
+
$IPSQL = " and IP=%s ";
|
533 |
+
$sqlArgs = array($afterTime, wfUtils::inet_pton($IP), $limit);
|
534 |
} else {
|
535 |
$sqlArgs = array($afterTime, $limit);
|
536 |
}
|
572 |
$res['type'] = $type;
|
573 |
$res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']);
|
574 |
$res['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and (permanent = 1 OR (blockedTime + %s > unix_timestamp()))", $res['IP'], wfConfig::get('blockedTime'));
|
575 |
+
$res['IP'] = wfUtils::inet_ntop($res['IP']);
|
576 |
$res['extReferer'] = false;
|
577 |
if(isset( $res['referer'] ) && $res['referer']){
|
578 |
if(wfUtils::hasXSS($res['referer'] )){ //filtering out XSS
|
655 |
$IPLocs = wfUtils::getIPsGeo($IPs); //Creates an array with IP as key and data as value
|
656 |
|
657 |
foreach($results as &$res){
|
658 |
+
$ip_printable = wfUtils::inet_ntop($res['IP']);
|
659 |
+
if(isset($IPLocs[$ip_printable])){
|
660 |
+
$res['loc'] = $IPLocs[$ip_printable];
|
661 |
} else {
|
662 |
$res['loc'] = false;
|
663 |
}
|
710 |
if($this->isWhitelisted($IP)){
|
711 |
return;
|
712 |
}
|
713 |
+
$IPnum = wfUtils::inet_pton($IP);
|
714 |
|
715 |
//New range and UA pattern blocking:
|
716 |
$r1 = $this->getDB()->querySelect("select id, blockType, blockString from " . $this->ipRangesTable);
|
725 |
$uaPattern = $bDat[1];
|
726 |
$refPattern = isset($bDat[2]) ? $bDat[2] : '';
|
727 |
if($ipRange){
|
728 |
+
list($start_range, $end_range) = explode('-', $ipRange);
|
729 |
+
if (preg_match('/[\.:]/', $start_range)) {
|
730 |
+
$start_range = wfUtils::inet_pton($start_range);
|
731 |
+
$end_range = wfUtils::inet_pton($end_range);
|
732 |
+
} else {
|
733 |
+
$start_range = wfUtils::inet_pton(long2ip($start_range));
|
734 |
+
$end_range = wfUtils::inet_pton(long2ip($end_range));
|
735 |
+
}
|
736 |
+
|
737 |
+
if (strcmp($IPnum, $start_range) >= 0 && strcmp($IPnum, $end_range) <= 0) {
|
738 |
$ipRangeBlocked = true;
|
739 |
}
|
740 |
}
|
894 |
wordfence::status(2, 'info', "Blocking IP $IP. $reason");
|
895 |
} else if($action == 'throttle'){
|
896 |
$IP = wfUtils::getIP();
|
897 |
+
$this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_pton($IP), $reason, $reason);
|
898 |
wordfence::status(2, 'info', "Throttling IP $IP. $reason");
|
899 |
wfConfig::inc('totalIPsThrottled');
|
900 |
$secsToGo = 60;
|
929 |
} else if($nb == 'neverBlockUA' || $nb == 'neverBlockVerified'){
|
930 |
if(wfCrawl::isGoogleCrawler()){ //Check the UA using regex
|
931 |
if($nb == 'neverBlockVerified'){
|
932 |
+
if(wfCrawl::verifyCrawlerPTR($this->googlePattern, wfUtils::getIP())){ //UA check passed, now verify using PTR if configured to
|
933 |
+
self::$gbSafeCache[$cacheKey] = false; //This is a verified Google crawler, so no we can't block it
|
934 |
+
} else {
|
935 |
+
self::$gbSafeCache[$cacheKey] = true; //This is a crawler claiming to be Google but it did not verify
|
|
|
|
|
|
|
|
|
936 |
}
|
|
|
937 |
} else { //neverBlockUA
|
938 |
self::$gbSafeCache[$cacheKey] = false; //User configured us to only do a UA check and this claims to be google so don't block
|
939 |
}
|
983 |
return array_reverse($results);
|
984 |
}
|
985 |
|
986 |
+
/**
|
987 |
+
* @return string
|
988 |
+
*/
|
989 |
+
public function getGooglePattern() {
|
990 |
+
return $this->googlePattern;
|
991 |
+
}
|
992 |
+
|
993 |
}
|
994 |
|
995 |
/**
|
|
|
996 |
*
|
|
|
997 |
*/
|
998 |
class wfUserIPRange {
|
999 |
|
1012 |
/**
|
1013 |
* Check if the supplied IP address is within the user supplied range.
|
1014 |
*
|
1015 |
+
* @param string $ip
|
1016 |
* @return bool
|
1017 |
*/
|
1018 |
public function isIPInRange($ip) {
|
1019 |
$ip_string = $this->getIPString();
|
1020 |
+
|
1021 |
+
// IPv4 range
|
1022 |
+
if (strpos($ip_string, '.') !== false && strpos($ip, '.') !== false) {
|
1023 |
+
if (preg_match('/\[\d+\-\d+\]/', $ip_string)) {
|
1024 |
+
$IPparts = explode('.', $ip);
|
1025 |
+
$whiteParts = explode('.', $ip_string);
|
1026 |
+
$mismatch = false;
|
1027 |
+
for ($i = 0; $i <= 3; $i++) {
|
1028 |
+
if (preg_match('/^\[(\d+)\-(\d+)\]#x2F;', $whiteParts[$i], $m)) {
|
1029 |
+
if ($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]) {
|
1030 |
+
$mismatch = true;
|
1031 |
+
}
|
1032 |
+
} else if ($whiteParts[$i] != $IPparts[$i]) {
|
1033 |
$mismatch = true;
|
1034 |
}
|
|
|
|
|
1035 |
}
|
1036 |
+
if ($mismatch === false) {
|
1037 |
+
return true; // Is whitelisted because we did not get a mismatch
|
1038 |
+
}
|
1039 |
+
} else if ($ip_string == $ip) {
|
1040 |
+
return true;
|
1041 |
}
|
1042 |
+
|
1043 |
+
// IPv6 range
|
1044 |
+
} else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
|
1045 |
+
if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
|
1046 |
+
$IPparts = explode(':', strtolower(wfUtils::expandIPv6Address($ip)));
|
1047 |
+
$whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
|
1048 |
+
$mismatch = false;
|
1049 |
+
for ($i = 0; $i <= 7; $i++) {
|
1050 |
+
if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]#x2F;i', $whiteParts[$i], $m)) {
|
1051 |
+
$ip_group = hexdec($IPparts[$i]);
|
1052 |
+
$range_group_from = hexdec($m[1]);
|
1053 |
+
$range_group_to = hexdec($m[2]);
|
1054 |
+
if ($ip_group < $range_group_from || $ip_group > $range_group_to) {
|
1055 |
+
$mismatch = true;
|
1056 |
+
break;
|
1057 |
+
}
|
1058 |
+
} else if ($whiteParts[$i] != $IPparts[$i]) {
|
1059 |
+
$mismatch = true;
|
1060 |
+
break;
|
1061 |
+
}
|
1062 |
+
}
|
1063 |
+
if ($mismatch === false) {
|
1064 |
+
return true; // Is whitelisted because we did not get a mismatch
|
1065 |
+
}
|
1066 |
+
} else if ($ip_string == $ip) {
|
1067 |
+
return true;
|
1068 |
}
|
|
|
|
|
1069 |
}
|
1070 |
+
|
1071 |
return false;
|
1072 |
}
|
1073 |
|
1081 |
/** @var wpdb $wpdb */
|
1082 |
global $wpdb;
|
1083 |
$ip_string = $this->getIPString();
|
1084 |
+
|
1085 |
+
if (strpos($ip_string, '.') !== false && preg_match('/\[\d+\-\d+\]/', $ip_string)) {
|
1086 |
$whiteParts = explode('.', $ip_string);
|
1087 |
+
$sql = "(SUBSTR($column, 1, 12) = LPAD(CHAR(0xff, 0xff), 12, CHAR(0)) AND ";
|
1088 |
+
|
1089 |
for ($i = 0, $j = 24; $i <= 3; $i++, $j -= 8) {
|
1090 |
+
// MySQL can only perform bitwise operations on integers
|
1091 |
+
$conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, 13, 8)), 16, 10) as UNSIGNED INTEGER)', $column);
|
1092 |
if (preg_match('/^\[(\d+)\-(\d+)\]#x2F;', $whiteParts[$i], $m)) {
|
1093 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFF BETWEEN %d AND %d", $m[1], $m[2]);
|
1094 |
} else {
|
1095 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFF = %d", $whiteParts[$i]);
|
1096 |
}
|
1097 |
$sql .= ' AND ';
|
1098 |
}
|
1099 |
$sql = substr($sql, 0, -5) . ')';
|
1100 |
return $sql;
|
1101 |
+
|
1102 |
+
} else if (strpos($ip_string, ':') !== false && preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/', $ip_string)) {
|
1103 |
+
$whiteParts = explode(':', strtolower(self::expandIPv6Range($ip_string)));
|
1104 |
+
$sql = '(';
|
1105 |
+
|
1106 |
+
for ($i = 0; $i <= 7; $i++) {
|
1107 |
+
// MySQL can only perform bitwise operations on integers
|
1108 |
+
$conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, %d, 8)), 16, 10) as UNSIGNED INTEGER)', $column, $i < 4 ? 1 : 9);
|
1109 |
+
$j = 16 * (3 - ($i % 4));
|
1110 |
+
if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]#x2F;', $whiteParts[$i], $m)) {
|
1111 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFFFF BETWEEN 0x%x AND 0x%x", hexdec($m[1]), hexdec($m[2]));
|
1112 |
+
} else {
|
1113 |
+
$sql .= $wpdb->prepare("$conv >> $j & 0xFFFF = 0x%x", hexdec($whiteParts[$i]));
|
1114 |
+
}
|
1115 |
+
$sql .= ' AND ';
|
1116 |
+
}
|
1117 |
+
$sql = substr($sql, 0, -5) . ')';
|
1118 |
+
return $sql;
|
1119 |
+
}
|
1120 |
+
return $wpdb->prepare("($column = %s)", wfUtils::inet_pton($ip_string));
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
/**
|
1124 |
+
* Expand a compressed printable range representation of an IPv6 address.
|
1125 |
+
*
|
1126 |
+
* @todo Hook up exceptions for better error handling.
|
1127 |
+
* @todo Allow IPv4 mapped IPv6 addresses (::ffff:192.168.1.1).
|
1128 |
+
* @param string $ip_range
|
1129 |
+
* @return string
|
1130 |
+
*/
|
1131 |
+
public static function expandIPv6Range($ip_range) {
|
1132 |
+
$colon_count = substr_count($ip_range, ':');
|
1133 |
+
$dbl_colon_count = substr_count($ip_range, '::');
|
1134 |
+
if ($dbl_colon_count > 1) {
|
1135 |
+
return false;
|
1136 |
+
}
|
1137 |
+
$dbl_colon_pos = strpos($ip_range, '::');
|
1138 |
+
if ($dbl_colon_pos !== false) {
|
1139 |
+
$ip_range = str_replace('::', str_repeat(':0000',
|
1140 |
+
(($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip_range) - 2) ? 9 : 8) - $colon_count) . ':', $ip_range);
|
1141 |
+
$ip_range = trim($ip_range, ':');
|
1142 |
+
}
|
1143 |
+
$colon_count = substr_count($ip_range, ':');
|
1144 |
+
if ($colon_count != 7) {
|
1145 |
+
return false;
|
1146 |
+
}
|
1147 |
+
|
1148 |
+
$groups = explode(':', $ip_range);
|
1149 |
+
$expanded = '';
|
1150 |
+
foreach ($groups as $group) {
|
1151 |
+
if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) {
|
1152 |
+
$expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':';
|
1153 |
+
} else if (preg_match('/[a-f0-9]{1,4}/i', $group)) {
|
1154 |
+
$expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':';
|
1155 |
+
} else {
|
1156 |
+
return false;
|
1157 |
+
}
|
1158 |
+
}
|
1159 |
+
return trim($expanded, ':');
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
/**
|
1163 |
+
* @return bool
|
1164 |
+
*/
|
1165 |
+
public function isValidRange() {
|
1166 |
+
return $this->isValidIPv4Range() || $this->isValidIPv6Range();
|
1167 |
+
}
|
1168 |
+
|
1169 |
+
/**
|
1170 |
+
* @return bool
|
1171 |
+
*/
|
1172 |
+
public function isValidIPv4Range() {
|
1173 |
+
$ip_string = $this->getIPString();
|
1174 |
+
preg_match('(\d)', $ip_string, $matches);
|
1175 |
+
foreach ($matches as $match) {
|
1176 |
+
if ($match[1] > 255 || $match[1] < 0) {
|
1177 |
+
return false;
|
1178 |
+
}
|
1179 |
}
|
1180 |
+
$group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])';
|
1181 |
+
return preg_match('/^' . str_repeat("$group_regex.", 3) . $group_regex . '#x2F;i', $ip_string) > 0;
|
1182 |
}
|
1183 |
|
1184 |
+
/**
|
1185 |
+
* @return bool
|
1186 |
+
*/
|
1187 |
+
public function isValidIPv6Range() {
|
1188 |
+
$ip_string = $this->getIPString();
|
1189 |
+
if (strpos($ip_string, '::') !== false) {
|
1190 |
+
$ip_string = self::expandIPv6Range($ip_string);
|
1191 |
+
}
|
1192 |
+
if (!$ip_string) {
|
1193 |
+
return false;
|
1194 |
+
}
|
1195 |
+
$group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])';
|
1196 |
+
return preg_match('/^' . str_repeat("$group_regex:", 7) . $group_regex . '#x2F;i', $ip_string) > 0;
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
|
1200 |
+
|
1201 |
/**
|
1202 |
* @return string|null
|
1203 |
*/
|
lib/wfScanEngine.php
CHANGED
@@ -21,6 +21,9 @@ class wfScanEngine {
|
|
21 |
public $maxExecTime = false; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
|
22 |
private $publicScanEnabled = false;
|
23 |
private $fileContentsResults = false;
|
|
|
|
|
|
|
24 |
private $scanner = false;
|
25 |
private $scanQueue = array();
|
26 |
private $hoover = false;
|
@@ -33,6 +36,17 @@ class wfScanEngine {
|
|
33 |
);
|
34 |
private $userPasswdQueue = "";
|
35 |
private $passwdHasIssues = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
37 |
return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues');
|
38 |
}
|
@@ -53,7 +67,7 @@ class wfScanEngine {
|
|
53 |
$this->jobList[] = 'knownFiles_init';
|
54 |
$this->jobList[] = 'knownFiles_main';
|
55 |
$this->jobList[] = 'knownFiles_finish';
|
56 |
-
foreach(array('knownFiles', 'fileContents', 'posts', 'comments', 'passwds', 'options', 'dns', 'diskSpace', 'oldVersions') as $scanType){
|
57 |
if(wfConfig::get('scansEnabled_' . $scanType)){
|
58 |
if(method_exists($this, 'scan_' . $scanType . '_init')){
|
59 |
foreach(array('init', 'main', 'finish') as $op){ $this->jobList[] = $scanType . '_' . $op; };
|
@@ -331,9 +345,40 @@ class wfScanEngine {
|
|
331 |
wordfence::statusEnd($this->statusIDX['infect'], $haveIssues);
|
332 |
wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
|
333 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
private function scan_posts_init(){
|
335 |
$this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
336 |
-
$blogsToScan =
|
337 |
$wfdb = new wfDB();
|
338 |
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
339 |
foreach($blogsToScan as $blog){
|
@@ -443,7 +488,7 @@ class wfScanEngine {
|
|
443 |
$this->scanData = array();
|
444 |
$this->scanQueue = array();
|
445 |
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
446 |
-
$blogsToScan =
|
447 |
$wfdb = new wfDB();
|
448 |
foreach($blogsToScan as $blog){
|
449 |
$q1 = $wfdb->querySelect("select comment_ID from " . $blog['table'] . " where comment_approved=1");
|
@@ -565,7 +610,7 @@ class wfScanEngine {
|
|
565 |
$this->status(2, 'info', "Scanned comment with $cDesc");
|
566 |
return false;
|
567 |
}
|
568 |
-
public function getBlogsToScan($table){
|
569 |
$wfdb = new wfDB();
|
570 |
global $wpdb;
|
571 |
$prefix = $wpdb->base_prefix;
|
@@ -739,7 +784,7 @@ class wfScanEngine {
|
|
739 |
}
|
740 |
}
|
741 |
private function scan_options(){
|
742 |
-
$blogsToScan =
|
743 |
$wfdb = new wfDB();
|
744 |
foreach($blogsToScan as $blog){
|
745 |
$charset = $wfdb->querySingle("select option_value from " . $blog['table'] . " where option_name='blog_charset'");
|
21 |
public $maxExecTime = false; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
|
22 |
private $publicScanEnabled = false;
|
23 |
private $fileContentsResults = false;
|
24 |
+
/**
|
25 |
+
* @var bool|wordfenceScanner
|
26 |
+
*/
|
27 |
private $scanner = false;
|
28 |
private $scanQueue = array();
|
29 |
private $hoover = false;
|
36 |
);
|
37 |
private $userPasswdQueue = "";
|
38 |
private $passwdHasIssues = false;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var array
|
42 |
+
*/
|
43 |
+
private $databaseResults;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var wordfenceDBScanner
|
47 |
+
*/
|
48 |
+
private $dbScanner;
|
49 |
+
|
50 |
public function __sleep(){ //Same order here as above for properties that are included in serialization
|
51 |
return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues');
|
52 |
}
|
67 |
$this->jobList[] = 'knownFiles_init';
|
68 |
$this->jobList[] = 'knownFiles_main';
|
69 |
$this->jobList[] = 'knownFiles_finish';
|
70 |
+
foreach(array('knownFiles', 'fileContents', 'database', 'posts', 'comments', 'passwds', 'options', 'dns', 'diskSpace', 'oldVersions') as $scanType){
|
71 |
if(wfConfig::get('scansEnabled_' . $scanType)){
|
72 |
if(method_exists($this, 'scan_' . $scanType . '_init')){
|
73 |
foreach(array('init', 'main', 'finish') as $op){ $this->jobList[] = $scanType . '_' . $op; };
|
345 |
wordfence::statusEnd($this->statusIDX['infect'], $haveIssues);
|
346 |
wordfence::statusEnd($this->statusIDX['GSB'], $haveIssuesGSB);
|
347 |
}
|
348 |
+
|
349 |
+
private function scan_database_init() {
|
350 |
+
$this->statusIDX['db_infect'] = wordfence::statusStart('Scanning database for infections and vulnerabilities');
|
351 |
+
$this->dbScanner = new wordfenceDBScanner($this->apiKey, $this->wp_version, ABSPATH);
|
352 |
+
$this->status(2, 'info', "Starting scan of database");
|
353 |
+
}
|
354 |
+
|
355 |
+
private function scan_database_main() {
|
356 |
+
if (!$this->dbScanner) {
|
357 |
+
$this->dbScanner = new wordfenceDBScanner($this->apiKey, $this->wp_version, ABSPATH);
|
358 |
+
}
|
359 |
+
$this->databaseResults = $this->dbScanner->scan($this);
|
360 |
+
}
|
361 |
+
|
362 |
+
private function scan_database_finish() {
|
363 |
+
$this->status(2, 'info', "Done database scan");
|
364 |
+
if ($this->dbScanner->errorMsg) {
|
365 |
+
throw new Exception($this->dbScanner->errorMsg);
|
366 |
+
}
|
367 |
+
$this->dbScanner = null;
|
368 |
+
$haveIssues = false;
|
369 |
+
foreach ($this->databaseResults as $issue) {
|
370 |
+
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
371 |
+
$issue_success = $this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
|
372 |
+
if ($issue_success) {
|
373 |
+
$haveIssues = true;
|
374 |
+
}
|
375 |
+
}
|
376 |
+
$this->databaseResults = null;
|
377 |
+
wordfence::statusEnd($this->statusIDX['db_infect'], $haveIssues);
|
378 |
+
}
|
379 |
private function scan_posts_init(){
|
380 |
$this->statusIDX['posts'] = wordfence::statusStart('Scanning posts for URL\'s in Google\'s Safe Browsing List');
|
381 |
+
$blogsToScan = self::getBlogsToScan('posts');
|
382 |
$wfdb = new wfDB();
|
383 |
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
384 |
foreach($blogsToScan as $blog){
|
488 |
$this->scanData = array();
|
489 |
$this->scanQueue = array();
|
490 |
$this->hoover = new wordfenceURLHoover($this->apiKey, $this->wp_version);
|
491 |
+
$blogsToScan = self::getBlogsToScan('comments');
|
492 |
$wfdb = new wfDB();
|
493 |
foreach($blogsToScan as $blog){
|
494 |
$q1 = $wfdb->querySelect("select comment_ID from " . $blog['table'] . " where comment_approved=1");
|
610 |
$this->status(2, 'info', "Scanned comment with $cDesc");
|
611 |
return false;
|
612 |
}
|
613 |
+
public static function getBlogsToScan($table){
|
614 |
$wfdb = new wfDB();
|
615 |
global $wpdb;
|
616 |
$prefix = $wpdb->base_prefix;
|
784 |
}
|
785 |
}
|
786 |
private function scan_options(){
|
787 |
+
$blogsToScan = self::getBlogsToScan('options');
|
788 |
$wfdb = new wfDB();
|
789 |
foreach($blogsToScan as $blog){
|
790 |
$charset = $wfdb->querySingle("select option_value from " . $blog['table'] . " where option_name='blog_charset'");
|
lib/wfUtils.php
CHANGED
@@ -65,27 +65,68 @@ class wfUtils {
|
|
65 |
return "$m1 $t1";
|
66 |
}
|
67 |
}
|
68 |
-
public static function formatBytes($bytes, $precision = 2) {
|
69 |
-
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
70 |
|
71 |
-
$bytes = max($bytes, 0);
|
72 |
-
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
73 |
-
$pow = min($pow, count($units) - 1);
|
74 |
|
75 |
// Uncomment one of the following alternatives
|
76 |
$bytes /= pow(1024, $pow);
|
77 |
// $bytes /= (1 << (10 * $pow));
|
78 |
|
79 |
-
return round($bytes, $precision) . ' ' . $units[$pow];
|
80 |
-
}
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
$long = 4294967295 - ($ip - 1);
|
83 |
return long2ip(-$long);
|
84 |
}
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
$ip = preg_replace('/(?<=^|\.)0+([1-9])/', '$1', $ip);
|
87 |
return sprintf("%u", ip2long($ip));
|
88 |
}
|
|
|
89 |
public static function hasLoginCookie(){
|
90 |
if(isset($_COOKIE)){
|
91 |
if(is_array($_COOKIE)){
|
@@ -117,25 +158,45 @@ class wfUtils {
|
|
117 |
public static function makeRandomIP(){
|
118 |
return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
|
119 |
}
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
}
|
126 |
}
|
127 |
-
|
|
|
|
|
128 |
}
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
$item = $arr[$i];
|
134 |
-
if(is_array($item)){
|
135 |
foreach($item as $j){
|
136 |
-
|
137 |
-
if
|
138 |
-
|
|
|
|
|
|
|
139 |
$privates[] = $j;
|
140 |
} else {
|
141 |
return $j;
|
@@ -146,10 +207,12 @@ class wfUtils {
|
|
146 |
}
|
147 |
$skipToNext = false;
|
148 |
foreach(array(',', ' ', "\t") as $char){
|
149 |
-
if(strpos($item, $char) !== false){
|
150 |
$sp = explode($char, $item);
|
151 |
foreach($sp as $j){
|
152 |
-
|
|
|
|
|
153 |
if(self::isValidIP($j)){
|
154 |
if(self::isPrivateAddress($j)){
|
155 |
$privates[] = $j;
|
@@ -164,7 +227,9 @@ class wfUtils {
|
|
164 |
}
|
165 |
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.
|
166 |
|
167 |
-
|
|
|
|
|
168 |
if(self::isValidIP($item)){
|
169 |
if(self::isPrivateAddress($item)){
|
170 |
$privates[] = $item;
|
@@ -198,25 +263,16 @@ class wfUtils {
|
|
198 |
$IP = self::getCleanIP(array($_SERVER[$howGet], $_SERVER['REMOTE_ADDR']));
|
199 |
}
|
200 |
} else {
|
201 |
-
|
|
|
202 |
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ $IPs[] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
|
203 |
if(isset($_SERVER['HTTP_X_REAL_IP'])){ $IPs[] = $_SERVER['HTTP_X_REAL_IP']; }
|
204 |
-
$IP = self::getCleanIP($IPs);
|
205 |
}
|
206 |
return $IP; //Returns a valid IP or false.
|
207 |
}
|
208 |
public static function isValidIP($IP){
|
209 |
-
|
210 |
-
if(
|
211 |
-
$m[1] >= 0 && $m[1] <= 255 &&
|
212 |
-
$m[2] >= 0 && $m[2] <= 255 &&
|
213 |
-
$m[3] >= 0 && $m[3] <= 255 &&
|
214 |
-
$m[4] >= 0 && $m[4] <= 255
|
215 |
-
){
|
216 |
-
return true;
|
217 |
-
}
|
218 |
-
}
|
219 |
-
return false;
|
220 |
}
|
221 |
public static function getRequestedURL(){
|
222 |
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']){
|
@@ -262,14 +318,14 @@ class wfUtils {
|
|
262 |
return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
|
263 |
}
|
264 |
public static function lcmem(){
|
265 |
-
$trace=debug_backtrace();
|
266 |
-
$caller=array_shift($trace);
|
267 |
$mem = memory_get_usage(true);
|
268 |
error_log("$mem at " . $caller['file'] . " line " . $caller['line']);
|
269 |
}
|
270 |
public static function logCaller(){
|
271 |
-
$trace=debug_backtrace();
|
272 |
-
$caller=array_shift($trace);
|
273 |
$c2 = array_shift($trace);
|
274 |
error_log("Caller for " . $caller['file'] . " line " . $caller['line'] . " is " . $c2['file'] . ' line ' . $c2['line']);
|
275 |
}
|
@@ -282,8 +338,8 @@ class wfUtils {
|
|
282 |
}
|
283 |
}
|
284 |
public static function isAdminPageMU(){
|
285 |
-
if(preg_match('/^[\/a-zA-Z0-9\-\_\s\+\~\!\^\.]*\/wp-admin\/network\//', $_SERVER['REQUEST_URI'])){
|
286 |
-
return true;
|
287 |
}
|
288 |
return false;
|
289 |
}
|
@@ -357,7 +413,7 @@ class wfUtils {
|
|
357 |
return self::$isWindows == 'yes' ? true : false;
|
358 |
}
|
359 |
public static function getScanLock(){
|
360 |
-
//Windows does not support non-blocking flock, so we use time.
|
361 |
$scanRunning = wfConfig::get('wf_scanRunning');
|
362 |
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
|
363 |
return false;
|
@@ -377,7 +433,7 @@ class wfUtils {
|
|
377 |
}
|
378 |
}
|
379 |
public static function getIPGeo($IP){ //Works with int or dotted
|
380 |
-
|
381 |
$locs = self::getIPsGeo(array($IP));
|
382 |
if(isset($locs[$IP])){
|
383 |
return $locs[$IP];
|
@@ -387,53 +443,57 @@ class wfUtils {
|
|
387 |
}
|
388 |
public static function getIPsGeo($IPs){ //works with int or dotted. Outputs same format it receives.
|
389 |
$IPs = array_unique($IPs);
|
390 |
-
$isInt = false;
|
391 |
-
if(strpos($IPs[0], '.') === false){
|
392 |
-
$isInt = true;
|
393 |
-
}
|
394 |
$toResolve = array();
|
395 |
$db = new wfDB();
|
396 |
global $wpdb;
|
397 |
$locsTable = $wpdb->base_prefix . 'wfLocs';
|
398 |
$IPLocs = array();
|
399 |
foreach($IPs as $IP){
|
400 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
401 |
if($row){
|
402 |
if($row['age'] > WORDFENCE_MAX_IPLOC_AGE){
|
403 |
$db->queryWrite("delete from " . $locsTable . " where IP=%s", $row['IP']);
|
404 |
} else {
|
405 |
if($row['failed'] == 1){
|
406 |
-
$IPLocs[$
|
407 |
} else {
|
408 |
-
|
409 |
-
|
410 |
-
}
|
411 |
-
$IPLocs[$IP] = $row;
|
412 |
}
|
413 |
}
|
414 |
}
|
415 |
-
if(! isset($IPLocs[$
|
416 |
-
$toResolve[] = $
|
417 |
}
|
418 |
}
|
419 |
if(sizeof($toResolve) > 0){
|
420 |
-
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
421 |
try {
|
422 |
$freshIPs = $api->call('resolve_ips', array(), array(
|
423 |
'ips' => implode(',', $toResolve)
|
424 |
));
|
425 |
if(is_array($freshIPs)){
|
426 |
foreach($freshIPs as $IP => $value){
|
|
|
427 |
if($value == 'failed'){
|
428 |
-
$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)",
|
429 |
$IPLocs[$IP] = false;
|
430 |
} else if(is_array($value)){
|
431 |
for($i = 0; $i <= 5; $i++){
|
432 |
//Prevent warnings in debug mode about uninitialized values
|
433 |
if(! isset($value[$i])){ $value[$i] = ''; }
|
434 |
}
|
435 |
-
$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
|
436 |
-
|
437 |
$value[3], //city
|
438 |
$value[2], //region
|
439 |
$value[1], //countryName
|
@@ -460,25 +520,39 @@ class wfUtils {
|
|
460 |
}
|
461 |
return $IPLocs;
|
462 |
}
|
463 |
-
|
|
|
464 |
$db = new wfDB();
|
465 |
global $wpdb;
|
466 |
$reverseTable = $wpdb->base_prefix . 'wfReverseCache';
|
467 |
-
$IPn = wfUtils::
|
468 |
$host = $db->querySingle("select host from " . $reverseTable . " where IP=%s and unix_timestamp() - lastUpdate < %d", $IPn, WORDFENCE_REVERSE_LOOKUP_CACHE_TIME);
|
469 |
-
if
|
470 |
-
|
471 |
-
if (function_exists('
|
472 |
-
$host =
|
473 |
}
|
474 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
$host = 'NONE';
|
476 |
-
} else {
|
477 |
-
$host = $host[0]['target'];
|
478 |
}
|
479 |
$db->queryWrite("insert into " . $reverseTable . " (IP, host, lastUpdate) values (%s, '%s', unix_timestamp()) ON DUPLICATE KEY UPDATE host='%s', lastUpdate=unix_timestamp()", $IPn, $host, $host);
|
480 |
}
|
481 |
-
if($host == 'NONE
|
482 |
return '';
|
483 |
} else {
|
484 |
return $host;
|
@@ -502,7 +576,7 @@ class wfUtils {
|
|
502 |
$fh = @fopen($file, 'r');
|
503 |
wfUtils::errorsOn();
|
504 |
if(! $fh){ return false; }
|
505 |
-
$offset = WORDFENCE_MAX_FILE_SIZE_TO_PROCESS + 1;
|
506 |
$tooBig = false;
|
507 |
try {
|
508 |
if(@fseek($fh, $offset, SEEK_SET) === 0){
|
@@ -517,7 +591,7 @@ class wfUtils {
|
|
517 |
public static function fileOver2Gigs($file){ //Surround calls to this func with try/catch because fseek may throw error.
|
518 |
$fh = @fopen($file, 'r');
|
519 |
if(! $fh){ return false; }
|
520 |
-
$offset = 2147483647;
|
521 |
$tooBig = false;
|
522 |
//My throw an error so surround calls to this func with try/catch
|
523 |
if(@fseek($fh, $offset, SEEK_SET) === 0){
|
@@ -542,11 +616,15 @@ class wfUtils {
|
|
542 |
return $URL;
|
543 |
}
|
544 |
public static function IP2Country($IP){
|
545 |
-
if(! (function_exists('geoip_open') && function_exists('geoip_country_code_by_addr'))){
|
546 |
require_once('wfGeoIP.php');
|
547 |
}
|
548 |
$gi = geoip_open(dirname(__FILE__) . "/GeoIP.dat",GEOIP_STANDARD);
|
549 |
-
|
|
|
|
|
|
|
|
|
550 |
geoip_close($gi);
|
551 |
return $country ? $country : '';
|
552 |
}
|
@@ -594,7 +672,35 @@ class wfUtils {
|
|
594 |
public static function isRefererBlocked($refPattern){
|
595 |
return fnmatch($refPattern, $_SERVER['HTTP_REFERER'], FNM_CASEFOLD);
|
596 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
597 |
public static function rangeToCIDRs($startIP, $endIP){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
598 |
$startIPBin = sprintf('%032b', $startIP);
|
599 |
$endIPBin = sprintf('%032b', $endIP);
|
600 |
$IPIncBin = $startIPBin;
|
@@ -611,6 +717,7 @@ class wfUtils {
|
|
611 |
}
|
612 |
return $CIDRs;
|
613 |
}
|
|
|
614 |
public static function setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly){
|
615 |
if(version_compare(PHP_VERSION, '5.2.0') >= 0){
|
616 |
@setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
|
@@ -661,6 +768,42 @@ class wfUtils {
|
|
661 |
public static function getPrivateAddrs() {
|
662 |
return self::$privateAddrs;
|
663 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
664 |
}
|
665 |
|
666 |
|
65 |
return "$m1 $t1";
|
66 |
}
|
67 |
}
|
68 |
+
public static function formatBytes($bytes, $precision = 2) {
|
69 |
+
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
70 |
|
71 |
+
$bytes = max($bytes, 0);
|
72 |
+
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
73 |
+
$pow = min($pow, count($units) - 1);
|
74 |
|
75 |
// Uncomment one of the following alternatives
|
76 |
$bytes /= pow(1024, $pow);
|
77 |
// $bytes /= (1 << (10 * $pow));
|
78 |
|
79 |
+
return round($bytes, $precision) . ' ' . $units[$pow];
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Return dot or colon notation of IPv4 or IPv6 address.
|
84 |
+
*
|
85 |
+
* @param string $ip
|
86 |
+
* @return string|bool
|
87 |
+
*/
|
88 |
+
public static function inet_ntop($ip) {
|
89 |
+
// trim this to the IPv4 equiv if it's in the mapped range
|
90 |
+
if (strlen($ip) == 16 && substr($ip, 0, 12) == pack("H*", '00000000000000000000ffff')) {
|
91 |
+
$ip = substr($ip, 12, 4);
|
92 |
+
}
|
93 |
+
return inet_ntop($ip);
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Return dot notation of IPv4 address.
|
98 |
+
*
|
99 |
+
* @param int $ip
|
100 |
+
* @return string|bool
|
101 |
+
*/
|
102 |
+
public static function inet_ntoa($ip) {
|
103 |
$long = 4294967295 - ($ip - 1);
|
104 |
return long2ip(-$long);
|
105 |
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Return the packed binary string of an IPv4 or IPv6 address.
|
109 |
+
*
|
110 |
+
* @param string $ip
|
111 |
+
* @return string
|
112 |
+
*/
|
113 |
+
public static function inet_pton($ip) {
|
114 |
+
// convert the 4 char IPv4 to IPv6 mapped version.
|
115 |
+
$pton = str_pad(inet_pton($ip), 16, pack("H*", '00000000000000000000ffff00000000'), STR_PAD_LEFT);
|
116 |
+
return $pton;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Return string representation of 32 bit int of the IP address.
|
121 |
+
*
|
122 |
+
* @param string $ip
|
123 |
+
* @return string
|
124 |
+
*/
|
125 |
+
public static function inet_aton($ip) {
|
126 |
$ip = preg_replace('/(?<=^|\.)0+([1-9])/', '$1', $ip);
|
127 |
return sprintf("%u", ip2long($ip));
|
128 |
}
|
129 |
+
|
130 |
public static function hasLoginCookie(){
|
131 |
if(isset($_COOKIE)){
|
132 |
if(is_array($_COOKIE)){
|
158 |
public static function makeRandomIP(){
|
159 |
return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
|
160 |
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* @param string $addr Should be in dot or colon notation (127.0.0.1 or ::1)
|
164 |
+
* @return bool
|
165 |
+
*/
|
166 |
+
public static function isPrivateAddress($addr) {
|
167 |
+
// Run this through the preset list for IPv4 addresses.
|
168 |
+
if (filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
|
169 |
+
$num = self::inet_aton($addr);
|
170 |
+
foreach (self::$privateAddrs as $a) {
|
171 |
+
if ($num >= $a[1] && $num <= $a[2]) {
|
172 |
+
return true;
|
173 |
+
}
|
174 |
}
|
175 |
}
|
176 |
+
|
177 |
+
return filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false
|
178 |
+
&& filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
|
179 |
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* 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.
|
183 |
+
* 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.
|
184 |
+
*
|
185 |
+
* @param array $arr
|
186 |
+
* @return bool|mixed
|
187 |
+
*/
|
188 |
+
private static function getCleanIP($arr){
|
189 |
+
$privates = array(); //Store private addrs until end as last resort.
|
190 |
+
for($i = 0; $i < count($arr); $i++){
|
191 |
$item = $arr[$i];
|
192 |
+
if(is_array($item)){
|
193 |
foreach($item as $j){
|
194 |
+
// try verifying the IP is valid before stripping the port off
|
195 |
+
if (!self::isValidIP($j)) {
|
196 |
+
$j = preg_replace('/:\d+#x2F;', '', $j); //Strip off port
|
197 |
+
}
|
198 |
+
if (self::isValidIP($j)) {
|
199 |
+
if (self::isPrivateAddress($j)) {
|
200 |
$privates[] = $j;
|
201 |
} else {
|
202 |
return $j;
|
207 |
}
|
208 |
$skipToNext = false;
|
209 |
foreach(array(',', ' ', "\t") as $char){
|
210 |
+
if(strpos($item, $char) !== false){
|
211 |
$sp = explode($char, $item);
|
212 |
foreach($sp as $j){
|
213 |
+
if (!self::isValidIP($j)) {
|
214 |
+
$j = preg_replace('/:\d+#x2F;', '', $j); //Strip off port
|
215 |
+
}
|
216 |
if(self::isValidIP($j)){
|
217 |
if(self::isPrivateAddress($j)){
|
218 |
$privates[] = $j;
|
227 |
}
|
228 |
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.
|
229 |
|
230 |
+
if (!self::isValidIP($item)) {
|
231 |
+
$item = preg_replace('/:\d+#x2F;', '', $item); //Strip off port
|
232 |
+
}
|
233 |
if(self::isValidIP($item)){
|
234 |
if(self::isPrivateAddress($item)){
|
235 |
$privates[] = $item;
|
263 |
$IP = self::getCleanIP(array($_SERVER[$howGet], $_SERVER['REMOTE_ADDR']));
|
264 |
}
|
265 |
} else {
|
266 |
+
// if no REMOTE_ADDR, it's probably running from the command line
|
267 |
+
$IPs = array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1');
|
268 |
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ $IPs[] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
|
269 |
if(isset($_SERVER['HTTP_X_REAL_IP'])){ $IPs[] = $_SERVER['HTTP_X_REAL_IP']; }
|
270 |
+
$IP = self::getCleanIP($IPs);
|
271 |
}
|
272 |
return $IP; //Returns a valid IP or false.
|
273 |
}
|
274 |
public static function isValidIP($IP){
|
275 |
+
return filter_var($IP, FILTER_VALIDATE_IP) !== false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
}
|
277 |
public static function getRequestedURL(){
|
278 |
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']){
|
318 |
return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
|
319 |
}
|
320 |
public static function lcmem(){
|
321 |
+
$trace=debug_backtrace();
|
322 |
+
$caller=array_shift($trace);
|
323 |
$mem = memory_get_usage(true);
|
324 |
error_log("$mem at " . $caller['file'] . " line " . $caller['line']);
|
325 |
}
|
326 |
public static function logCaller(){
|
327 |
+
$trace=debug_backtrace();
|
328 |
+
$caller=array_shift($trace);
|
329 |
$c2 = array_shift($trace);
|
330 |
error_log("Caller for " . $caller['file'] . " line " . $caller['line'] . " is " . $c2['file'] . ' line ' . $c2['line']);
|
331 |
}
|
338 |
}
|
339 |
}
|
340 |
public static function isAdminPageMU(){
|
341 |
+
if(preg_match('/^[\/a-zA-Z0-9\-\_\s\+\~\!\^\.]*\/wp-admin\/network\//', $_SERVER['REQUEST_URI'])){
|
342 |
+
return true;
|
343 |
}
|
344 |
return false;
|
345 |
}
|
413 |
return self::$isWindows == 'yes' ? true : false;
|
414 |
}
|
415 |
public static function getScanLock(){
|
416 |
+
//Windows does not support non-blocking flock, so we use time.
|
417 |
$scanRunning = wfConfig::get('wf_scanRunning');
|
418 |
if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_TIME){
|
419 |
return false;
|
433 |
}
|
434 |
}
|
435 |
public static function getIPGeo($IP){ //Works with int or dotted
|
436 |
+
|
437 |
$locs = self::getIPsGeo(array($IP));
|
438 |
if(isset($locs[$IP])){
|
439 |
return $locs[$IP];
|
443 |
}
|
444 |
public static function getIPsGeo($IPs){ //works with int or dotted. Outputs same format it receives.
|
445 |
$IPs = array_unique($IPs);
|
|
|
|
|
|
|
|
|
446 |
$toResolve = array();
|
447 |
$db = new wfDB();
|
448 |
global $wpdb;
|
449 |
$locsTable = $wpdb->base_prefix . 'wfLocs';
|
450 |
$IPLocs = array();
|
451 |
foreach($IPs as $IP){
|
452 |
+
$isBinaryIP = !self::isValidIP($IP);
|
453 |
+
if ($isBinaryIP) {
|
454 |
+
$ip_printable = wfUtils::inet_ntop($IP);
|
455 |
+
$ip_bin = $IP;
|
456 |
+
} else {
|
457 |
+
$ip_printable = $IP;
|
458 |
+
$ip_bin = wfUtils::inet_pton($IP);
|
459 |
+
}
|
460 |
+
|
461 |
+
$row = $db->querySingleRec("select IP, ctime, failed, city, region, countryName, countryCode, lat, lon, unix_timestamp() - ctime as age from " . $locsTable . " where IP=%s", $ip_bin);
|
462 |
if($row){
|
463 |
if($row['age'] > WORDFENCE_MAX_IPLOC_AGE){
|
464 |
$db->queryWrite("delete from " . $locsTable . " where IP=%s", $row['IP']);
|
465 |
} else {
|
466 |
if($row['failed'] == 1){
|
467 |
+
$IPLocs[$ip_printable] = false;
|
468 |
} else {
|
469 |
+
$row['IP'] = self::inet_ntop($row['IP']);
|
470 |
+
$IPLocs[$ip_printable] = $row;
|
|
|
|
|
471 |
}
|
472 |
}
|
473 |
}
|
474 |
+
if(! isset($IPLocs[$ip_printable])){
|
475 |
+
$toResolve[] = $ip_printable;
|
476 |
}
|
477 |
}
|
478 |
if(sizeof($toResolve) > 0){
|
479 |
+
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
|
480 |
try {
|
481 |
$freshIPs = $api->call('resolve_ips', array(), array(
|
482 |
'ips' => implode(',', $toResolve)
|
483 |
));
|
484 |
if(is_array($freshIPs)){
|
485 |
foreach($freshIPs as $IP => $value){
|
486 |
+
$IP_bin = wfUtils::inet_pton($IP);
|
487 |
if($value == 'failed'){
|
488 |
+
$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values (%s, unix_timestamp(), 1)", $IP_bin);
|
489 |
$IPLocs[$IP] = false;
|
490 |
} else if(is_array($value)){
|
491 |
for($i = 0; $i <= 5; $i++){
|
492 |
//Prevent warnings in debug mode about uninitialized values
|
493 |
if(! isset($value[$i])){ $value[$i] = ''; }
|
494 |
}
|
495 |
+
$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values (%s, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
|
496 |
+
$IP_bin,
|
497 |
$value[3], //city
|
498 |
$value[2], //region
|
499 |
$value[1], //countryName
|
520 |
}
|
521 |
return $IPLocs;
|
522 |
}
|
523 |
+
|
524 |
+
public static function reverseLookup($IP) {
|
525 |
$db = new wfDB();
|
526 |
global $wpdb;
|
527 |
$reverseTable = $wpdb->base_prefix . 'wfReverseCache';
|
528 |
+
$IPn = wfUtils::inet_pton($IP);
|
529 |
$host = $db->querySingle("select host from " . $reverseTable . " where IP=%s and unix_timestamp() - lastUpdate < %d", $IPn, WORDFENCE_REVERSE_LOOKUP_CACHE_TIME);
|
530 |
+
if (!$host) {
|
531 |
+
// This function works for IPv4 or IPv6
|
532 |
+
if (function_exists('gethostbyaddr')) {
|
533 |
+
$host = gethostbyaddr($IP);
|
534 |
}
|
535 |
+
if (!$host) {
|
536 |
+
$ptr = false;
|
537 |
+
if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
|
538 |
+
$ptr = implode(".", array_reverse(explode(".", $IP))) . ".in-addr.arpa";
|
539 |
+
} else if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
540 |
+
$ptr = implode(".", array_reverse(str_split(bin2hex($IPn)))) . ".ip6.arpa";
|
541 |
+
}
|
542 |
+
|
543 |
+
if ($ptr && function_exists('dns_get_record')) {
|
544 |
+
$host = @dns_get_record($ptr, DNS_PTR);
|
545 |
+
if ($host) {
|
546 |
+
$host = $host[0]['target'];
|
547 |
+
}
|
548 |
+
}
|
549 |
+
}
|
550 |
+
if (!$host) {
|
551 |
$host = 'NONE';
|
|
|
|
|
552 |
}
|
553 |
$db->queryWrite("insert into " . $reverseTable . " (IP, host, lastUpdate) values (%s, '%s', unix_timestamp()) ON DUPLICATE KEY UPDATE host='%s', lastUpdate=unix_timestamp()", $IPn, $host, $host);
|
554 |
}
|
555 |
+
if ($host == 'NONE') {
|
556 |
return '';
|
557 |
} else {
|
558 |
return $host;
|
576 |
$fh = @fopen($file, 'r');
|
577 |
wfUtils::errorsOn();
|
578 |
if(! $fh){ return false; }
|
579 |
+
$offset = WORDFENCE_MAX_FILE_SIZE_TO_PROCESS + 1;
|
580 |
$tooBig = false;
|
581 |
try {
|
582 |
if(@fseek($fh, $offset, SEEK_SET) === 0){
|
591 |
public static function fileOver2Gigs($file){ //Surround calls to this func with try/catch because fseek may throw error.
|
592 |
$fh = @fopen($file, 'r');
|
593 |
if(! $fh){ return false; }
|
594 |
+
$offset = 2147483647;
|
595 |
$tooBig = false;
|
596 |
//My throw an error so surround calls to this func with try/catch
|
597 |
if(@fseek($fh, $offset, SEEK_SET) === 0){
|
616 |
return $URL;
|
617 |
}
|
618 |
public static function IP2Country($IP){
|
619 |
+
if(! (function_exists('geoip_open') && function_exists('geoip_country_code_by_addr') && function_exists('geoip_country_code_by_addr_v6'))){
|
620 |
require_once('wfGeoIP.php');
|
621 |
}
|
622 |
$gi = geoip_open(dirname(__FILE__) . "/GeoIP.dat",GEOIP_STANDARD);
|
623 |
+
if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
624 |
+
$country = geoip_country_code_by_addr_v6($gi, $IP);
|
625 |
+
} else {
|
626 |
+
$country = geoip_country_code_by_addr($gi, $IP);
|
627 |
+
}
|
628 |
geoip_close($gi);
|
629 |
return $country ? $country : '';
|
630 |
}
|
672 |
public static function isRefererBlocked($refPattern){
|
673 |
return fnmatch($refPattern, $_SERVER['HTTP_REFERER'], FNM_CASEFOLD);
|
674 |
}
|
675 |
+
|
676 |
+
/**
|
677 |
+
* @param $startIP
|
678 |
+
* @param $endIP
|
679 |
+
* @return array
|
680 |
+
*/
|
681 |
public static function rangeToCIDRs($startIP, $endIP){
|
682 |
+
$start_ip_printable = wfUtils::inet_ntop($startIP);
|
683 |
+
if (filter_var($start_ip_printable, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
684 |
+
return self::rangeToCIDRsIPv4(current(unpack('N', substr($startIP, 12, 4))), current(unpack('N', substr($endIP, 12, 4))));
|
685 |
+
}
|
686 |
+
$startIPBin = str_pad(wfHelperBin::bin2str($startIP), 128, '0', STR_PAD_LEFT);
|
687 |
+
$endIPBin = str_pad(wfHelperBin::bin2str($endIP), 128, '0', STR_PAD_LEFT);
|
688 |
+
$IPIncBin = $startIPBin;
|
689 |
+
$CIDRs = array();
|
690 |
+
while (strcmp($IPIncBin, $endIPBin) <= 0) {
|
691 |
+
$longNetwork = 128;
|
692 |
+
$IPNetBin = $IPIncBin;
|
693 |
+
while (($IPIncBin[$longNetwork - 1] == '0') && (strcmp(substr_replace($IPNetBin, '1', $longNetwork - 1, 1), $endIPBin) <= 0)) {
|
694 |
+
$IPNetBin[$longNetwork - 1] = '1';
|
695 |
+
$longNetwork--;
|
696 |
+
}
|
697 |
+
$CIDRs[] = self::inet_ntop(str_pad(wfHelperBin::str2bin($IPIncBin), 16, "\x00", STR_PAD_LEFT)) . ($longNetwork < 128 ? '/' . $longNetwork : '');
|
698 |
+
$IPIncBin = str_pad(wfHelperBin::bin2str(wfHelperBin::addbin2bin(chr(1), wfHelperBin::str2bin($IPNetBin))), 128, '0', STR_PAD_LEFT);
|
699 |
+
}
|
700 |
+
return $CIDRs;
|
701 |
+
}
|