| 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: |  |