1: <?php
2:
3: /**
4: * Class Protector
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: * Constructor
58: */
59: protected function __construct()
60: {
61: $this->mydirname = 'protector';
62:
63: // Preferences from configs/cache
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: // die if PHP_SELF XSS found (disabled in 2.53)
75: // if ( preg_match( '/[<>\'";\n ]/' , @$_SERVER['PHP_SELF'] ) ) {
76: // $this->message .= "Invalid PHP_SELF '{$_SERVER['PHP_SELF']}' found.\n" ;
77: // $this->output_log( 'PHP_SELF XSS' ) ;
78: // die( 'invalid PHP_SELF' ) ;
79: // }
80:
81: // sanitize against PHP_SELF/PATH_INFO XSS (disabled in 3.33)
82: // $_SERVER['PHP_SELF'] = strtr( @$_SERVER['PHP_SELF'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
83: // if( ! empty( $_SERVER['PATH_INFO'] ) ) $_SERVER['PATH_INFO'] = strtr( @$_SERVER['PATH_INFO'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
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: * @param $val
117: * @param $key
118: */
119: protected function _initial_recursive($val, $key)
120: {
121: if (is_array($val)) {
122: foreach ($val as $subkey => $subval) {
123: // check bad globals
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: // check nullbyte attack
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: // $this->purge() ;
139: }
140:
141: // register as doubtful requests against SQL Injections
142: if (preg_match('?[\s\'"`/]?', $val)) {
143: $this->_doubtful_requests["$key"] = $val;
144: }
145: }
146: }
147:
148: /**
149: * @return Protector
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: * @return bool
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: // update config cache
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: * @param $conn
195: */
196: public function setConn($conn)
197: {
198: $this->_conn = $conn;
199: }
200:
201: /**
202: * @return array
203: */
204: public function getConf()
205: {
206: return $this->_conf;
207: }
208:
209: /**
210: * @param bool $redirect_to_top
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: // clear all session values
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: /** @var XoopsUser $xoopsUser */
261: global $xoopsUser;
262:
263: if (is_object($xoopsUser)) {
264: /** @var XoopsMemberHandler */
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: * @param string $type
276: * @param int $uid
277: * @param bool $unique_check
278: * @param int $level
279: *
280: * @return bool
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: * @param $expire
333: *
334: * @return bool
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: * @return mixed
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: * @return string
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: * @param $bad_ips
374: *
375: * @return bool
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: * @param int $jailed_time
396: * @param null|string|false $ip
397: *
398: * @return bool
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: * @param bool $with_jailed_time
417: *
418: * @return array|mixed
419: */
420: public function get_bad_ips($with_jailed_time = false)
421: {
422: // list($bad_ips_serialized) = @file(Protector::get_filepath4badips());
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: // expire jailed_time
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: * @return string
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: * @param bool $with_info
460: *
461: * @return array|mixed
462: */
463: public function get_group1_ips($with_info = false)
464: {
465: // list($group1_ips_serialized) = @file(Protector::get_filepath4group1ips());
466: $group1_ips = [];
467: // Check if the file exists before attempting to read it
468: $filepath = Protector::get_filepath4group1ips();
469: if (file_exists($filepath)) {
470: $filepath4group1ips = file($filepath);
471: if ($filepath4group1ips === false) {
472: // Handle the error condition when file reading fails
473: } else {
474: // Proceed with your logic here
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: // File does not exist; handle this condition
490: }
491:
492: return $group1_ips;
493: }
494:
495: /**
496: * @return string
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: * @return string
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: * @param $ips
513: *
514: * @return bool
515: */
516: public function ip_match($ips)
517: {
518: $requestIp = \Xmf\IPAddress::fromRequest()->asReadable();
519: if (false === $requestIp) { // nothing to match
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: // foward match
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: // full match
551: if ($requestIp == $ip) {
552: $this->ip_matched_info = $info;
553: return true;
554: }
555: break;
556: default :
557: // perl regex
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: * @param null|string|false $ip
572: *
573: * @return bool
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: // make backup as uploads/.htaccess.bak automatically
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: // if .htaccess is broken, restore from backup
600: if (!$ht_body && file_exists($backup_htaccess)) {
601: $ht_body = file_get_contents($backup_htaccess);
602: }
603:
604: // new .htaccess
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: // error_log( "$new_ht_body\n" , 3 , "/tmp/error_log" ) ;
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: * @return array
631: */
632: public function getDblayertrapDoubtfuls()
633: {
634: return $this->_dblayertrap_doubtfuls;
635: }
636:
637: /**
638: * @param $val
639: * @return null
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: * @param bool $force_override
662: * @return null
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: } // skip
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: * @param $val
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: * @param $s
714: *
715: * @return string
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: * @return bool
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: * @return bool
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: /* foreach ($_POST as $key => $val) {
821: if( is_array( $_POST[ $key ] ) ) continue ;
822: if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
823: $this->last_error_type = 'ParentDir' ;
824: $this->message .= "Doubtful file specification '$val' found.\n" ;
825: $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
826: $sanitized_val = str_replace( chr(0) , '' , $val ) ;
827: if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
828: $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ;
829: if ($_REQUEST[ $key ] == $_POST[ $key ]) {
830: $_REQUEST[ $key ] = $sanitized_val ;
831: }
832: }
833: }
834: foreach ($_COOKIE as $key => $val) {
835: if( is_array( $_COOKIE[ $key ] ) ) continue ;
836: if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
837: $this->last_error_type = 'ParentDir' ;
838: $this->message .= "Doubtful file specification '$val' found.\n" ;
839: $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
840: $sanitized_val = str_replace( chr(0) , '' , $val ) ;
841: if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
842: $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ;
843: if ($_REQUEST[ $key ] == $_COOKIE[ $key ]) {
844: $_REQUEST[ $key ] = $sanitized_val ;
845: }
846: }
847: }*/
848:
849: return true;
850: }
851:
852: /**
853: * @param $current
854: * @param $indexes
855: *
856: * @return bool
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: * @param $key
873: * @param $val
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: * @return bool
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: // extensions never uploaded
922: $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp');
923: // extensions needed image check (anti-IE Content-Type XSS)
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: // anti multiple dot file (Apache mod_mime.c)
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: // anti dangerous extensions
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: // anti camouflaged image file
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: // open_basedir restriction
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: * @return bool
993: */
994: public function check_contami_systemglobals()
995: {
996: /* if( $this->_done_contami ) return $this->_safe_contami ;
997: else $this->_done_contami = true ; */
998:
999: /* foreach ($this->_bad_globals as $bad_global) {
1000: if ( isset( $_REQUEST[ $bad_global ] ) ) {
1001: $this->message .= "Attempt to inject '$bad_global' was found.\n" ;
1002: $this->_safe_contami = false ;
1003: $this->last_error_type = 'CONTAMI' ;
1004: }
1005: }*/
1006:
1007: return $this->_safe_contami;
1008: }
1009:
1010: /**
1011: * @param bool $sanitize
1012: *
1013: * @return bool
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: * @param bool $sanitize
1043: *
1044: * @return bool
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: // $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val));
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: * @param $uid
1072: *
1073: * @return bool
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: * @param int $uid
1169: * @param bool $can_ban
1170: *
1171: * @return bool
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: // gargage collection
1191: $result = $xoopsDB->queryF(
1192: 'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1193: . ' WHERE expire < UNIX_TIMESTAMP()'
1194: );
1195:
1196: // for older versions before updating this module
1197: if ($result === false) {
1198: $this->_done_dos = true;
1199:
1200: return true;
1201: }
1202:
1203: // sql for recording access log (INSERT should be placed after SELECT)
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: // bandwidth limitation
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: // F5 attack check (High load & same URI)
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: // delayed insert
1233: $xoopsDB->queryF($sql4insertlog);
1234:
1235: // extends the expires of the IP with 5 minutes at least (pending)
1236: // $result = $xoopsDB->queryF( "UPDATE ".$xoopsDB->prefix($this->mydirname.'_access')." SET expire=UNIX_TIMESTAMP()+300 WHERE ip='$ip4sql' AND expire<UNIX_TIMESTAMP()+300" ) ;
1237:
1238: // call the filter first
1239: $ret = $this->call_filter('f5attack_overrun');
1240:
1241: // actions for F5 Attack
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: // Check its Agent
1277: if (trim($this->_conf['dos_crsafe']) != '' && isset($_SERVER['HTTP_USER_AGENT']) && preg_match($this->_conf['dos_crsafe'], $_SERVER['HTTP_USER_AGENT'])) {
1278: // welcomed crawler
1279: $this->_done_dos = true;
1280:
1281: return true;
1282: }
1283:
1284: // Crawler check (High load & different URI)
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: // delayed insert
1293: $xoopsDB->queryF($sql4insertlog);
1294:
1295: if ($crawler_count > $this->_conf['dos_crcount']) {
1296:
1297: // call the filter first
1298: $ret = $this->call_filter('crawler_overrun');
1299:
1300: // actions for bad Crawler
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: * @return bool|null
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: // some UA send 'deleted' as a value of the deleted cookie.
1356: if ($victim_uname === 'deleted') {
1357: return null;
1358: }
1359: $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname");
1360:
1361: // gargage collection
1362: $result = $xoopsDB->queryF(
1363: 'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()'
1364: );
1365:
1366: // sql for recording access log (INSERT should be placed after SELECT)
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: // count check
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: // delayed insert
1392: $xoopsDB->queryF($sql4insertlog);
1393: return null;
1394: }
1395:
1396: /**
1397: * @param $val
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: // http_host
1407: $path_array = parse_url(XOOPS_URL);
1408: $http_host = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
1409:
1410: // count URI up
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: // count BBCode likd [url=www....] up (without [url=http://...])
1422: $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
1423: }
1424: }
1425:
1426: /**
1427: * @param $points4deny
1428: * @param $uid
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: // disable "Notice: Undefined index: ..."
1450: $error_reporting_level = error_reporting(0);
1451:
1452: //
1453: // bit 1 : disable XMLRPC , criteria bug
1454: //
1455: if ($this->_conf['disable_features'] & 1) {
1456:
1457: // zx 2005/1/5 disable xmlrpc.php in root
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: // security bug of class/criteria.php 2005/6/27
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: // bit 11 : XSS+CSRFs in XOOPS < 2.0.10
1472: //
1473: if ($this->_conf['disable_features'] & 1024) {
1474:
1475: // root controllers
1476: if (isset($_SERVER['SCRIPT_NAME']) && false === stripos($_SERVER['SCRIPT_NAME'], 'modules')) {
1477: // zx 2004/12/13 misc.php debug (file check)
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: // zx 2004/12/13 misc.php smilies
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: // zx 2005/1/5 edituser.php avatarchoose
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: // zx 2005/1/4 findusers
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: // preview CSRF zx 2004/12/14
1507: // news submit.php
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: // news admin/index.php
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: // comment comment_post.php
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: // disable preview of system's blocksadmin
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: // tpl preview
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: // restore reporting level
1532: error_reporting($error_reporting_level);
1533: }
1534:
1535: /**
1536: * @param string $type
1537: * @param string $dying_message
1538: *
1539: * @return int|mixed
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: