| 1: | <?php
|
| 2: |
|
| 3: | |
| 4: | |
| 5: |
|
| 6: | class Protector
|
| 7: | {
|
| 8: | public $mydirname;
|
| 9: |
|
| 10: | public $_conn;
|
| 11: | public $_conf = array();
|
| 12: | public $_conf_serialized = '';
|
| 13: |
|
| 14: | public $_bad_globals = array();
|
| 15: |
|
| 16: | public $message = '';
|
| 17: | public $warning = false;
|
| 18: | public $error = false;
|
| 19: | public $_doubtful_requests = array();
|
| 20: | public $_bigumbrella_doubtfuls = array();
|
| 21: |
|
| 22: | public $_dblayertrap_doubtfuls = array();
|
| 23: | public $_dblayertrap_doubtful_needles = array(
|
| 24: | 'information_schema',
|
| 25: | 'select',
|
| 26: | "'",
|
| 27: | '"');
|
| 28: |
|
| 29: | public $_logged = false;
|
| 30: |
|
| 31: | public $_done_badext = false;
|
| 32: | public $_done_intval = false;
|
| 33: | public $_done_dotdot = false;
|
| 34: | public $_done_nullbyte = false;
|
| 35: | public $_done_contami = false;
|
| 36: | public $_done_isocom = false;
|
| 37: | public $_done_union = false;
|
| 38: | public $_done_dos = false;
|
| 39: |
|
| 40: | public $_safe_badext = true;
|
| 41: | public $_safe_contami = true;
|
| 42: | public $_safe_isocom = true;
|
| 43: | public $_safe_union = true;
|
| 44: |
|
| 45: | public $_spamcount_uri = 0;
|
| 46: |
|
| 47: | public $_should_be_banned_time0 = false;
|
| 48: | public $_should_be_banned = false;
|
| 49: |
|
| 50: | public $_dos_stage;
|
| 51: |
|
| 52: | public $ip_matched_info;
|
| 53: |
|
| 54: | public $last_error_type = 'UNKNOWN';
|
| 55: |
|
| 56: | |
| 57: | |
| 58: |
|
| 59: | protected function __construct()
|
| 60: | {
|
| 61: | $this->mydirname = 'protector';
|
| 62: |
|
| 63: |
|
| 64: | $this->_conf_serialized = @file_get_contents($this->get_filepath4confighcache());
|
| 65: | $this->_conf = @unserialize($this->_conf_serialized, array('allowed_classes' => false));
|
| 66: | if (empty($this->_conf)) {
|
| 67: | $this->_conf = array();
|
| 68: | }
|
| 69: |
|
| 70: | if (!empty($this->_conf['global_disabled'])) {
|
| 71: | return;
|
| 72: | }
|
| 73: |
|
| 74: |
|
| 75: |
|
| 76: |
|
| 77: |
|
| 78: |
|
| 79: |
|
| 80: |
|
| 81: |
|
| 82: |
|
| 83: |
|
| 84: |
|
| 85: | $this->_bad_globals = array(
|
| 86: | 'GLOBALS',
|
| 87: | '_SESSION',
|
| 88: | 'HTTP_SESSION_VARS',
|
| 89: | '_GET',
|
| 90: | 'HTTP_GET_VARS',
|
| 91: | '_POST',
|
| 92: | 'HTTP_POST_VARS',
|
| 93: | '_COOKIE',
|
| 94: | 'HTTP_COOKIE_VARS',
|
| 95: | '_SERVER',
|
| 96: | 'HTTP_SERVER_VARS',
|
| 97: | '_REQUEST',
|
| 98: | '_ENV',
|
| 99: | '_FILES',
|
| 100: | 'xoopsDB',
|
| 101: | 'xoopsUser',
|
| 102: | 'xoopsUserId',
|
| 103: | 'xoopsUserGroups',
|
| 104: | 'xoopsUserIsAdmin',
|
| 105: | 'xoopsConfig',
|
| 106: | 'xoopsOption',
|
| 107: | 'xoopsModule',
|
| 108: | 'xoopsModuleConfig');
|
| 109: |
|
| 110: | $this->_initial_recursive($_GET, 'G');
|
| 111: | $this->_initial_recursive($_POST, 'P');
|
| 112: | $this->_initial_recursive($_COOKIE, 'C');
|
| 113: | }
|
| 114: |
|
| 115: | |
| 116: | |
| 117: | |
| 118: |
|
| 119: | protected function _initial_recursive($val, $key)
|
| 120: | {
|
| 121: | if (is_array($val)) {
|
| 122: | foreach ($val as $subkey => $subval) {
|
| 123: |
|
| 124: | if (in_array($subkey, $this->_bad_globals, true)) {
|
| 125: | $this->message .= "Attempt to inject '$subkey' was found.\n";
|
| 126: | $this->_safe_contami = false;
|
| 127: | $this->last_error_type = 'CONTAMI';
|
| 128: | }
|
| 129: | $this->_initial_recursive($subval, $key . '_' . base64_encode($subkey));
|
| 130: | }
|
| 131: | } else {
|
| 132: |
|
| 133: | if (isset($this->_conf['san_nullbyte']) && $this->_conf['san_nullbyte'] && false !== strpos($val, chr(0))) {
|
| 134: | $val = str_replace(chr(0), ' ', $val);
|
| 135: | $this->replace_doubtful($key, $val);
|
| 136: | $this->message .= "Injecting Null-byte '$val' found.\n";
|
| 137: | $this->output_log('NullByte', 0, false, 32);
|
| 138: |
|
| 139: | }
|
| 140: |
|
| 141: |
|
| 142: | if (preg_match('?[\s\'"`/]?', $val)) {
|
| 143: | $this->_doubtful_requests["$key"] = $val;
|
| 144: | }
|
| 145: | }
|
| 146: | }
|
| 147: |
|
| 148: | |
| 149: | |
| 150: |
|
| 151: | public static function getInstance()
|
| 152: | {
|
| 153: | static $instance;
|
| 154: | if (!isset($instance)) {
|
| 155: | $instance = new Protector();
|
| 156: | }
|
| 157: |
|
| 158: | return $instance;
|
| 159: | }
|
| 160: |
|
| 161: | |
| 162: | |
| 163: |
|
| 164: | public function updateConfFromDb()
|
| 165: | {
|
| 166: | $constpref = '_MI_' . strtoupper($this->mydirname);
|
| 167: |
|
| 168: | if (empty($this->_conn)) {
|
| 169: | return false;
|
| 170: | }
|
| 171: |
|
| 172: | $result = @mysqli_query($this->_conn, 'SELECT conf_name,conf_value FROM ' . XOOPS_DB_PREFIX . "_config WHERE conf_title like '" . $constpref . "%'");
|
| 173: | if (!$result || mysqli_num_rows($result) < 5) {
|
| 174: | return false;
|
| 175: | }
|
| 176: | $db_conf = array();
|
| 177: | while (list($key, $val) = mysqli_fetch_row($result)) {
|
| 178: | $db_conf[$key] = $val;
|
| 179: | }
|
| 180: | $db_conf_serialized = serialize($db_conf);
|
| 181: |
|
| 182: |
|
| 183: | if ($db_conf_serialized != $this->_conf_serialized) {
|
| 184: | $fp = fopen($this->get_filepath4confighcache(), 'w');
|
| 185: | fwrite($fp, $db_conf_serialized);
|
| 186: | fclose($fp);
|
| 187: | $this->_conf = $db_conf;
|
| 188: | }
|
| 189: |
|
| 190: | return true;
|
| 191: | }
|
| 192: |
|
| 193: | |
| 194: | |
| 195: |
|
| 196: | public function setConn($conn)
|
| 197: | {
|
| 198: | $this->_conn = $conn;
|
| 199: | }
|
| 200: |
|
| 201: | |
| 202: | |
| 203: |
|
| 204: | public function getConf()
|
| 205: | {
|
| 206: | return $this->_conf;
|
| 207: | }
|
| 208: |
|
| 209: | |
| 210: | |
| 211: |
|
| 212: | public function purge($redirect_to_top = false)
|
| 213: | {
|
| 214: | $this->purgeNoExit();
|
| 215: |
|
| 216: | if ($redirect_to_top) {
|
| 217: | header('Location: ' . XOOPS_URL . '/');
|
| 218: | exit;
|
| 219: | } else {
|
| 220: | $ret = $this->call_filter('prepurge_exit');
|
| 221: | if ($ret == false) {
|
| 222: | die('Protector detects attacking actions');
|
| 223: | }
|
| 224: | }
|
| 225: | }
|
| 226: |
|
| 227: | public function purgeSession()
|
| 228: | {
|
| 229: |
|
| 230: | if (isset($_SESSION)) {
|
| 231: | foreach ($_SESSION as $key => $val) {
|
| 232: | $_SESSION[$key] = '';
|
| 233: | if (isset($GLOBALS[$key])) {
|
| 234: | $GLOBALS[$key] = '';
|
| 235: | }
|
| 236: | }
|
| 237: | }
|
| 238: | }
|
| 239: |
|
| 240: | public function purgeCookies()
|
| 241: | {
|
| 242: | if (!headers_sent()) {
|
| 243: | $domain = defined(XOOPS_COOKIE_DOMAIN) ? XOOPS_COOKIE_DOMAIN : '';
|
| 244: | $past = time() - 3600;
|
| 245: | foreach ($_COOKIE as $key => $value) {
|
| 246: | setcookie($key, '', $past, '', $domain);
|
| 247: | setcookie($key, '', $past, '/', $domain);
|
| 248: | }
|
| 249: | }
|
| 250: | }
|
| 251: |
|
| 252: | public function purgeNoExit()
|
| 253: | {
|
| 254: | $this->purgeSession();
|
| 255: | $this->purgeCookies();
|
| 256: | }
|
| 257: |
|
| 258: | public function deactivateCurrentUser()
|
| 259: | {
|
| 260: |
|
| 261: | global $xoopsUser;
|
| 262: |
|
| 263: | if (is_object($xoopsUser)) {
|
| 264: |
|
| 265: | $userHandler = xoops_getHandler('user');
|
| 266: | $xoopsUser->setVar('level', 0);
|
| 267: | $actkey = substr(md5(uniqid(mt_rand(), 1)), 0, 8);
|
| 268: | $xoopsUser->setVar('actkey', $actkey);
|
| 269: | $userHandler->insert($xoopsUser);
|
| 270: | }
|
| 271: | $this->purgeNoExit();
|
| 272: | }
|
| 273: |
|
| 274: | |
| 275: | |
| 276: | |
| 277: | |
| 278: | |
| 279: | |
| 280: | |
| 281: |
|
| 282: | public function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1)
|
| 283: | {
|
| 284: | if ($this->_logged) {
|
| 285: | return true;
|
| 286: | }
|
| 287: |
|
| 288: | if (!($this->_conf['log_level'] & $level)) {
|
| 289: | return true;
|
| 290: | }
|
| 291: |
|
| 292: | if (empty($this->_conn)) {
|
| 293: | mysqli_report(MYSQLI_REPORT_OFF);
|
| 294: | $this->_conn = new mysqli(XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS);
|
| 295: | if (0 !== $this->_conn->connect_errno) {
|
| 296: | die('db connection failed.');
|
| 297: | }
|
| 298: | if (!mysqli_select_db($this->_conn, XOOPS_DB_NAME)) {
|
| 299: | die('db selection failed.');
|
| 300: | }
|
| 301: | }
|
| 302: |
|
| 303: | $ip = \Xmf\IPAddress::fromRequest()->asReadable();
|
| 304: | $agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
| 305: |
|
| 306: |
|
| 307: | if ($unique_check) {
|
| 308: | $result = mysqli_query($this->_conn, 'SELECT ip,type FROM ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1');
|
| 309: | list($last_ip, $last_type) = mysqli_fetch_row($result);
|
| 310: | if ($last_ip == $ip && $last_type == $type) {
|
| 311: | $this->_logged = true;
|
| 312: |
|
| 313: | return true;
|
| 314: | }
|
| 315: | }
|
| 316: |
|
| 317: | mysqli_query(
|
| 318: | $this->_conn,
|
| 319: | 'INSERT INTO ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . "_log SET ip='"
|
| 320: | . mysqli_real_escape_string($this->_conn, $ip) . "',agent='"
|
| 321: | . mysqli_real_escape_string($this->_conn, $agent) . "',type='"
|
| 322: | . mysqli_real_escape_string($this->_conn, $type) . "',description='"
|
| 323: | . mysqli_real_escape_string($this->_conn, $this->message) . "',uid='"
|
| 324: | . (int)$uid . "',timestamp=NOW()"
|
| 325: | );
|
| 326: | $this->_logged = true;
|
| 327: |
|
| 328: | return true;
|
| 329: | }
|
| 330: |
|
| 331: | |
| 332: | |
| 333: | |
| 334: | |
| 335: |
|
| 336: | public function write_file_bwlimit($expire)
|
| 337: | {
|
| 338: | $expire = min((int)$expire, time() + 300);
|
| 339: |
|
| 340: | $fp = @fopen($this->get_filepath4bwlimit(), 'w');
|
| 341: | if ($fp) {
|
| 342: | @flock($fp, LOCK_EX);
|
| 343: | fwrite($fp, $expire . "\n");
|
| 344: | @flock($fp, LOCK_UN);
|
| 345: | fclose($fp);
|
| 346: |
|
| 347: | return true;
|
| 348: | } else {
|
| 349: | return false;
|
| 350: | }
|
| 351: | }
|
| 352: |
|
| 353: | |
| 354: | |
| 355: |
|
| 356: | public function get_bwlimit()
|
| 357: | {
|
| 358: | list($expire) = @file(Protector::get_filepath4bwlimit());
|
| 359: | $expire = min((int)$expire, time() + 300);
|
| 360: |
|
| 361: | return $expire;
|
| 362: | }
|
| 363: |
|
| 364: | |
| 365: | |
| 366: |
|
| 367: | public static function get_filepath4bwlimit()
|
| 368: | {
|
| 369: | return XOOPS_VAR_PATH . '/protector/bwlimit' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
|
| 370: | }
|
| 371: |
|
| 372: | |
| 373: | |
| 374: | |
| 375: | |
| 376: |
|
| 377: | public function write_file_badips($bad_ips)
|
| 378: | {
|
| 379: | asort($bad_ips);
|
| 380: |
|
| 381: | $fp = @fopen($this->get_filepath4badips(), 'w');
|
| 382: | if ($fp) {
|
| 383: | @flock($fp, LOCK_EX);
|
| 384: | fwrite($fp, serialize($bad_ips) . "\n");
|
| 385: | @flock($fp, LOCK_UN);
|
| 386: | fclose($fp);
|
| 387: |
|
| 388: | return true;
|
| 389: | } else {
|
| 390: | return false;
|
| 391: | }
|
| 392: | }
|
| 393: |
|
| 394: | |
| 395: | |
| 396: | |
| 397: | |
| 398: | |
| 399: |
|
| 400: | public function register_bad_ips($jailed_time = 0, $ip = null)
|
| 401: | {
|
| 402: | if (empty($ip)) {
|
| 403: | $ip = \Xmf\IPAddress::fromRequest()->asReadable();
|
| 404: | }
|
| 405: | if (empty($ip)) {
|
| 406: | return false;
|
| 407: | }
|
| 408: |
|
| 409: | $bad_ips = $this->get_bad_ips(true);
|
| 410: | $bad_ips[$ip] = $jailed_time ?: 0x7fffffff;
|
| 411: |
|
| 412: | return $this->write_file_badips($bad_ips);
|
| 413: | }
|
| 414: |
|
| 415: | |
| 416: | |
| 417: | |
| 418: | |
| 419: |
|
| 420: | public function get_bad_ips($with_jailed_time = false)
|
| 421: | {
|
| 422: |
|
| 423: | $filepath4badips = @file(Protector::get_filepath4badips());
|
| 424: |
|
| 425: | if (is_array($filepath4badips) && isset($filepath4badips[0])) {
|
| 426: | list($bad_ips_serialized) = $filepath4badips;
|
| 427: | }
|
| 428: | $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized, array('allowed_classes' => false));
|
| 429: | if (!is_array($bad_ips) || isset($bad_ips[0])) {
|
| 430: | $bad_ips = array();
|
| 431: | }
|
| 432: |
|
| 433: |
|
| 434: | $pos = 0;
|
| 435: | foreach ($bad_ips as $bad_ip => $jailed_time) {
|
| 436: | if ($jailed_time >= time()) {
|
| 437: | break;
|
| 438: | }
|
| 439: | ++$pos;
|
| 440: | }
|
| 441: | $bad_ips = array_slice($bad_ips, $pos);
|
| 442: |
|
| 443: | if ($with_jailed_time) {
|
| 444: | return $bad_ips;
|
| 445: | } else {
|
| 446: | return array_keys($bad_ips);
|
| 447: | }
|
| 448: | }
|
| 449: |
|
| 450: | |
| 451: | |
| 452: |
|
| 453: | public static function get_filepath4badips()
|
| 454: | {
|
| 455: | return XOOPS_VAR_PATH . '/protector/badips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
|
| 456: | }
|
| 457: |
|
| 458: | |
| 459: | |
| 460: | |
| 461: | |
| 462: |
|
| 463: | public function get_group1_ips($with_info = false)
|
| 464: | {
|
| 465: |
|
| 466: | $group1_ips = [];
|
| 467: |
|
| 468: | $filepath = Protector::get_filepath4group1ips();
|
| 469: | if (file_exists($filepath)) {
|
| 470: | $filepath4group1ips = file($filepath);
|
| 471: | if ($filepath4group1ips === false) {
|
| 472: |
|
| 473: | } else {
|
| 474: |
|
| 475: | if (is_array($filepath4group1ips) && isset($filepath4group1ips[0])) {
|
| 476: | list($group1_ips_serialized) = $filepath4group1ips;
|
| 477: | }
|
| 478: |
|
| 479: | $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized, array('allowed_classes' => false));
|
| 480: | if (!is_array($group1_ips)) {
|
| 481: | $group1_ips = array();
|
| 482: | }
|
| 483: |
|
| 484: | if ($with_info) {
|
| 485: | $group1_ips = array_flip($group1_ips);
|
| 486: | }
|
| 487: | }
|
| 488: | } else {
|
| 489: |
|
| 490: | }
|
| 491: |
|
| 492: | return $group1_ips;
|
| 493: | }
|
| 494: |
|
| 495: | |
| 496: | |
| 497: |
|
| 498: | public static function get_filepath4group1ips()
|
| 499: | {
|
| 500: | return XOOPS_VAR_PATH . '/protector/group1ips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
|
| 501: | }
|
| 502: |
|
| 503: | |
| 504: | |
| 505: |
|
| 506: | public function get_filepath4confighcache()
|
| 507: | {
|
| 508: | return XOOPS_VAR_PATH . '/protector/configcache' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
|
| 509: | }
|
| 510: |
|
| 511: | |
| 512: | |
| 513: | |
| 514: | |
| 515: |
|
| 516: | public function ip_match($ips)
|
| 517: | {
|
| 518: | $requestIp = \Xmf\IPAddress::fromRequest()->asReadable();
|
| 519: | if (false === $requestIp) {
|
| 520: | $this->ip_matched_info = null;
|
| 521: | return false;
|
| 522: | }
|
| 523: | foreach ($ips as $ip => $info) {
|
| 524: | if ($ip) {
|
| 525: | switch (strtolower(substr($ip, -1))) {
|
| 526: | case '.' :
|
| 527: | case ':' :
|
| 528: |
|
| 529: | if (substr($requestIp, 0, strlen($ip)) == $ip) {
|
| 530: | $this->ip_matched_info = $info;
|
| 531: | return true;
|
| 532: | }
|
| 533: | break;
|
| 534: | case '0' :
|
| 535: | case '1' :
|
| 536: | case '2' :
|
| 537: | case '3' :
|
| 538: | case '4' :
|
| 539: | case '5' :
|
| 540: | case '6' :
|
| 541: | case '7' :
|
| 542: | case '8' :
|
| 543: | case '9' :
|
| 544: | case 'a' :
|
| 545: | case 'b' :
|
| 546: | case 'c' :
|
| 547: | case 'd' :
|
| 548: | case 'e' :
|
| 549: | case 'f' :
|
| 550: |
|
| 551: | if ($requestIp == $ip) {
|
| 552: | $this->ip_matched_info = $info;
|
| 553: | return true;
|
| 554: | }
|
| 555: | break;
|
| 556: | default :
|
| 557: |
|
| 558: | if (@preg_match($ip, $requestIp)) {
|
| 559: | $this->ip_matched_info = $info;
|
| 560: | return true;
|
| 561: | }
|
| 562: | break;
|
| 563: | }
|
| 564: | }
|
| 565: | }
|
| 566: | $this->ip_matched_info = null;
|
| 567: | return false;
|
| 568: | }
|
| 569: |
|
| 570: | |
| 571: | |
| 572: | |
| 573: | |
| 574: |
|
| 575: | public function deny_by_htaccess($ip = null)
|
| 576: | {
|
| 577: | if (empty($ip)) {
|
| 578: | $ip = \Xmf\IPAddress::fromRequest()->asReadable();
|
| 579: | }
|
| 580: | if (empty($ip)) {
|
| 581: | return false;
|
| 582: | }
|
| 583: | if (!function_exists('file_get_contents')) {
|
| 584: | return false;
|
| 585: | }
|
| 586: |
|
| 587: | $target_htaccess = XOOPS_ROOT_PATH . '/.htaccess';
|
| 588: | $backup_htaccess = XOOPS_ROOT_PATH . '/uploads/.htaccess.bak';
|
| 589: |
|
| 590: | $ht_body = file_get_contents($target_htaccess);
|
| 591: |
|
| 592: |
|
| 593: | if ($ht_body && !file_exists($backup_htaccess)) {
|
| 594: | $fw = fopen($backup_htaccess, 'w');
|
| 595: | fwrite($fw, $ht_body);
|
| 596: | fclose($fw);
|
| 597: | }
|
| 598: |
|
| 599: |
|
| 600: | if (!$ht_body && file_exists($backup_htaccess)) {
|
| 601: | $ht_body = file_get_contents($backup_htaccess);
|
| 602: | }
|
| 603: |
|
| 604: |
|
| 605: | if ($ht_body === false) {
|
| 606: | $ht_body = '';
|
| 607: | }
|
| 608: |
|
| 609: | if (preg_match("/^(.*)#PROTECTOR#\s+(DENY FROM .*)\n#PROTECTOR#\n(.*)$/si", $ht_body, $regs)) {
|
| 610: | if (substr($regs[2], -strlen($ip)) == $ip) {
|
| 611: | return true;
|
| 612: | }
|
| 613: | $new_ht_body = $regs[1] . "#PROTECTOR#\n" . $regs[2] . " $ip\n#PROTECTOR#\n" . $regs[3];
|
| 614: | } else {
|
| 615: | $new_ht_body = "#PROTECTOR#\nDENY FROM $ip\n#PROTECTOR#\n" . $ht_body;
|
| 616: | }
|
| 617: |
|
| 618: |
|
| 619: |
|
| 620: | $fw = fopen($target_htaccess, 'w');
|
| 621: | @flock($fw, LOCK_EX);
|
| 622: | fwrite($fw, $new_ht_body);
|
| 623: | @flock($fw, LOCK_UN);
|
| 624: | fclose($fw);
|
| 625: |
|
| 626: | return true;
|
| 627: | }
|
| 628: |
|
| 629: | |
| 630: | |
| 631: |
|
| 632: | public function getDblayertrapDoubtfuls()
|
| 633: | {
|
| 634: | return $this->_dblayertrap_doubtfuls;
|
| 635: | }
|
| 636: |
|
| 637: | |
| 638: | |
| 639: | |
| 640: |
|
| 641: | protected function _dblayertrap_check_recursive($val)
|
| 642: | {
|
| 643: | if (is_array($val)) {
|
| 644: | foreach ($val as $subval) {
|
| 645: | $this->_dblayertrap_check_recursive($subval);
|
| 646: | }
|
| 647: | } else {
|
| 648: | if (strlen($val) < 6) {
|
| 649: | return null;
|
| 650: | }
|
| 651: | $val = @get_magic_quotes_gpc() ? stripslashes($val) : $val;
|
| 652: | foreach ($this->_dblayertrap_doubtful_needles as $needle) {
|
| 653: | if (false !== stripos($val, $needle)) {
|
| 654: | $this->_dblayertrap_doubtfuls[] = $val;
|
| 655: | }
|
| 656: | }
|
| 657: | }
|
| 658: | }
|
| 659: |
|
| 660: | |
| 661: | |
| 662: | |
| 663: |
|
| 664: | public function dblayertrap_init($force_override = false)
|
| 665: | {
|
| 666: | if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) {
|
| 667: | return null;
|
| 668: | }
|
| 669: |
|
| 670: | $this->_dblayertrap_doubtfuls = array();
|
| 671: | $this->_dblayertrap_check_recursive($_GET);
|
| 672: | $this->_dblayertrap_check_recursive($_POST);
|
| 673: | $this->_dblayertrap_check_recursive($_COOKIE);
|
| 674: | if (empty($this->_conf['dblayertrap_wo_server'])) {
|
| 675: | $this->_dblayertrap_check_recursive($_SERVER);
|
| 676: | }
|
| 677: |
|
| 678: | if (!empty($this->_dblayertrap_doubtfuls) || $force_override) {
|
| 679: | @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');
|
| 680: | require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php';
|
| 681: | }
|
| 682: | }
|
| 683: |
|
| 684: | |
| 685: | |
| 686: |
|
| 687: | protected function _bigumbrella_check_recursive($val)
|
| 688: | {
|
| 689: | if (is_array($val)) {
|
| 690: | foreach ($val as $subval) {
|
| 691: | $this->_bigumbrella_check_recursive($subval);
|
| 692: | }
|
| 693: | } else {
|
| 694: | if (preg_match('/[<\'"].{15}/s', $val, $regs)) {
|
| 695: | $this->_bigumbrella_doubtfuls[] = $regs[0];
|
| 696: | }
|
| 697: | }
|
| 698: | }
|
| 699: |
|
| 700: | public function bigumbrella_init()
|
| 701: | {
|
| 702: | $this->_bigumbrella_doubtfuls = array();
|
| 703: | $this->_bigumbrella_check_recursive($_GET);
|
| 704: | $this->_bigumbrella_check_recursive(isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '');
|
| 705: |
|
| 706: |
|
| 707: | if (!empty($this->_bigumbrella_doubtfuls)) {
|
| 708: | ob_start(array($this, 'bigumbrella_outputcheck'));
|
| 709: | }
|
| 710: | }
|
| 711: |
|
| 712: | |
| 713: | |
| 714: | |
| 715: | |
| 716: |
|
| 717: | public function bigumbrella_outputcheck($s)
|
| 718: | {
|
| 719: | if (defined('BIGUMBRELLA_DISABLED')) {
|
| 720: | return $s;
|
| 721: | }
|
| 722: |
|
| 723: | if (function_exists('headers_list')) {
|
| 724: | foreach (headers_list() as $header) {
|
| 725: | if (false !== stripos($header, 'Content-Type:') && false === stripos($header, 'text/html')) {
|
| 726: | return $s;
|
| 727: | }
|
| 728: | }
|
| 729: | }
|
| 730: |
|
| 731: | if (!is_array($this->_bigumbrella_doubtfuls)) {
|
| 732: | return 'bigumbrella injection found.';
|
| 733: | }
|
| 734: |
|
| 735: | foreach ($this->_bigumbrella_doubtfuls as $doubtful) {
|
| 736: | if (false !== strpos($s, $doubtful)) {
|
| 737: | return 'XSS found by Protector.';
|
| 738: | }
|
| 739: | }
|
| 740: |
|
| 741: | return $s;
|
| 742: | }
|
| 743: |
|
| 744: | |
| 745: | |
| 746: |
|
| 747: | public function intval_allrequestsendid()
|
| 748: | {
|
| 749: | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
|
| 750: |
|
| 751: | if ($this->_done_intval) {
|
| 752: | return true;
|
| 753: | } else {
|
| 754: | $this->_done_intval = true;
|
| 755: | }
|
| 756: |
|
| 757: | foreach ($_GET as $key => $val) {
|
| 758: | if (substr($key, -2) === 'id' && !is_array($_GET[$key])) {
|
| 759: | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
|
| 760: | $_GET[$key] = $HTTP_GET_VARS[$key] = $newval;
|
| 761: | if ($_REQUEST[$key] == $_GET[$key]) {
|
| 762: | $_REQUEST[$key] = $newval;
|
| 763: | }
|
| 764: | }
|
| 765: | }
|
| 766: | foreach ($_POST as $key => $val) {
|
| 767: | if (substr($key, -2) === 'id' && !is_array($_POST[$key])) {
|
| 768: | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
|
| 769: | $_POST[$key] = $HTTP_POST_VARS[$key] = $newval;
|
| 770: | if ($_REQUEST[$key] == $_POST[$key]) {
|
| 771: | $_REQUEST[$key] = $newval;
|
| 772: | }
|
| 773: | }
|
| 774: | }
|
| 775: | foreach ($_COOKIE as $key => $val) {
|
| 776: | if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) {
|
| 777: | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
|
| 778: | $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval;
|
| 779: | if ($_REQUEST[$key] == $_COOKIE[$key]) {
|
| 780: | $_REQUEST[$key] = $newval;
|
| 781: | }
|
| 782: | }
|
| 783: | }
|
| 784: |
|
| 785: | return true;
|
| 786: | }
|
| 787: |
|
| 788: | |
| 789: | |
| 790: |
|
| 791: | public function eliminate_dotdot()
|
| 792: | {
|
| 793: | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
|
| 794: |
|
| 795: | if ($this->_done_dotdot) {
|
| 796: | return true;
|
| 797: | } else {
|
| 798: | $this->_done_dotdot = true;
|
| 799: | }
|
| 800: |
|
| 801: | foreach ($_GET as $key => $val) {
|
| 802: | if (is_array($_GET[$key])) {
|
| 803: | continue;
|
| 804: | }
|
| 805: | if (substr(trim($val), 0, 3) === '../' || false !== strpos($val, '/../')) {
|
| 806: | $this->last_error_type = 'DirTraversal';
|
| 807: | $this->message .= "Directory Traversal '$val' found.\n";
|
| 808: | $this->output_log($this->last_error_type, 0, false, 64);
|
| 809: | $sanitized_val = str_replace(chr(0), '', $val);
|
| 810: | if (substr($sanitized_val, -2) !== ' .') {
|
| 811: | $sanitized_val .= ' .';
|
| 812: | }
|
| 813: | $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val;
|
| 814: | if ($_REQUEST[$key] == $_GET[$key]) {
|
| 815: | $_REQUEST[$key] = $sanitized_val;
|
| 816: | }
|
| 817: | }
|
| 818: | }
|
| 819: |
|
| 820: | |
| 821: | |
| 822: | |
| 823: | |
| 824: | |
| 825: | |
| 826: | |
| 827: | |
| 828: | |
| 829: | |
| 830: | |
| 831: | |
| 832: | |
| 833: | |
| 834: | |
| 835: | |
| 836: | |
| 837: | |
| 838: | |
| 839: | |
| 840: | |
| 841: | |
| 842: | |
| 843: | |
| 844: | |
| 845: | |
| 846: | |
| 847: |
|
| 848: |
|
| 849: | return true;
|
| 850: | }
|
| 851: |
|
| 852: | |
| 853: | |
| 854: | |
| 855: | |
| 856: | |
| 857: |
|
| 858: | public function &get_ref_from_base64index(&$current, $indexes)
|
| 859: | {
|
| 860: | foreach ($indexes as $index) {
|
| 861: | $index = base64_decode($index);
|
| 862: | if (!is_array($current)) {
|
| 863: | return false;
|
| 864: | }
|
| 865: | $current =& $current[$index];
|
| 866: | }
|
| 867: |
|
| 868: | return $current;
|
| 869: | }
|
| 870: |
|
| 871: | |
| 872: | |
| 873: | |
| 874: |
|
| 875: | public function replace_doubtful($key, $val)
|
| 876: | {
|
| 877: | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
|
| 878: |
|
| 879: | $index_expression = '';
|
| 880: | $indexes = explode('_', $key);
|
| 881: | $base_array = array_shift($indexes);
|
| 882: |
|
| 883: | switch ($base_array) {
|
| 884: | case 'G' :
|
| 885: | $main_ref =& $this->get_ref_from_base64index($_GET, $indexes);
|
| 886: | $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes);
|
| 887: | break;
|
| 888: | case 'P' :
|
| 889: | $main_ref =& $this->get_ref_from_base64index($_POST, $indexes);
|
| 890: | $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes);
|
| 891: | break;
|
| 892: | case 'C' :
|
| 893: | $main_ref =& $this->get_ref_from_base64index($_COOKIE, $indexes);
|
| 894: | $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes);
|
| 895: | break;
|
| 896: | default :
|
| 897: | exit;
|
| 898: | }
|
| 899: | if (!isset($main_ref)) {
|
| 900: | exit;
|
| 901: | }
|
| 902: | $request_ref =& $this->get_ref_from_base64index($_REQUEST, $indexes);
|
| 903: | if ($request_ref !== false && $main_ref == $request_ref) {
|
| 904: | $request_ref = $val;
|
| 905: | }
|
| 906: | $main_ref = $val;
|
| 907: | $legacy_ref = $val;
|
| 908: | }
|
| 909: |
|
| 910: | |
| 911: | |
| 912: |
|
| 913: | public function check_uploaded_files()
|
| 914: | {
|
| 915: | if ($this->_done_badext) {
|
| 916: | return $this->_safe_badext;
|
| 917: | } else {
|
| 918: | $this->_done_badext = true;
|
| 919: | }
|
| 920: |
|
| 921: |
|
| 922: | $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp');
|
| 923: |
|
| 924: | $image_extensions = array(
|
| 925: | 1 => 'gif',
|
| 926: | 2 => 'jpg',
|
| 927: | 3 => 'png',
|
| 928: | 4 => 'swf',
|
| 929: | 5 => 'psd',
|
| 930: | 6 => 'bmp',
|
| 931: | 7 => 'tif',
|
| 932: | 8 => 'tif',
|
| 933: | 9 => 'jpc',
|
| 934: | 10 => 'jp2',
|
| 935: | 11 => 'jpx',
|
| 936: | 12 => 'jb2',
|
| 937: | 13 => 'swc',
|
| 938: | 14 => 'iff',
|
| 939: | 15 => 'wbmp',
|
| 940: | 16 => 'xbm');
|
| 941: |
|
| 942: | foreach ($_FILES as $_file) {
|
| 943: | if (!empty($_file['error'])) {
|
| 944: | continue;
|
| 945: | }
|
| 946: | if (!empty($_file['name']) && is_string($_file['name'])) {
|
| 947: | $ext = strtolower(substr(strrchr($_file['name'], '.'), 1));
|
| 948: | if ($ext === 'jpeg') {
|
| 949: | $ext = 'jpg';
|
| 950: | } elseif ($ext === 'tiff') {
|
| 951: | $ext = 'tif';
|
| 952: | }
|
| 953: |
|
| 954: |
|
| 955: | if (count(explode('.', str_replace('.tar.gz', '.tgz', $_file['name']))) > 2) {
|
| 956: | $this->message .= "Attempt to multiple dot file {$_file['name']}.\n";
|
| 957: | $this->_safe_badext = false;
|
| 958: | $this->last_error_type = 'UPLOAD';
|
| 959: | }
|
| 960: |
|
| 961: |
|
| 962: | if (in_array($ext, $bad_extensions)) {
|
| 963: | $this->message .= "Attempt to upload {$_file['name']}.\n";
|
| 964: | $this->_safe_badext = false;
|
| 965: | $this->last_error_type = 'UPLOAD';
|
| 966: | }
|
| 967: |
|
| 968: |
|
| 969: | if (in_array($ext, $image_extensions)) {
|
| 970: | $image_attributes = @getimagesize($_file['tmp_name']);
|
| 971: | if ($image_attributes === false && is_uploaded_file($_file['tmp_name'])) {
|
| 972: |
|
| 973: | $temp_file = XOOPS_ROOT_PATH . '/uploads/protector_upload_temporary' . md5(time());
|
| 974: | move_uploaded_file($_file['tmp_name'], $temp_file);
|
| 975: | $image_attributes = @getimagesize($temp_file);
|
| 976: | @unlink($temp_file);
|
| 977: | }
|
| 978: |
|
| 979: | if ($image_attributes === false || $image_extensions[(int)$image_attributes[2]] != $ext) {
|
| 980: | $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n";
|
| 981: | $this->_safe_badext = false;
|
| 982: | $this->last_error_type = 'UPLOAD';
|
| 983: | }
|
| 984: | }
|
| 985: | }
|
| 986: | }
|
| 987: |
|
| 988: | return $this->_safe_badext;
|
| 989: | }
|
| 990: |
|
| 991: | |
| 992: | |
| 993: |
|
| 994: | public function check_contami_systemglobals()
|
| 995: | {
|
| 996: | |
| 997: |
|
| 998: |
|
| 999: | |
| 1000: | |
| 1001: | |
| 1002: | |
| 1003: | |
| 1004: | |
| 1005: |
|
| 1006: |
|
| 1007: | return $this->_safe_contami;
|
| 1008: | }
|
| 1009: |
|
| 1010: | |
| 1011: | |
| 1012: | |
| 1013: | |
| 1014: |
|
| 1015: | public function check_sql_isolatedcommentin($sanitize = true)
|
| 1016: | {
|
| 1017: | if ($this->_done_isocom) {
|
| 1018: | return $this->_safe_isocom;
|
| 1019: | } else {
|
| 1020: | $this->_done_isocom = true;
|
| 1021: | }
|
| 1022: |
|
| 1023: | foreach ($this->_doubtful_requests as $key => $val) {
|
| 1024: | $str = $val;
|
| 1025: | while ($str = strstr($str, '/*')) {
|
| 1026: | $str = strstr(substr($str, 2), '*/');
|
| 1027: | if ($str === false) {
|
| 1028: | $this->message .= "Isolated comment-in found. ($val)\n";
|
| 1029: | if ($sanitize) {
|
| 1030: | $this->replace_doubtful($key, $val . '*/');
|
| 1031: | }
|
| 1032: | $this->_safe_isocom = false;
|
| 1033: | $this->last_error_type = 'ISOCOM';
|
| 1034: | }
|
| 1035: | }
|
| 1036: | }
|
| 1037: |
|
| 1038: | return $this->_safe_isocom;
|
| 1039: | }
|
| 1040: |
|
| 1041: | |
| 1042: | |
| 1043: | |
| 1044: | |
| 1045: |
|
| 1046: | public function check_sql_union($sanitize = true)
|
| 1047: | {
|
| 1048: | if ($this->_done_union) {
|
| 1049: | return $this->_safe_union;
|
| 1050: | } else {
|
| 1051: | $this->_done_union = true;
|
| 1052: | }
|
| 1053: |
|
| 1054: | foreach ($this->_doubtful_requests as $key => $val) {
|
| 1055: | $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val));
|
| 1056: | if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) {
|
| 1057: | $this->message .= "Pattern like SQL injection found. ($val)\n";
|
| 1058: | if ($sanitize) {
|
| 1059: |
|
| 1060: | $this->replace_doubtful($key, str_ireplace('union', 'uni-on', $val));
|
| 1061: | }
|
| 1062: | $this->_safe_union = false;
|
| 1063: | $this->last_error_type = 'UNION';
|
| 1064: | }
|
| 1065: | }
|
| 1066: |
|
| 1067: | return $this->_safe_union;
|
| 1068: | }
|
| 1069: |
|
| 1070: | |
| 1071: | |
| 1072: | |
| 1073: | |
| 1074: |
|
| 1075: | public function stopforumspam($uid)
|
| 1076: | {
|
| 1077: | if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
| 1078: | return false;
|
| 1079: | }
|
| 1080: |
|
| 1081: | $result = $this->stopForumSpamLookup(
|
| 1082: | isset($_POST['email']) ? $_POST['email'] : null,
|
| 1083: | $_SERVER['REMOTE_ADDR'],
|
| 1084: | isset($_POST['uname']) ? $_POST['uname'] : null
|
| 1085: | );
|
| 1086: |
|
| 1087: | if (false === $result || isset($result['http_code'])) {
|
| 1088: | return false;
|
| 1089: | }
|
| 1090: |
|
| 1091: | $spammer = false;
|
| 1092: | if (isset($result['email']) && isset($result['email']['lastseen'])) {
|
| 1093: | $spammer = true;
|
| 1094: | }
|
| 1095: |
|
| 1096: | if (isset($result['ip']) && isset($result['ip']['lastseen'])) {
|
| 1097: | $last = strtotime($result['ip']['lastseen']);
|
| 1098: | $oneMonth = 60 * 60 * 24 * 31;
|
| 1099: | $oneMonthAgo = time() - $oneMonth;
|
| 1100: | if ($last > $oneMonthAgo) {
|
| 1101: | $spammer = true;
|
| 1102: | }
|
| 1103: | }
|
| 1104: |
|
| 1105: | if (!$spammer) {
|
| 1106: | return false;
|
| 1107: | }
|
| 1108: |
|
| 1109: | $this->last_error_type = 'SPAMMER POST';
|
| 1110: |
|
| 1111: | switch ($this->_conf['stopforumspam_action']) {
|
| 1112: | default :
|
| 1113: | case 'log' :
|
| 1114: | break;
|
| 1115: | case 'san' :
|
| 1116: | $_POST = array();
|
| 1117: | $this->message .= 'POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
|
| 1118: | break;
|
| 1119: | case 'biptime0' :
|
| 1120: | $_POST = array();
|
| 1121: | $this->message .= 'BAN and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
|
| 1122: | $this->_should_be_banned_time0 = true;
|
| 1123: | break;
|
| 1124: | case 'bip' :
|
| 1125: | $_POST = array();
|
| 1126: | $this->message .= 'Ban and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
|
| 1127: | $this->_should_be_banned = true;
|
| 1128: | break;
|
| 1129: | }
|
| 1130: |
|
| 1131: | $this->output_log($this->last_error_type, $uid, false, 16);
|
| 1132: |
|
| 1133: | return true;
|
| 1134: | }
|
| 1135: |
|
| 1136: | public function stopForumSpamLookup($email, $ip, $username)
|
| 1137: | {
|
| 1138: | if (!function_exists('curl_init')) {
|
| 1139: | return false;
|
| 1140: | }
|
| 1141: |
|
| 1142: | $query = '';
|
| 1143: | $query .= (empty($ip)) ? '' : '&ip=' . $ip;
|
| 1144: | $query .= (empty($email)) ? '' : '&email=' . $email;
|
| 1145: | $query .= (empty($username)) ? '' : '&username=' . $username;
|
| 1146: |
|
| 1147: | if (empty($query)) {
|
| 1148: | return false;
|
| 1149: | }
|
| 1150: |
|
| 1151: | $url = 'http://www.stopforumspam.com/api?f=json' . $query;
|
| 1152: | $ch = curl_init();
|
| 1153: | curl_setopt($ch, CURLOPT_URL, $url);
|
| 1154: | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
| 1155: | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
| 1156: | $result = curl_exec($ch);
|
| 1157: | if (false === $result) {
|
| 1158: | $result = curl_getinfo($ch);
|
| 1159: | } else {
|
| 1160: | $result = json_decode(curl_exec($ch), true);
|
| 1161: | }
|
| 1162: | curl_close($ch);
|
| 1163: |
|
| 1164: | return $result;
|
| 1165: | }
|
| 1166: |
|
| 1167: | |
| 1168: | |
| 1169: | |
| 1170: | |
| 1171: | |
| 1172: |
|
| 1173: | public function check_dos_attack($uid = 0, $can_ban = false)
|
| 1174: | {
|
| 1175: | global $xoopsDB;
|
| 1176: |
|
| 1177: | if ($this->_done_dos) {
|
| 1178: | return true;
|
| 1179: | }
|
| 1180: |
|
| 1181: | $ip = \Xmf\IPAddress::fromRequest();
|
| 1182: | if (false === $ip->asReadable()) {
|
| 1183: | return true;
|
| 1184: | }
|
| 1185: | $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
| 1186: |
|
| 1187: | $ip4sql = $xoopsDB->quote($ip->asReadable());
|
| 1188: | $uri4sql = $xoopsDB->quote($uri);
|
| 1189: |
|
| 1190: |
|
| 1191: | $result = $xoopsDB->queryF(
|
| 1192: | 'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
|
| 1193: | . ' WHERE expire < UNIX_TIMESTAMP()'
|
| 1194: | );
|
| 1195: |
|
| 1196: |
|
| 1197: | if ($result === false) {
|
| 1198: | $this->_done_dos = true;
|
| 1199: |
|
| 1200: | return true;
|
| 1201: | }
|
| 1202: |
|
| 1203: |
|
| 1204: | $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
|
| 1205: | . " SET ip={$ip4sql}, request_uri={$uri4sql},"
|
| 1206: | . " expire=UNIX_TIMESTAMP()+'" . (int)$this->_conf['dos_expire'] . "'";
|
| 1207: |
|
| 1208: |
|
| 1209: | if (isset($this->_conf['bwlimit_count']) && $this->_conf['bwlimit_count'] >= 10) {
|
| 1210: | $sql = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access');
|
| 1211: | $result = $xoopsDB->query($sql);
|
| 1212: | if ($xoopsDB->isResultSet($result)) {
|
| 1213: | list($bw_count) = $xoopsDB->fetchRow($result);
|
| 1214: | if ($bw_count > $this->_conf['bwlimit_count']) {
|
| 1215: | $this->write_file_bwlimit(time() + $this->_conf['dos_expire']);
|
| 1216: | }
|
| 1217: | }
|
| 1218: | }
|
| 1219: |
|
| 1220: |
|
| 1221: |
|
| 1222: | $sql = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql} AND request_uri={$uri4sql}";
|
| 1223: | $result = $xoopsDB->query($sql);
|
| 1224: | if (!$xoopsDB->isResultSet($result)) {
|
| 1225: | throw new \RuntimeException(
|
| 1226: | \sprintf(_DB_QUERY_ERROR, $sql) . $xoopsDB->error(), E_USER_ERROR
|
| 1227: | );
|
| 1228: | }
|
| 1229: | list($f5_count) = $xoopsDB->fetchRow($result);
|
| 1230: | if ($f5_count > $this->_conf['dos_f5count']) {
|
| 1231: |
|
| 1232: |
|
| 1233: | $xoopsDB->queryF($sql4insertlog);
|
| 1234: |
|
| 1235: |
|
| 1236: |
|
| 1237: |
|
| 1238: |
|
| 1239: | $ret = $this->call_filter('f5attack_overrun');
|
| 1240: |
|
| 1241: |
|
| 1242: | $this->_done_dos = true;
|
| 1243: | $this->last_error_type = 'DoS';
|
| 1244: | switch ($this->_conf['dos_f5action']) {
|
| 1245: | default :
|
| 1246: | case 'exit' :
|
| 1247: | $this->output_log($this->last_error_type, $uid, true, 16);
|
| 1248: | exit;
|
| 1249: | case 'none' :
|
| 1250: | $this->output_log($this->last_error_type, $uid, true, 16);
|
| 1251: |
|
| 1252: | return true;
|
| 1253: | case 'biptime0' :
|
| 1254: | if ($can_ban) {
|
| 1255: | $this->register_bad_ips(time() + $this->_conf['banip_time0']);
|
| 1256: | }
|
| 1257: | break;
|
| 1258: | case 'bip' :
|
| 1259: | if ($can_ban) {
|
| 1260: | $this->register_bad_ips();
|
| 1261: | }
|
| 1262: | break;
|
| 1263: | case 'hta' :
|
| 1264: | if ($can_ban) {
|
| 1265: | $this->deny_by_htaccess();
|
| 1266: | }
|
| 1267: | break;
|
| 1268: | case 'sleep' :
|
| 1269: | sleep(5);
|
| 1270: | break;
|
| 1271: | }
|
| 1272: |
|
| 1273: | return false;
|
| 1274: | }
|
| 1275: |
|
| 1276: |
|
| 1277: | if (trim($this->_conf['dos_crsafe']) != '' && isset($_SERVER['HTTP_USER_AGENT']) && preg_match($this->_conf['dos_crsafe'], $_SERVER['HTTP_USER_AGENT'])) {
|
| 1278: |
|
| 1279: | $this->_done_dos = true;
|
| 1280: |
|
| 1281: | return true;
|
| 1282: | }
|
| 1283: |
|
| 1284: |
|
| 1285: | $sql = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql}";
|
| 1286: | $result = $xoopsDB->query($sql);
|
| 1287: | if (!$xoopsDB->isResultSet($result)) {
|
| 1288: | return false;
|
| 1289: | }
|
| 1290: | list($crawler_count) = $xoopsDB->fetchRow($result);
|
| 1291: |
|
| 1292: |
|
| 1293: | $xoopsDB->queryF($sql4insertlog);
|
| 1294: |
|
| 1295: | if ($crawler_count > $this->_conf['dos_crcount']) {
|
| 1296: |
|
| 1297: |
|
| 1298: | $ret = $this->call_filter('crawler_overrun');
|
| 1299: |
|
| 1300: |
|
| 1301: | $this->_done_dos = true;
|
| 1302: | $this->last_error_type = 'CRAWLER';
|
| 1303: | switch ($this->_conf['dos_craction']) {
|
| 1304: | default :
|
| 1305: | case 'exit' :
|
| 1306: | $this->output_log($this->last_error_type, $uid, true, 16);
|
| 1307: | exit;
|
| 1308: | case 'none' :
|
| 1309: | $this->output_log($this->last_error_type, $uid, true, 16);
|
| 1310: |
|
| 1311: | return true;
|
| 1312: | case 'biptime0' :
|
| 1313: | if ($can_ban) {
|
| 1314: | $this->register_bad_ips(time() + $this->_conf['banip_time0']);
|
| 1315: | }
|
| 1316: | break;
|
| 1317: | case 'bip' :
|
| 1318: | if ($can_ban) {
|
| 1319: | $this->register_bad_ips();
|
| 1320: | }
|
| 1321: | break;
|
| 1322: | case 'hta' :
|
| 1323: | if ($can_ban) {
|
| 1324: | $this->deny_by_htaccess();
|
| 1325: | }
|
| 1326: | break;
|
| 1327: | case 'sleep' :
|
| 1328: | sleep(5);
|
| 1329: | break;
|
| 1330: | }
|
| 1331: |
|
| 1332: | return false;
|
| 1333: | }
|
| 1334: |
|
| 1335: | return true;
|
| 1336: | }
|
| 1337: |
|
| 1338: |
|
| 1339: | |
| 1340: | |
| 1341: |
|
| 1342: | public function check_brute_force()
|
| 1343: | {
|
| 1344: | global $xoopsDB;
|
| 1345: |
|
| 1346: | $ip = \Xmf\IPAddress::fromRequest();
|
| 1347: | if (false === $ip->asReadable()) {
|
| 1348: | return true;
|
| 1349: | }
|
| 1350: | $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
| 1351: | $ip4sql = $xoopsDB->quote($ip->asReadable());
|
| 1352: | $uri4sql = $xoopsDB->quote($uri);
|
| 1353: |
|
| 1354: | $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname'];
|
| 1355: |
|
| 1356: | if ($victim_uname === 'deleted') {
|
| 1357: | return null;
|
| 1358: | }
|
| 1359: | $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname");
|
| 1360: |
|
| 1361: |
|
| 1362: | $result = $xoopsDB->queryF(
|
| 1363: | 'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()'
|
| 1364: | );
|
| 1365: |
|
| 1366: |
|
| 1367: | $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
|
| 1368: | . " SET ip={$ip4sql}, request_uri={$uri4sql}, malicious_actions={$mal4sql}, expire=UNIX_TIMESTAMP()+600";
|
| 1369: |
|
| 1370: |
|
| 1371: | $bf_count = 0;
|
| 1372: | $sql = 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql} AND malicious_actions like 'BRUTE FORCE:%'";
|
| 1373: | $result = $xoopsDB->query($sql);
|
| 1374: | if ($xoopsDB->isResultSet($result)) {
|
| 1375: | list($bf_count) = $xoopsDB->fetchRow($result);
|
| 1376: | } else {
|
| 1377: | throw new \RuntimeException(
|
| 1378: | \sprintf(_DB_QUERY_ERROR, $sql) . $xoopsDB->error(), E_USER_ERROR
|
| 1379: | );
|
| 1380: | }
|
| 1381: | if ($bf_count > $this->_conf['bf_count']) {
|
| 1382: | $this->register_bad_ips(time() + $this->_conf['banip_time0']);
|
| 1383: | $this->last_error_type = 'BruteForce';
|
| 1384: | $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n";
|
| 1385: | $this->output_log('BRUTE FORCE', 0, true, 1);
|
| 1386: | $ret = $this->call_filter('bruteforce_overrun');
|
| 1387: | if ($ret == false) {
|
| 1388: | exit;
|
| 1389: | }
|
| 1390: | }
|
| 1391: |
|
| 1392: | $xoopsDB->queryF($sql4insertlog);
|
| 1393: | return null;
|
| 1394: | }
|
| 1395: |
|
| 1396: | |
| 1397: | |
| 1398: |
|
| 1399: | protected function _spam_check_point_recursive($val)
|
| 1400: | {
|
| 1401: | if (is_array($val)) {
|
| 1402: | foreach ($val as $subval) {
|
| 1403: | $this->_spam_check_point_recursive($subval);
|
| 1404: | }
|
| 1405: | } else {
|
| 1406: |
|
| 1407: | $path_array = parse_url(XOOPS_URL);
|
| 1408: | $http_host = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
|
| 1409: |
|
| 1410: |
|
| 1411: | $count = -1;
|
| 1412: | foreach (preg_split('#https?\:\/\/#i', $val) as $fragment) {
|
| 1413: | if (strncmp($fragment, $http_host, strlen($http_host)) !== 0) {
|
| 1414: | ++$count;
|
| 1415: | }
|
| 1416: | }
|
| 1417: | if ($count > 0) {
|
| 1418: | $this->_spamcount_uri += $count;
|
| 1419: | }
|
| 1420: |
|
| 1421: |
|
| 1422: | $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
|
| 1423: | }
|
| 1424: | }
|
| 1425: |
|
| 1426: | |
| 1427: | |
| 1428: | |
| 1429: |
|
| 1430: | public function spam_check($points4deny, $uid)
|
| 1431: | {
|
| 1432: | $this->_spamcount_uri = 0;
|
| 1433: | $this->_spam_check_point_recursive($_POST);
|
| 1434: |
|
| 1435: | if ($this->_spamcount_uri >= $points4deny) {
|
| 1436: | $this->message .= (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '') . " SPAM POINT: $this->_spamcount_uri\n";
|
| 1437: | $this->output_log('URI SPAM', $uid, false, 128);
|
| 1438: | $ret = $this->call_filter('spamcheck_overrun');
|
| 1439: | if ($ret == false) {
|
| 1440: | exit;
|
| 1441: | }
|
| 1442: | }
|
| 1443: | }
|
| 1444: |
|
| 1445: | public function disable_features()
|
| 1446: | {
|
| 1447: | global $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS;
|
| 1448: |
|
| 1449: |
|
| 1450: | $error_reporting_level = error_reporting(0);
|
| 1451: |
|
| 1452: |
|
| 1453: |
|
| 1454: |
|
| 1455: | if ($this->_conf['disable_features'] & 1) {
|
| 1456: |
|
| 1457: |
|
| 1458: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -10) === 'xmlrpc.php') {
|
| 1459: | $this->output_log('xmlrpc', 0, true, 1);
|
| 1460: | exit;
|
| 1461: | }
|
| 1462: |
|
| 1463: |
|
| 1464: | if ((isset($_POST['uname']) && $_POST['uname'] === '0') || (isset($_COOKIE['autologin_pass']) && $_COOKIE['autologin_pass'] === '0')) {
|
| 1465: | $this->output_log('CRITERIA');
|
| 1466: | exit;
|
| 1467: | }
|
| 1468: | }
|
| 1469: |
|
| 1470: |
|
| 1471: |
|
| 1472: |
|
| 1473: | if ($this->_conf['disable_features'] & 1024) {
|
| 1474: |
|
| 1475: |
|
| 1476: | if (isset($_SERVER['SCRIPT_NAME']) && false === stripos($_SERVER['SCRIPT_NAME'], 'modules')) {
|
| 1477: |
|
| 1478: | if (substr($_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ((isset($_GET['type']) && $_GET['type'] === 'debug') || (isset($_POST['type']) && $_POST['type'] === 'debug')) && isset($_GET['file']) && !preg_match('/^dummy_\d+\.html$/', $_GET['file'])) {
|
| 1479: | $this->output_log('misc debug');
|
| 1480: | exit;
|
| 1481: | }
|
| 1482: |
|
| 1483: |
|
| 1484: | if (substr($_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ((isset($_GET['type']) && $_GET['type'] === 'smilies') || (isset($_POST['type']) && $_POST['type'] === 'smilies')) && isset($_GET['target']) && !preg_match('/^[0-9a-z_]*$/i', $_GET['target'])) {
|
| 1485: | $this->output_log('misc smilies');
|
| 1486: | exit;
|
| 1487: | }
|
| 1488: |
|
| 1489: |
|
| 1490: | if (substr($_SERVER['SCRIPT_NAME'], -12) === 'edituser.php' && isset($_POST['op']) && $_POST['op'] === 'avatarchoose' && isset($_POST['user_avatar']) && false !== strpos($_POST['user_avatar'], '..')) {
|
| 1491: | $this->output_log('edituser avatarchoose');
|
| 1492: | exit;
|
| 1493: | }
|
| 1494: | }
|
| 1495: |
|
| 1496: |
|
| 1497: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ((isset($_GET['fct']) && $_GET['fct'] === 'findusers') || (isset($_POST['fct']) && $_POST['fct'] === 'findusers'))) {
|
| 1498: | foreach ($_POST as $key => $val) {
|
| 1499: | if (false !== strpos($key, "'") || false !== strpos($val, "'")) {
|
| 1500: | $this->output_log('findusers');
|
| 1501: | exit;
|
| 1502: | }
|
| 1503: | }
|
| 1504: | }
|
| 1505: |
|
| 1506: |
|
| 1507: |
|
| 1508: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -23) === 'modules/news/submit.php' && isset($_POST['preview']) && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/submit.php') !== 0) {
|
| 1509: | $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
|
| 1510: | }
|
| 1511: |
|
| 1512: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -28) === 'modules/news/admin/index.php' && ($_POST['op'] === 'preview' || $_GET['op'] === 'preview') && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/admin/index.php') !== 0) {
|
| 1513: | $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
|
| 1514: | }
|
| 1515: |
|
| 1516: | if (isset($_POST['com_dopreview']) && isset($_SERVER['HTTP_REFERER']) && false === strpos(substr($_SERVER['HTTP_REFERER'], -16), 'comment_post.php')) {
|
| 1517: | $HTTP_POST_VARS['dohtml'] = $_POST['dohtml'] = 0;
|
| 1518: | }
|
| 1519: |
|
| 1520: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'blocksadmin' || $_POST['fct'] === 'blocksadmin') && isset($_POST['previewblock'])) {
|
| 1521: | die("Danger! don't use this preview. Use 'altsys module' instead.(by Protector)");
|
| 1522: | }
|
| 1523: |
|
| 1524: | if (isset($_SERVER['SCRIPT_NAME']) && substr($_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'tplsets' || $_POST['fct'] === 'tplsets')) {
|
| 1525: | if ($_POST['op'] === 'previewpopup' || $_GET['op'] === 'previewpopup' || isset($_POST['previewtpl'])) {
|
| 1526: | die("Danger! don't use this preview.(by Protector)");
|
| 1527: | }
|
| 1528: | }
|
| 1529: | }
|
| 1530: |
|
| 1531: |
|
| 1532: | error_reporting($error_reporting_level);
|
| 1533: | }
|
| 1534: |
|
| 1535: | |
| 1536: | |
| 1537: | |
| 1538: | |
| 1539: | |
| 1540: |
|
| 1541: | public function call_filter($type, $dying_message = '')
|
| 1542: | {
|
| 1543: | require_once __DIR__ . '/ProtectorFilter.php';
|
| 1544: | $filter_handler = ProtectorFilterHandler::getInstance();
|
| 1545: | $ret = $filter_handler->execute($type);
|
| 1546: | if ($ret == false && $dying_message) {
|
| 1547: | die($dying_message);
|
| 1548: | }
|
| 1549: |
|
| 1550: | return $ret;
|
| 1551: | }
|
| 1552: | }
|
| 1553: | |