· 6 years ago · Dec 12, 2019, 06:06 PM
1<?php
2class pareto_functions {
3 private $_open_basedir = 0, $_banip = 0, $_quietscript = 0, $_get_ip_count = 0, $_total_ips = 500, $_post_filter_mode = 0, $_ban_time = 86400, $_hard_ban_count = 10;
4 private $_doc_root, $_datalist, $_log_file, $_log_file_key;
5 private $_bypassbanip = false, $_spider_bypass = false;
6 private $_injectors = array(), $_ip_array = array(), $_get_all = array(), $_post_all = array();
7 protected $lockdown_setting = 'pareto_security_lockdown', $_hard_ban_mode = false, $_tor_block = false, $_timestamp = '';
8 protected $settings_field = 'pareto_security_settings_options', $ip_hash_list = 'pareto_security_ip_flood_list';
9 public $_trim_log_entry = 450, $_time_offset, $_adv_mode = 0, $safe_host = '';
10 /**
11 * Pareto Security constructor.
12 *
13 */
14 public function __construct() {
15 if ( false !== $this->is_wp() ) {
16 if ( !isset( $this->_time_offset ) ) $this->_time_offset = ( int ) get_option( 'gmt_offset' );
17 $unix_time = $this->file_created();
18 define( 'PARETO_RELEASE_DATE', date_i18n( 'F j, Y', $unix_time ) );
19 define( 'SETTINGS_INSTALL_LOG', str_replace( ' ', '%20', PARETO_RELEASE_DATE ) . " Safe " . $_SERVER[ 'SERVER_ADDR' ] . " GET plugins.php Pareto%20Security%20Installed" );
20 $this->do_filters();
21 define( 'PARETO_LOG_LIST', 'pareto_security_log_list' );
22 } else {
23 define( 'PARETO_LOGS', __DIR__ . "/logs/" );
24 $this->set_crypto_key_file();
25 }
26 }
27 /**
28 * Runs through a series of WP filters
29 *
30 * @return void
31 */
32 function do_filters() {
33 if ( !function_exists( 'wp_delete_file' ) ) require_once ABSPATH . WPINC . '/functions.php';
34 add_filter( 'wp_delete_file', array( $this, 'check_filenames' ) );
35 }
36 /**
37 * Check for AFD attempts
38 *
39 *
40 * @return array
41 */
42 function check_filenames( $file ) {
43 $file_path = $this->get_dir() . 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
44 if ( false === strpos( $file, $file_path ) ) {
45 if ( false === $this->is_wp( false, true, true ) ) $this->karo( "Arbitrary File Deletion Attempt: " . $file, true, 'Medium', true );
46 }
47 return $file;
48 }
49 /**
50 * Sets error_reporting
51 *
52 *
53 * @return void
54 */
55 function _set_error_level() {
56 $val = ( false !== $this->integ_prop( $this->_quietscript ) ) ? ( int ) $this->_quietscript : 0;
57 @ini_set( 'display_errors', 0 );
58 switch ( ( int ) $val ) {
59 case ( 0 ):
60 error_reporting( 6135 );
61 break;
62 case ( 1 ):
63 error_reporting( 0 );
64 break;
65 case ( 2 ):
66 error_reporting( 32767 );
67 @ini_set( 'display_errors', 1 );
68 break;
69 default:
70 error_reporting( 6135 );
71 }
72 }
73 function http_status( $num ) {
74 $http = array(
75 200 => 'HTTP/1.1 200 OK',
76 403 => 'HTTP/1.1 403 Forbidden',
77 404 => 'HTTP/1.1 404 Not Found',
78 );
79 header( $http[ $num ] );
80 return
81 array(
82 'code' => $num,
83 'error' => $http[ $num ],
84 );
85 }
86 /**
87 * Custom 403 Access Denied
88 *
89 *
90 * @return void
91 */
92 function send403() {
93 $this->http_status( 403 );
94 exit();
95 }
96 /**
97 * Send custom 200 OK denied
98 *
99 * return void
100 */
101 function send200() {
102 $this->http_status( 200 );
103 exit();
104 }
105 /**
106 * Activates the plugin
107 *
108 * @return void
109 */
110 public function _activate() {
111 update_option( PARETO_LOG_LIST, array(
112 0 => SETTINGS_INSTALL_LOG ) );
113 update_option( $this->settings_field, array( // set defaults
114 'advanced_mode' => 0,
115 'hard_ban_mode' => 0,
116 'email_report' => 1,
117 'ban_mode' => 0,
118 'tor_block' => 0
119 ) );
120 update_option( $this->ip_hash_list, array() );
121 }
122 /**
123 * Deactivates the plugin
124 *
125 *
126 * @return void
127 */
128 public function _deactivate() {
129 if ( false !== $this->get_file_perms( $this->htapath(), true, true ) ) {
130 # clear IP addresses from HTACCESS
131 $this->htaccess_unbanip();
132 }
133 update_option( PARETO_LOG_LIST, "" );
134 update_option( $this->settings_field, "" );
135 update_option( $this->ip_hash_list, array() );
136 }
137 /**
138 * Prevent page execution
139 * Trigger logging
140 * Ban Ip Address
141 *
142 * @return void
143 */
144 function karo( $req = '', $t = false, $severity = '', $log_only = false, $safelist_url = '' ) {
145 if ( $this->cmpstr( $severity, '' ) ) {
146 $ban_type = 'Medium';
147 } else $ban_type = $severity;
148
149 $is_wp = $this->is_wp();
150 $req = ( $this->cmpstr( substr( $req, 0, 2 ), "/?" ) ) ? substr( $req, 2 ) : $req;
151 $req = ( $this->cmpstr( substr( $req, 0, 1 ), "/" ) ) ? substr( $req, 1 ) : $req;
152
153 if ( false === $is_wp ) {
154 if ( false !== $this->logfile_name() )
155 $this->_log_file = $this->logfile_name();
156 if ( false === $this->logfile_exists() ) $this->create_fileset();
157 }
158
159 $this_ip = $this->get_ip();
160 $block_request = false;
161 $is_admin_ip = false;
162 if ( false !== $is_wp ) {
163 $is_admin_ip = $this->is_admin_ip();
164 }
165 $block_request = ( false !== $this->is_iis() ||
166 false === ( bool ) $t ||
167 false !== $this->is_server( $this_ip ) ||
168 false !== $this->_bypassbanip ||
169 false !== $is_admin_ip ) ? true : false;
170 $is_registered = false;
171 if ( false !== $is_wp ) {
172 $is_registered = true;
173 $this_user = $this->get_wp_current_user();
174 if ( strlen( $this_user ) > 0 ) $req = ( false !== $is_registered ) ? 'User: ' . $this_user . ' :: ' . $req : $req;
175 if ( false === $log_only || false !== $is_admin_ip || ( $this->is_server( $this_ip ) && $this->cmpstr( 'wp-cron.php', $this->get_filename() ) ) ) {
176 $req = '[Notice] ' . $req . ' (WP Admin IP)';
177 $ban_type = 'Safe';
178 } elseif ( false !== $block_request ) {
179 $req = '[Blocked] ' . $req;
180 } else {
181 $req = '[Banned] '. $req;
182 }
183 }
184 #$req = ( false !== $is_admin_ip ) ? $req . ' (WP Admin IP)' : $req;
185
186 # create the log entry
187 # set $lockdown_mode
188 $this->log_request( $req, $ban_type );
189
190 # Give a logged in WP Admins a pass only when posting data
191 # if notification only
192 if ( false !== $this->is_wp( false, true ) || false === $log_only ) return;
193
194 # Do not ban or block the following
195 if ( false !== $this->cmpstr( $ban_type, 'Safe' ) || false !== $is_admin_ip ) return;
196
197 # add IP address or return 403 only
198 if ( false === $block_request ) $this->htaccessbanip( $this_ip );
199 $this->send403();
200 }
201 function file_created() {
202 $file_created = filemtime( __FILE__ );
203 return $this->updated( $file_created, $this->_time_offset );
204 }
205 /**
206 * Create the log entry
207 *
208 * @return void
209 */
210 function write_log( $req = "", $req_orig = "", $ban_type ) {
211 $logfile = array();
212 $ban_type = strtolower( $ban_type );
213 $logfile = get_option( PARETO_LOG_LIST );
214 $req_orig = $this->htmlentities_safe( $req_orig );
215 # set lockdown mode
216 if ( false !== $this->lockdown_mode( $logfile ) ) return; // no email notifications or entries into DB
217
218 if ( isset( $this->options[ 'email_report' ] ) && false !== $this->options[ 'email_report' ] || $this->cmpstr( 'medium', $ban_type, true ) || $this->cmpstr( "high", $ban_type, true ) ) {
219 $x = 0;
220 # count medium and high log entries
221 foreach( $logfile as $key => $val ) {
222 $short_val = strtolower( substr( $val, 0, 79 ) );
223 if ( ( false !== strpos( $short_val, "high" ) || false !== strpos( $short_val, "medium" ) ) && false === strpos( $short_val, "[blocked]" ) && false === strpos( $short_val, " safe " ) ) $x++;
224 }
225 # email every 5 entries
226 $ban_type = ucfirst( $ban_type );
227 if ( false === $this->cmpstr( 'Safe', $ban_type, true ) && false === $this->cmpstr( 'Low', $ban_type, true ) ) {
228 $logged_count = ( string ) ( $x / 5 );
229 if ( false !== ( bool ) $this->_email_report && $x != 0 && false !== ctype_digit( $logged_count ) ) {
230 $text_color = ( $this->cmpstr( 'Medium', $ban_type, true ) ) ? "#e68735" : "#c72b2c";
231 $this->email_log( "\n<tr style=\"background-color: #F3F3F3\">\n" . " <td style=\"font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;vertical-align:top; width:100px; white-space: nowrap\">" . $this->set_timestamp( time() ) . "</td>\n
232 <td style=\"vertical-align:top; text-align: center; width:70px; white-space: nowrap; font-size:11px;white-space:nowrap; font-family:Verdana,Tahoma,Arial,sans-serif;font-weight: bold; color:" . $text_color . "\">" . $ban_type . "</td>\n
233 <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:140px; white-space: nowrap\">" . $this->get_ip() . "</td>\n
234 <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:50px; white-space: nowrap\">" . $_SERVER[ 'REQUEST_METHOD' ] . "</td>\n
235 <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:100px; white-space: nowrap\">" . $this->get_filename() . "</td>\n
236 <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;white-space: nowrap\"><code>" . $req_orig . "</code></td>\n
237 </tr>" );
238 }
239 }
240 }
241
242 # Insert new entry
243 array_unshift( $logfile, $req );
244 if ( !empty( $logfile ) ) {
245 $mylogs = array();
246 $log_total = ( ( $l_count = count( $logfile ) ) >= 100 ) ? 100 : $l_count;
247 for ( $x = 0; ( $x <= $log_total && !empty( $logfile[ $x ] ) ); $x++ ) {
248 $mylogs[ $x ] = $logfile[ $x ];
249 }
250 update_option( PARETO_LOG_LIST, $mylogs );
251 }
252 }
253 /**
254 * Test attack rate, trigger lockdown
255 *
256 * @param $logfile = array()
257 *
258 * @return void
259 */
260 function lockdown_mode( $logfile ) {
261 if ( empty( $logfile ) || 10 > count( $logfile ) ) return false;
262 $timestamps = array();
263 # only testing the last 10 timestamps.
264 for ( $x = 0; $x <= 9; $x++ ) {
265 $ts = $logfile[ $x ];
266 if ( false !== is_numeric( trim( substr( $ts, 0, 10 ) ) ) ) {
267 $ts = trim( substr( $ts, 0, 10 ) );
268 $ts = $this->updated( $ts, $this->_time_offset );
269 $timestamps[] = ( int ) $ts;
270 } else {
271 $ts = str_replace( '<br>', ' ', $ts );
272 $ts = substr( $ts, 0, 27 );
273 $ts = explode( ' ' , $ts );
274 $timestamps[] = strtotime( $ts[ 0 ] . $ts[ 1 ] );
275 }
276 }
277 # 10 entries in 10 minutes = trigger lockdown mode
278 if ( 600 >= ( int ) ( time() - ( int ) $timestamps[ 9 ] ) ) {
279 return true;
280 } else {
281 return false;
282 }
283 }
284 /**
285 * Create the log entry if not Wordpress
286 *
287 * @return void
288 */
289 function write_log_non_wp( $req = "", $htpath ) {
290 $req = htmlentities_decode_safe( $req );
291 $logfile = PARETO_LOGS . $this->_log_file;
292 if ( file_exists( $logfile ) ) {
293 @chmod( $logfile, 0666 );
294 $fp = fopen( $logfile, 'a' );
295 fwrite( $fp, $req );
296 fclose( $fp );
297 $mylogs_tmp = array_reverse( file( $logfile ) );
298 $mylogs = array();
299 $log_total = ( ( $l_count = count( $mylogs_tmp ) ) >= 100 ) ? 99 : $l_count;
300 for ( $x = 0; ( $x <= $log_total && !empty( $mylogs_tmp[ $x ] ) ); $x++ ) {
301 $mylogs[ $x ] = $mylogs_tmp[ $x ];
302 }
303 $fp = fopen( $logfile, 'w' );
304 fwrite( $fp, implode( array_reverse( $mylogs ), "" ) );
305 fclose( $fp );
306 chmod( $logfile, 0644 );
307 }
308 }
309 /**
310 * Create a unique log string
311 *
312 * @return string
313 */
314 function get_ulid() {
315 $ulid = substr( $this->cleanString( 6, $this->do_bcrypt( $this->get_wp_key(), 12 ) ), -16 );
316 return $ulid;
317 }
318 /**
319 * Make log entry html safe
320 *
321 * @return void
322 */
323 function log_request( $req, $ban_type ) {
324 if ( false !== ( bool ) $this->_adv_mode || ( false === ( bool ) $this->_adv_mode && $ban_type != "Low" ) ) {
325 if ( false === $this->is_wp() ) date_default_timezone_set( 'NZ' );
326 $req = ( strlen( $req ) > $this->_trim_log_entry ) ? substr( $req, 0, $this->_trim_log_entry ) . "..." : $req;
327 $req = $this->cleanString( 11, $req ); // remove control characters (being extra safe)
328 $req = str_replace( "\\", "\", $req );
329 $uuid = $this->get_ulid();
330 $req_orig = ( strlen( $req ) > 250 ) ? wordwrap( $req, 200, "<br />\n" ) : $req;
331 $req = time() . " " .
332 $ban_type . " " .
333 $this->get_ip() . " " .
334 $_SERVER[ 'REQUEST_METHOD' ] . " " .
335 $this->get_filename() . " " .
336 str_replace( " ", "%20", $req ) . " " .
337 $uuid;
338 if ( false !== $this->is_wp() ) {
339 $this->write_log( $req, $req_orig, $ban_type );
340 } else {
341 $this->write_log_non_wp( $req, $this->_log_file );
342 }
343 }
344 }
345 /**
346 * Get file permissions
347 *
348 * @return string
349 */
350 function dirfile_perms( $path ) {
351 $length = strlen( decoct( fileperms( $path ) ) ) - 3;
352 return substr( decoct( fileperms( $path ) ), $length );
353 }
354 /**
355 * Sets $this->_log_file_key
356 *
357 * @return void
358 */
359 function set_crypto_key_file() {
360 $this->_log_file_key = substr( $this->get_uuid(), 0, 32 ) . '_request.key';
361 }
362 /**
363 * Generate a logfile name
364 *
365 * @return string
366 */
367 function logfile_name() {
368 if ( false !== $this->logfile_exists() ) {
369 $key_array = file( PARETO_LOGS . $this->_log_file_key );
370 } else
371 return false;
372 $filename = substr( hash( 'sha256', $key_array[ 0 ], false ), 0, 32 ) . "_request.log";
373 return $filename;
374 }
375 /**
376 * Delete logs
377 *
378 * @return void
379 */
380 function logfile_cleanup() {
381 $filelist = scandir( PARETO_LOGS );
382 foreach ( $filelist as $key => $filename ) {
383 if ( strlen( $filename ) > 20 && false === $this->cmpstr( $filename, $this->_log_file, true ) && $this->cmpstr( '_request.log', substr( $filename, -12, 12 ), true ) ) {
384 $logfilename = PARETO_LOGS . $filename;
385 if ( false === strpos( $logfilename, 'img' ) )
386 unlink( PARETO_LOGS . $filename );
387 }
388 if ( strlen( $filename ) > 20 && false === $this->cmpstr( $filename, $this->_log_file_key, true ) && $this->cmpstr( '_request.key', substr( $filename, -12, 12 ), true ) ) {
389 unlink( PARETO_LOGS . $filename );
390 }
391 }
392 }
393 /**
394 * Check if logs exhist
395 *
396 * @return boolean
397 */
398 function logfile_exists() {
399 return ( bool ) ( file_exists( PARETO_LOGS . ".htaccess" ) || file_exists( PARETO_LOGS . $this->_log_file_key ) );
400 }
401 /**
402 * Create unique string
403 *
404 * @return string
405 */
406 function do_bcrypt( $string, $cost = 5 ) {
407 $salt = ( function_exists( 'random_bytes' ) ) ? substr( strtr( base64_encode( random_bytes( 32 ) ), '+', '.' ), 0, 22 ) : substr( base64_encode( openssl_random_pseudo_bytes( 32 ) ), 0, 22 );
408 $salt = str_replace( "+", ".", $salt );
409 $param = '$' . implode( '$', array(
410 "2y",
411 $cost,
412 $salt
413 ) );
414 $output = crypt( $string, $param );
415 return $output;
416 }
417
418 /**
419 * If not WP, create log file set
420 *
421 * @return void
422 */
423 function create_fileset() {
424 $htlog_content = 'Options -Indexes' . "\n" . 'Options +SymLinksIfOwnerMatch' . "\n" . 'ServerSignature off' . "\n" . '<Files ~ "^.*\_([Rr][Ee][Qq][Uu][Ee][Ss][Tt]\.)">' . "\n" . 'order allow,deny' . "\n" . 'deny from all' . "\n" . 'satisfy all' . "\n" . '</Files>' . "\n";
425
426 if ( false === is_dir( PARETO_LOGS ) )
427 @mkdir( PARETO_LOGS, 0755 );
428
429 # Create key
430 $crypto_key_file = PARETO_LOGS . $this->_log_file_key;
431 $hash_string = ( $this->is_wp() ) ? $this->get_wp_key() : $this->get_uuid();
432 $key = $this->do_bcrypt( $hash_string, 12 );
433 $fp = fopen( $crypto_key_file, 'w' );
434 fwrite( $fp, $key );
435 fclose( $fp );
436
437 $this->_log_file = $this->logfile_name();
438
439 $logfile = PARETO_LOGS . $this->_log_file;
440 # Create logfile
441 $fp = fopen( $logfile, 'c' );
442 fwrite( $fp, "" );
443 fclose( $fp );
444
445 # Create HTACCESS
446 $fp = fopen( PARETO_LOGS . ".htaccess", 'w' );
447 fwrite( $fp, $htlog_content );
448 fclose( $fp );
449
450 # remove any older logs
451 if ( false !== $this->is_wp() )
452 $this->logfile_cleanup();
453 @chmod( PARETO_LOGS, 0755 );
454 @chmod( PARETO_LOGS . ".htaccess", 0644 );
455 @chmod( $logfile, 0644 );
456 @chmod( $crypto_key, 0644 );
457 }
458 /**
459 * Generate a unique key from WP variables
460 *
461 * @return string
462 */
463 function get_wp_key() {
464 $token = '';
465 $key_vars = array(
466 'AUTH_KEY' => AUTH_KEY,
467 'SECURE_AUTH_KEY' => SECURE_AUTH_KEY,
468 'NONCE_KEY' => NONCE_KEY,
469 'AUTH_SALT' => AUTH_SALT,
470 'SECURE_AUTH_SALT' => SECURE_AUTH_SALT,
471 'NONCE_SALT' => NONCE_SALT
472 );
473 foreach ( $key_vars as $const_key => $const_arg ) {
474 if ( defined( $const_key ) && strlen( $const_arg ) > 40 )
475 $token = hash( 'sha256', $const_arg . $token, false );
476 }
477 return $token;
478 }
479 /**
480 * Create unique user id from server variables
481 *
482 * @return string
483 */
484 function get_uuid() {
485 $_get_server = array_change_key_case( $_SERVER, CASE_LOWER );
486 $uuid = '';
487 $server_vars = array(
488 'server_admin',
489 'server_addr',
490 'document_root',
491 'server_software',
492 'path',
493 'server_protocol',
494 'rails_env',
495 'gateway_interface',
496 'server_addr',
497 'server_name',
498 'server_signature',
499 'http_accept_language',
500 'http_accept_encoding',
501 'http_accept',
502 'http_upgrade_insecure_requests',
503 'http_cache_control',
504 'ssl_tls_sni',
505 'https',
506 'dh_user',
507 'dsid',
508 'fcgi_role'
509 );
510
511 $x = 0;
512 $uuid = '';
513 while ( $x < count( $server_vars ) ) {
514 if ( isset( $_get_server[ $server_vars[ $x ] ] ) ) {
515 if ( !$_get_server[ $server_vars[ $x ] ] == 'max-age=0' ) {
516 $uuid = hash( 'sha256', $_get_server[ $server_vars[ $x ] ] . $uuid, false );
517 }
518 }
519 $x++;
520 }
521 return $uuid;
522 }
523 /**
524 * Convert unix timestamp to readable date
525 *
526 * @return string
527 */
528 function updated( $unixtime, $offset ) {
529 if ( false !== $this->integ_prop( $offset ) ) {
530 if ( $offset > 0 && $offset < 14 ) {
531 return $unixtime + ( $offset * 3600 );
532 } elseif ( $this->cmpstr( $offset, 0 ) ) {
533 return $unixtime;
534 }
535 } elseif ( false !== preg_match( "#^(-[0-9]{1,}|[0-9]{1,})$#", $offset ) ) {
536 $x = ( int ) str_replace( '-', '', ( string ) $offset );
537 return $unixtime - ( $x * 3600 );
538 }
539 }
540 /**
541 * Load XML lists into arrays
542 *
543 * @return void
544 */
545 public function load_lists( $blacklists = false, $injectors = false ) {
546 if ( false !== $blacklists ) {
547 $xml_lists = ( ( false !== $this->is_wp() ) ? plugin_dir_path( __FILE__ ) : dirname( __FILE__ ) ) . "/xml/lists.xml";
548 if ( false !== $this->is_wp() ) $this->_datalist = wp_cache_get( 'mylists' );
549 if ( file_exists( $xml_lists ) ) {
550 $xml = simplexml_load_file( $xml_lists );
551 $this->_datalist[ 1 ] = explode( ',', preg_replace( "/[\s]/i", "", $xml->get ) );
552 $this->_datalist[ 2 ] = explode( ',', preg_replace( "/[\s]/i", "", $xml->post ) );
553 $this->_datalist[ 3 ] = explode( ',', preg_replace( "/[\s]/i", "", $xml->bad_ua ) );
554 $this->_datalist[ 4 ] = explode( ',', preg_replace( "/[\s]/i", "", $xml->good_ua ) );
555 if ( false !== $this->is_wp() ) wp_cache_set( 'mylists', $this->_datalist, '', 10800 );
556 }
557 }
558 if ( false !== $injectors ) {
559 $xml_db = ( ( false !== $this->is_wp() ) ? plugin_dir_path( __FILE__ ) : dirname( __FILE__ ) ) . "/xml/injectors.xml";
560 if ( false !== $this->is_wp() ) $this->_injectors = wp_cache_get( 'myinjectors' );
561 if ( file_exists( $xml_db ) ) {
562 $xml = simplexml_load_file( $xml_db );
563 $this->_injectors[ 1 ] = preg_replace( "/[\s]/i", "", $xml->vartrig );
564 $this->_injectors[ 2 ] = preg_replace( "/[\s]/i", "", $xml->matchlist );
565 $this->_injectors[ 3 ] = preg_replace( "/[\s]/i", "", $xml->sqlupdate );
566 $this->_injectors[ 4 ] = preg_replace( "/[\s]/i", "", $xml->fileinject );
567 $this->_injectors[ 5 ] = preg_replace( "/[\s]/i", "", $xml->pattern );
568 $this->_injectors[ 6 ] = preg_replace( "/[\s]/i", "", $this->decode_code( $xml->symtrig ) );
569 if ( false !== $this->is_wp() ) wp_cache_set( 'myinjectors', $this->_injectors, '', 10800 );
570 }
571 }
572 }
573 /**
574 * Filter $string for injection attempts
575 *
576 * @return boolean
577 */
578 function injectMatch( $string ) {
579 $string = $this->url_decoder( strtolower( $string ) );
580
581 $matches1 = array();
582 $matches2 = array();
583 $matches3 = array();
584 $matches4 = array();
585
586 # these are the triggers to engage the rest of this function.
587 $vartrig = $this->_injectors[ 1 ];
588 for ( $x = 0; $x <= 9; $x++ ) {
589 $this_string = $this->cleanString( $x, $string );
590 preg_match_all( "/$vartrig/im", $this_string, $matches1 );
591 # second set of tests, string must include at least one of these to trigger
592 $symtrig = ( string ) $this->_injectors[ 6 ];
593 preg_match_all( "/[$symtrig]|[0-9]/i", $this_string, $matches2 );
594 }
595 # Hutana we have a raru!
596 if ( empty( $vartrig ) && empty( $symtrig ) ) return false;
597
598 $a = 0;
599 $b = 0;
600 if ( !empty( $matches1[ 0 ] ) ) $a = count( array_unique( $matches1[ 0 ] ) );
601 if ( !empty( $matches2[ 0 ] ) ) $b = count( array_unique( $matches2[ 0 ] ) );
602
603 if ( ( ( int )$a > 0 ) && ( ( int )$b > 0 ) ) {
604 $j = 0;
605 # toggle through 6 different filters
606 $sqlmatchlist = $this->_injectors[ 2 ];
607 $sqlupdatelist = $this->_injectors[ 3 ];
608 $sqlfilematchlist = $this->_injectors[ 4 ];
609
610 while ( $j <= 9 ) {
611 $this_string = $this->cleanString( $j, $string );
612
613 # First up, REGEX! ( Borrowed from NoScript https://noscript.net/ )
614 # Most injection attempts are caught here
615 $regex_pattern = ( string ) $this->_injectors[ 5 ];
616 if ( false !== ( bool ) preg_match( "/$regex_pattern/i", $this_string ) ) {
617 return true;
618 }
619 if ( false !== ( bool ) preg_match( "/\bdrop\b/i", $this_string ) && false !== ( bool ) preg_match( "/\btable\b|\buser\b/i", $this_string ) && false !== ( bool ) preg_match( "/--/i", $this_string ) ) {
620 return true;
621 } elseif ( ( false !== strpos( $this_string, 'grant' ) ) && ( false !== strpos( $this_string, 'all' ) ) && ( false !== strpos( $this_string, 'privileges' ) ) ) {
622 return true;
623 } elseif ( false !== ( bool ) preg_match( "/(?:(sleep\((\s*)(\d*)(\s*)\)|benchmark\((.*)\,(.*)\)))/i", $this_string ) ) {
624 return true;
625 } elseif ( preg_match_all( "/\bload\b|\bdata\b|\binfile\b|\btable\b|\bterminated\b/i", $this_string, $matches ) > 3 ) {
626 $match_list = array_unique( $matches[ 0 ] );
627 if ( count( $match_list ) > 3 )
628 return true;
629 } elseif ( ( ( false !== ( bool ) preg_match( "/select|sleep|isnull|declare|ascii\(substring|length\(/i", $this_string, $matches1 ) ) &&
630 ( false !== ( bool ) preg_match( "/\band\b|\bif\b|group_|_ws|load_|exec|when|then|concat\(|\bfrom\b/i", $this_string, $matches2 ) ) &&
631 ( false !== ( bool ) preg_match_all( "/$sqlmatchlist/im", $this_string, $matches3 ) ) ) ) {
632 $this_matches = array();
633 $this_matches = array_merge( $matches1, $matches2, $matches3[ 0 ] );
634 $this_matches = array_unique( $this_matches );
635 $n = count( $this_matches );
636 if ( strpos( $this_string, 'union' ) ) $n = $n < 1;
637 if ( $n > 3 ) return true;
638 } elseif ( false !== strpos( $this_string, 'from' ) && false !== strpos( $this_string, 'update' ) && false !== ( bool ) preg_match( "/\bset\b/i", $this_string ) && false !== ( bool ) preg_match( "/$sqlupdatelist/im", $this_string, $matches ) ) {
639 return true;
640 } elseif ( false !== strpos( $this_string, 'having' ) && false !== ( bool ) preg_match( "/\bor\b|\band\b/i", $this_string ) && false !== ( bool ) preg_match( "/$sqlupdatelist/im", $this_string ) ) {
641 # tackle the noDB / js issue
642 return true;
643 } elseif ( ( $this->substri_count( $this_string, 'var' ) > 1 ) && false !== ( bool ) preg_match( "/date\(|while\(|sleep\(/i", $this_string ) ) {
644 return true;
645 # reflected download attack
646 } elseif ( ( substr_count( $this_string, '|' ) > 2 ) && false !== ( bool ) preg_match( "/json/i", $this_string ) ) {
647 return true;
648 }
649
650 # run through a set of filters to find specific attack vectors on the request string
651 $thenode = $this->cleanString( $j, $this->getREQUEST_URI() );
652
653 if ( ( false !== ( bool ) preg_match( "/onmouse(?:down|over)/i", $this_string ) ) && ( 2 < ( int ) preg_match_all( "/c(?:path|tthis|t\(this)|http:|(?:forgotte|admi)n|sqlpatch|ftp:|(?:aler|promp)t/i", $thenode, $matches ) ) ) {
654 $match_list = array_unique( $matches[ 0 ] );
655 if ( count( $match_list ) > 2 )
656 return true;
657 } elseif ( ( ( false !== strpos( $thenode, 'ftp:' ) ) && ( $this->substri_count( $thenode, 'ftp' ) > 1 ) ) && ( 2 < ( int ) preg_match_all( "/@|\/\/|:/i", $thenode, $matches ) ) ) {
658 $match_list = array_unique( $matches[ 0 ] );
659 if ( count( $match_list ) > 2 )
660 return true;
661 } elseif ( ( substr_count( $this_string, '../' ) > 3 ) || ( substr_count( $this_string, '..//' ) > 3 ) || ( $this->substri_count( $this_string, '0x2e0x2e/' ) > 1 ) ) {
662 if ( false !== ( bool ) preg_match( "/$sqlfilematchlist/im", $this_string ) ) {
663 return true;
664 }
665 } elseif ( ( substr_count( $this_string, '/' ) > 1 ) && ( 2 <= ( int ) preg_match_all( "/$sqlfilematchlist/im", $thenode, $matches ) ) ) {
666 $match_list = array_unique( $matches[ 0 ] );
667 if ( count( $match_list ) > 2 )
668 return true;
669 } elseif ( ( false !== ( bool ) preg_match( "/%0D%0A/i", $thenode ) ) && ( false !== strpos( $thenode, 'utf-7' ) ) ) {
670 return true;
671 }
672 if ( 5 <= substr_count( $this_string, '%' ) )
673 $this_string = str_replace( '%', '', $this_string );
674 if ( ( false !== ( bool ) preg_match( "/\border by\b|\bgroup by\b/i", $this_string ) ) && ( false !== ( bool ) preg_match( "/select|\band\b/i", $this_string ) ) && ( false !== ( bool ) preg_match( "/\bcolumn\b|\bdesc\b|\berror\b|\bfrom\b|hav|\blimit\b|offset|\btable\b|\/|--/i", $this_string ) || ( false !== ( bool ) preg_match( "/\b[0-9]\b/i", $this_string ) ) ) ) {
675 return true;
676 } elseif ( ( false !== ( bool ) preg_match( "/\btable\b|\bcolumn\b/i", $this_string ) ) && false !== strpos( $this_string, 'exists' ) && false !== ( bool ) preg_match( "/\bif\b|\berror\b|\buser\b|\bno\b/i", $this_string ) ) {
677 return true;
678 } elseif ( ( false !== strpos( $this_string, 'waitfor' ) && false !== strpos( $this_string, 'delay' ) && ( ( bool ) preg_match( "/(:)/i", $this_string ) ) ) || ( false !== strpos( $this_string, 'nowait' ) && false !== strpos( $this_string, 'with' ) && ( false !== ( bool ) preg_match( "/--|\/|\blimit\b|\bshutdown\b|\bupdate\b|\bdesc\b/i", $this_string ) ) ) ) {
679 return true;
680 } elseif ( false !== ( bool ) preg_match( "/\binto\b/i", $this_string ) && ( false !== ( bool ) preg_match( "/\boutfile\b/i", $this_string ) ) ) {
681 return true;
682 } elseif ( false !== ( bool ) preg_match( "/\bdrop\b/i", $this_string ) && ( false !== ( bool ) preg_match( "/\--/i", $this_string ) ) && ( false !== ( bool ) preg_match( "/\btable\b/i", $this_string ) ) ) {
683 return true;
684 } elseif ( ( ( false !== strpos( $this_string, 'create' ) && false !== ( bool ) preg_match( "/\btable\b|\buser\b|\bselect\b/i", $this_string, $matches1 ) ) ||
685 ( false !== strpos( $this_string, 'delete' ) && false !== strpos( $this_string, 'from' ) ) ||
686 ( false !== strpos( $this_string, 'insert' ) && ( false !== ( bool ) preg_match( "/\bexec\b|\binto\b|from/i", $this_string, $matches2 ) ) ) ||
687 ( false !== strpos( $this_string, 'select' ) && ( false !== ( bool ) preg_match_all( "/\bby\b|\bcase\b|extract|from|\bif\b|\binto\b|\bord\b|union/i", $this_string, $matches3 ) ) ) ) &&
688 ( ( false !== ( bool ) preg_match_all( "/$sqlmatchlist/im", $this_string, $matches4 ) ) ) ) {
689 $this_matches = array();
690 $get_match1 = ( is_array( $matches1 ) ) ? $matches1 : array();
691 $get_match2 = ( is_array( $matches2 ) ) ? $matches2 : array();
692 $get_match3 = ( is_array( $matches3[ 0 ] ) ) ? $matches3[ 0 ] : array();
693 $get_match4 = ( is_array( $matches4[ 0 ] ) ) ? $matches4[ 0 ] : array();
694 $this_matches = array_merge( $get_match1, $get_match2, $get_match3, $get_match4 );
695 $this_matches = array_unique( $this_matches );
696 if ( count( $this_matches ) > 3 ) return true;
697 } elseif ( ( false !== strpos( $this_string, 'union' ) ) && ( false !== strpos( $this_string, 'select' ) ) && false !== ( bool ) preg_match( "/\bfrom\b|\bnull\b|--/i", $this_string ) ) {
698 return true;
699 } elseif ( false !== strpos( $this_string, 'etc/passwd' ) ) {
700 return true;
701 } elseif ( ( false !== strpos( $this_string, 'procedure' ) ) && ( false !== strpos( $this_string, 'analyse' ) ) && ( false !== strpos( $this_string, 'extractvalue' ) ) ) {
702 return true;
703 } elseif ( false !== strpos( $this_string, 'null' ) ) {
704 $nstring = preg_replace( "/[^a-z]/i", '', $this->url_decoder( $this_string ) );
705 if ( false !== ( bool ) preg_match( "/(null){3,}/i", $nstring ) ) {
706 return true;
707 }
708 }
709 $j++;
710 }
711 }
712 return false;
713 }
714 /**
715 * Filter datalists for blacklisted variables
716 *
717 * @return boolean
718 */
719 function datalist( $val, $list = 0 ) {
720 $val = $this->cleanString( 9, $val );
721 if ( !is_numeric( $val ) && empty( $val ) ) return false;
722 if ( empty( $this->_datalist ) || !is_array( $this->_datalist ) ) return false;
723
724 # although we try not to do this, arbitrary blacklisting of certain request variables
725 # cannot be avoided. however I will attempt to keep this list short.
726
727 // Remove whitespace from string
728 $val = preg_replace( "/\s+/i", '', $this->decode_code( str_replace( "'", '', ( $val ) ) ) );
729 $_datalist_tmp = array();
730 $val = $this->decode_code( $val );
731 for ( $x = 0; $x < count( $this->_datalist[ ( int ) $list ] ); $x++ ) {
732 $this_item = $this->decode_code( $this->_datalist[ ( int ) $list ][ $x ] );
733 # Test 1: Hex test
734 if ( false !== strpos( strtolower( pack( "H*", preg_replace( "/[^a-f0-9]/i", '', $val ) ) ), $this_item ) ) {
735 return true;
736 }
737 # Test 2:
738 if ( false !== strpos( strtolower( $val ), $this_item ) || false !== $this->cmpstr( $val, $this_item ) ) {
739 return true;
740 }
741 }
742 return false;
743 }
744 /**
745 * filter the lists
746 * @params $input,
747 * $list,
748 * $desc,
749 * $bantype = boolean,
750 * $severity = string,
751 * $log = boolean,
752 * $reqmatch = boolean,
753 * $injectmatch = boolean *
754 * @return void
755 */
756 function do_blacklists( $input, $list, $desc, $bantype = false, $severity = "High", $log = false, $reqmatch = false, $injectmatch = false ) {
757 if ( $list > 0 && false !== $reqmatch && false !== ( bool ) $this->datalist( $input, $list ) ) $this->karo( $desc . ": " . $input, $bantype, $severity, $log );
758 if ( false !== $injectmatch && false !== $this->injectMatch( $input ) ) $this->karo( "Injection " . $desc . ": " . $input, $bantype, $severity, $log );
759 if ( ( false !== ( bool ) $this->_hard_ban_mode ) &&
760 ( $list == 2 ) && ( !empty( $input ) ) &&
761 ( false !== $this->controlchar_exists( $input ) ) ) {
762 $this->karo( "Control Character Injection " . $desc . ": " . $input, $bantype, "High", $log ); // test POST variables for control characters
763 }
764 }
765 /**
766 * Filter REQUEST_URI
767 *
768 * @return void
769 */
770 public function _REQUEST_SHIELD() {
771
772 $q_str = $this->getQUERY_STRING();
773 $req_type = "Request";
774 # Often part of a preemptive strike package
775 if ( $this->cmpstr( 'wp-links-opml.php', $this->get_filename(), true ) ) {
776 if ( $this->string_prop( $q_str, 2 ) ) {
777 $qs = '?' . $q_str;
778 } else $qs = '';
779 # only for a logged in administrator
780 if ( false === $this->is_wp( false, true ) )
781 $this->karo( "WPScan: non-admin call to wp-links-opml.php" . $qs, ( ( false !== ( bool ) $this->_hard_ban_mode ) ? ( bool ) $this->_banip : false ), 'Low', true );
782 }
783 # if empty then the rest of no interest to us
784 if ( false !== empty( $_REQUEST ) ) return;
785 $_get_server = $_SERVER;
786 $_get_post = $_POST;
787
788 # specific attacks that do not necessarily
789 # involve query_string manipulation
790 $req = $this->getREQUEST_URI();
791 # Apache Struts2 Remote Code Execution
792 preg_match_all( "/redirect|context|opensymphony|dispatcher|httpservletresponse|flush\(|getwriter/i", $req, $matches );
793 if ( is_array( $matches[ 0 ] ) && ( count( $matches[ 0 ] ) > 4 ) ) {
794 $match_count = ( $m = count( $matches[ 0 ] ) > 4 ) ? 4 : $m;
795 for ( $x = 0; $x <= $match_count; $x++ ) {
796 $results .= ( !is_array( $matches[ 0 ][ $x ] ) ) ? $matches[ 0 ][ $x ] . ' ' : '';
797 }
798 $this->karo( "Apache Struts2 RCE: " . $results, true, "Medium", true );
799 }
800
801 if ( false !== $q_str ) {
802 # SQLMAP Prevention
803 preg_match_all( "/union|all|select|script|table|from|schema|where|exec|etc/i", $q_str, $matches );
804 if ( is_array( $matches[ 0 ] ) ) {
805 $mcount = array_unique( $matches[ 0 ] );
806 if ( count( $mcount ) >= 8 ) {
807 $req_type = "(SQLMAP) " . $req_type;
808 $this->karo( "SQLMAP Injection Attempt: " . $q_str, true, "Medium", true );
809 }
810 }
811
812 # Reflected File Download Attack
813 $file_injects = ( string ) $this->_injectors[ 4 ];
814
815 if ( false !== ( bool ) preg_match( "/$file_injects/i", $q_str, $matches ) ) {
816 $match_len = strlen( $matches[ 0 ] );
817 $end_char = substr( $q_str, strpos( $q_str, $matches[ 0 ] ) + $match_len, 1 );
818 if ( ( empty( $end_char ) || false !== ( false === ctype_alpha( $end_char ) ) ) && ( false === ( bool ) preg_match( "/(?:\.|-|_|\/)/i", $end_char ) ) ) {
819 $this->karo( "Reflected File Download (RFD): " . $q_str, true, "High", true );
820 }
821 }
822 # Reflected File Download Attack
823 preg_match_all( '/echo|{ifs}|\||base64|decode|python/i', strtolower( $q_str ), $matches );
824 if ( count( array_unique( $matches[ 0 ] ) ) > 4 ) $this->karo( "Reflected File Download (RFD): " . $q_str, true, "High", true );
825 }
826 # osCommerce / Magento specific exploit
827 if ( false !== strpos( $req, '.php/admin' ) )
828 $this->karo( "osCommerce / Magento Exploit: " . $req, true, "Low", true );
829
830 # Null byte
831 if ( false !== strpos( $req, '\0' ) ) $this->karo( "Null byte: " . $req, true, "Low", true );
832
833 # prevent arbitrary file includes/uploads
834 if ( false !== ( bool ) @ini_get( 'allow_url_include' ) ) {
835 if ( false !== ( bool ) $this->instr_url( $req, false ) ) {
836 preg_match( "/(?:http:|https:|ftp:|file:|php:)/i", $req, $matches );
837 $match_count = count( $matches[ 0 ] );
838 if ( false === stripos( $req, $this->get_http_host() ) && $this->cmpstr( $match_count, 1, true ) ) {
839 $this->karo( "RFI: " . $req, true, "High", true );
840 } elseif ( false !== stripos( $req, $this->get_http_host() ) && count( $matches[ 0 ] ) > 1 ) {
841 $this->karo( "RFI: " . $req, true, "High", true );
842 }
843 }
844 }
845
846 # prevent command injection
847 if ( false !== in_array( "'cmd'", $this->_get_all ) || false !== in_array( "'system'", $this->_get_all ) )
848 $this->karo( "CMD Inject: " . $req, true, "High", true );
849
850 if ( false !== $q_str ) {
851 # Detect HTTP Parameter Pollution
852 # i.e when devs mistakenly use $_REQUEST to return values
853 $dup_check_get = array();
854 $qs_arr = explode( '&', $this->getQUERY_STRING() );
855 for ( $x = 0; $x < count( $qs_arr ); $x++ ) {
856 $this_key = strtolower( $this->decode_code( substr( $qs_arr[ $x ], 0, strpos( $qs_arr[ $x ], '=' ) ), false, true ) );
857 if ( false !== $this->string_prop( $this_key, 1 ) && false === $this->cmpstr( '[]', substr( $this_key, -2 ) ) ) {
858 $dup_check_get[ $x ] = escapeshellarg( str_replace( "'", '', $this_key ) );
859 }
860 }
861 $dup_check_get = array_unique( $dup_check_get );
862 }
863
864 # _POST
865 if ( false !== $this->cmpstr( 'POST', $_get_server[ 'REQUEST_METHOD' ], true ) && false === empty( $_get_post ) ) {
866 # while we're checking _POST, prevent attempts to esculate user privileges in WP
867 if ( ( false !== $this->is_wp() && false !== function_exists( 'is_admin' ) && false === is_admin() ) &&
868 false !== $this->cmpstr( 'admin-ajax.php', $this->get_filename(), true ) ) {
869 if ( false !== in_array( 'default_role', $_get_post ) && false !== $this->cmpstr( 'administrator', $_get_post[ 'default_role' ], true ) )
870 $this->karo( "Privalege Esculation / Admin Bypass: " . $req, true, "High", true );
871 # Prevent the exploit of Unauthenticated Call Any Action or Update Any Option https://bit.ly/2FdoRhP
872 if ( isset( $_get_post[ 'action' ] ) && false !== $this->cmpstr( 'wpgdprc_process_action', $_get_post[ 'action' ], true ) &&
873 isset( $_get_post[ 'security' ] ) && isset( $_get_post[ 'data' ] ) ) {
874 preg_match_all( "/option|value|type|save_setting|append|true|enabled/i", $_get_post[ 'data' ], $matches );
875 $matches = array_unique( $matches[ 0 ] );
876 if ( count( $matches ) == 7 ) {
877 if ( !current_user_can( 'manage_options' ) ) $this->karo( "Unauthenticated Call Any Action or Update Any Option", true, "High", true );
878 }
879 }
880 }
881 if ( !empty( $dup_check_get ) ) {
882 # Start HTTP Parameter Pollution
883 $dup_check_post = array();
884 for ( $x = 0; $x < count( $this->_post_all ); $x++ ) {
885 $this_key = strtolower( $this->decode_code( $this->_post_all[ $x ], false, true ) );
886 if ( $this->string_prop( $this_key, 1 ) && false === $this->cmpstr( '[]', substr( $this_key, -2 ) ) ) {
887 $dup_check_post[ $x ] = $this_key;
888 }
889 }
890 if ( false === empty( $dup_check_post ) )
891 $dup_check_post = array_unique( $dup_check_post );
892
893 # We only test for duplicate keys that appear in both QUERY_STRING and POST global.
894 if ( count( array_intersect( $dup_check_get, $dup_check_post ) ) > 0 ) {
895 if ( false !== ( bool ) $this->_hard_ban_mode ) {
896 $this->karo( "HTTP Parameter Pollution: " . implode( ', ', $dup_check_post ), ( bool ) $this->_banip, 'Medium', true );
897 } else {
898 if ( $this->is_wp() ) {
899 wp_safe_redirect( get_bloginfo( 'url' ) );
900 exit;
901 } else {
902 header( "Location: " . $this->getURL() );
903 exit();
904 }
905 }
906 }
907 }
908 }
909 # WP Author Discovery
910 if ( false !== strpos( $req, '?author=' ) ) {
911 $this->karo( "Authorised user discovery scan attempt", true, 'Low', true );
912 }
913 # WP Admin Authentication Bypass Attach
914 if ( false !== strpos( $req, '?up_auto_log=' ) || false !== strpos( $req, '&up_auto_log=' ) ) {
915 $this->karo( "Authentication Bypass Attack: " . $req, true, 'Low', true );
916 }
917 # WP DoS Mitigation CVE-2018-6389
918 if ( false !== ( bool ) preg_match( '/^load-(?:scripts|styles).php$/i', $this->get_filename() ) ) {
919 # Disable concatenation of JS and CSS files
920 if ( false !== version_compare( phpversion(), '7.1', '<' ) && !defined( 'CURL_HTTP_VERSION_2_0' ) ) {
921 # *chances* are high this server does not support HTTP2, s
922 # so manually prevent concatenation of scripts
923 if ( !defined( 'CONCATENATE_SCRIPTS' ) ) define( 'CONCATENATE_SCRIPTS', false );
924 }
925 # Now filter attack attempts
926 if ( isset( $_REQUEST[ 'load' ] ) ) {
927 $query_len = strlen( $_REQUEST[ 'load' ][ 0 ] );
928 if ( $query_len > 1000 )
929 $this->karo( "CVE-2018-6389 DoS Attack: Query Length = " . $query_len, true, 'Medium', true );
930 }
931 }
932
933 # this occurence of these many slashes etc are always an attack attempt
934 $limit = 25;
935 if ( ( substr_count( $req, '/' ) > $limit ) || ( substr_count( $req, '\\' ) > $limit ) || ( substr_count( $req, '|' ) > $limit ) ) {
936 $this->karo( "Irregular Request: " . $req, false, 'Low', true );
937 }
938
939 preg_match_all( "/recently_products|ids|\[added_at\]|\[product_id\]|\[from\]/i", $q_str, $matches );
940 if ( is_array( $matches[ 0 ] ) ) {
941 $match_list = array_unique( $matches[ 0 ] );
942 $match_list_count = count( $match_list );
943 if ( $this->cmpstr( $match_list_count, 5 ) ) {
944 $req_type = "(SQLMAP) " . $req_type;
945 }
946 }
947 preg_match_all( "/opinionstage|content|login|callback|page|success/i", $q_str, $matches );
948 if ( is_array( $matches[ 0 ] ) ) {
949 $match_list = array_unique( $matches[ 0 ] );
950 $match_list_count = count( $match_list );
951 if ( $this->cmpstr( $match_list_count, 6 ) ) {
952 $req_type = "OpinionStage Version 2.5.0 [Depreciated] Plugin " . $req_type;
953 }
954 }
955 # finally run the black lists one more time
956 $this->do_blacklists( $this->getQUERY_STRING(), 1, $req_type, true, "High", true, true, true );
957 }
958 /**
959 * Filter GET array
960 *
961 * @return void
962 */
963 function querystring_filter( $val, $key ) {
964 $this->_get_all[] = $this->decode_code( $key, true );
965 if ( false !== ( bool ) $this->string_prop( $val, 1 ) ) {
966 $val = strtolower( $this->decode_code( $val ) );
967 $this->do_blacklists( $val, 1, "Request", true, "High", true, true, true );
968 }
969 }
970 /**
971 * Filter GET variables
972 *
973 * @return void
974 */
975 public function _QUERYSTRING_SHIELD() {
976 if ( false !== empty( $_REQUEST ) || false === $this->cmpstr( 'GET', $_SERVER[ 'REQUEST_METHOD' ], true ) || false === ( bool ) $this->string_prop( $this->getQUERY_STRING(), 1 ) ) {
977 return; // of no interest to us
978 } else {
979 # run $_GET through filters
980 array_walk_recursive( $_GET, array(
981 $this,
982 'querystring_filter'
983 ) );
984 }
985 return;
986 }
987 /**
988 * Filter _POST array
989 *
990 * @return void
991 */
992 function post_filter( $val, $key ) {
993
994 # catch attempts to insert malware
995 if ( false !== strpos( $val, $this->htmlentities_decode_safe( 'array_diff_' ) ) && ( false !== strpos( $val, "system" ) ||
996 false !== strpos( $val, "cmd" ) ) && $this->substri_count( $val, "array(" ) > 1 )
997 $this->karo( "Malware Injection Attempt: " . $val, true, "High", true );
998 # Attempts to pop a shell
999 preg_match_all( "/php|system\(|import|python|connect|\?\>/i", $val, $matches );
1000 if ( is_array( $matches[ 0 ] ) ) {
1001 $match_list = array_unique( $matches[ 0 ] );
1002 if ( count( $match_list ) > 4 ) {
1003 $s = strpos( $val, $match_list[ 0 ] );
1004 $f = strlen( $val ) - $s;
1005 $val = substr( $val, $s, $f );
1006 $this->karo( "Shell Inject: " . $val, true, "High", true );
1007 }
1008 }
1009 $matches = array();
1010 # Satori Botnet
1011 preg_match_all( "/wget http|:\/\/|->|tmp\/r|-O|;sh/i", $val, $matches );
1012 if ( is_array( $matches[ 0 ] ) ) {
1013 $match_list = array_unique( $matches[ 0 ] );
1014 $match_list_count = count( $match_list );
1015 if ( $this->cmpstr( $match_list_count, 6 ) ) {
1016 $val = $this->cleanString( 6, $this->remove_comments( $val ) );
1017 $this->karo( "Botnet :: " . $val, true, 'Low', true );
1018 }
1019 }
1020 preg_match_all( "/php|echo|base64|system\(|\_GET|cmd/i", $val, $matches );
1021 if ( is_array( $matches[ 0 ] ) ) {
1022 $match_list = array_unique( $matches[ 0 ] );
1023 if ( count( $match_list ) > 5 ) {
1024 $s = strpos( $val, $match_list[ 0 ] );
1025 $f = strlen( $val ) - $s;
1026 $val = substr( $val, $s, $f );
1027 $val = $this->cleanString( 9, $this->remove_comments( $val ) );
1028 $this->karo( "Shell Inject: " . $val, true, "High", true );
1029 }
1030 }
1031 $matches = array();
1032
1033 if ( preg_match( "/@eval|base64/i", $val ) ) {
1034 $filtval = preg_replace( "/[^{}a-z0-9_?,();=\*@\[\]\$\/\-]/i", '', strtolower( $val ) );
1035 preg_match_all( "/@eval|base64\_|{|\]\(|\_post|\}\[|\)\;|\/\*|\*\//i", $filtval, $matches );
1036 if ( is_array( $matches[ 0 ] ) ) {
1037 $match_list = array_unique( $matches[ 0 ] );
1038 if ( count( $match_list ) > 4 ) {
1039 $filtval = $this->cleanString( 6, $this->remove_comments( $filtval ) );
1040 $this->karo( "Shell Inject: " . $filtval, true, "High", true );
1041 }
1042 }
1043 $matches = array();
1044
1045 preg_match_all( "/@eval|\_magic\_quotes\_gpc|stripslashes|\_post\[chr\(/i", $filtval, $matches );
1046 if ( is_array( $matches[ 0 ] ) ) {
1047 $match_list = array_unique( $matches[ 0 ] );
1048 $match_list_count = count( $match_list );
1049 if ( $this->cmpstr( $match_list_count, 4 ) ) {
1050 $val = $this->cleanString( 6, $this->remove_comments( $val ) );
1051 $this->karo( "Malicious Data Exfiltrations Attempt :: " . $val, true, "High", true );
1052 }
1053 }
1054 }
1055 $matches = array();
1056
1057 preg_match_all( "/\_server|ini\_set|\_magic\_quotes\_runtime\(0|php\_uname|php\_self|print|die|posix\_/i", strtolower( $val ), $matches );
1058 if ( count( array_unique( $matches[ 0 ] ) ) > 4 ) {
1059 $val = $this->cleanString( 6, $this->remove_comments( $val ) );
1060 $this->karo( "Malicious Data Exfiltrations Attempt :: " . $val, true, "High", true );
1061 }
1062 $matches = array();
1063
1064 preg_match_all( "/(?:script|type|text|javascript|http|pastebin)/i", $val, $matches );
1065 $this_match = count( array_unique( $matches[ 0 ] ) );
1066 if ( $this->cmpstr( $this_match, 6 ) ) {
1067 $trimval = trim( substr( $val, 0, 20 ) );
1068 $this->karo( "Malware Inject: Attempt to inject malware via Pastebin " . $trimval, true, "High", true );
1069 }
1070
1071 # Load the keys into an array
1072 $this->_post_all[] = strtolower( $this->decode_code( $key, true ) );
1073
1074 # Finally, the blacklist
1075 $this->do_blacklists( $this->decode_code( $val ), 2, "POST Request", true, "High", true, true, false );
1076 }
1077 /**
1078 * Strip comment brackets
1079 *
1080 * @return string
1081 */
1082 function remove_comments( $str ) {
1083 while( strpos( $str, "/*" ) ) {
1084 $s = strpos( $str, "/*" );
1085 $f = strpos( $str, "*/" );
1086 if ( $f > $s ) $remval = substr( $str, $s, ( $f - $s ) + 2 );
1087 $str = str_replace( $remval, "", $str );
1088 }
1089 return $str;
1090 }
1091 /**
1092 * Filter _POST super global
1093 *
1094 * @return void
1095 */
1096 public function _POST_SHIELD() {
1097 if ( false === $this->cmpstr( 'POST', $_SERVER[ 'REQUEST_METHOD' ], true ) )
1098 return; // of no interest to us
1099 # _POST content-length should be longer than 0
1100 if ( ( false !== ( bool ) $this->_adv_mode || false !== ( bool ) $this->_post_filter_mode ) ) {
1101 if ( count( $_POST, COUNT_RECURSIVE ) >= 10000 )
1102 $this->karo( "_POST DoS Attack", ( bool ) $this->_banip, "High", true ); // very likely a denial of service attack
1103 }
1104 array_walk_recursive( $_POST, array(
1105 $this,
1106 'post_filter'
1107 ) );
1108 if ( false !== $this->is_wp() ) {
1109 if ( false !== ( bool ) $this->options[ 'advanced_mode' ] || false !== ( bool ) $this->_adv_mode ) {
1110 if ( $this->cmpstr( 'xmlrpc.php', $this->get_filename(), true ) ) {
1111 if ( isset( $HTTP_RAW_POST_DATA ) ) { // this is set above wp-load.php
1112 # deal with this dangerous global
1113 if ( $this->string_prop( $HTTP_RAW_POST_DATA ) && false !== $this->datalist( $this->decode_code( $HTTP_RAW_POST_DATA ), 2 ) ) $this->karo( "HTTP_RAW_POST_DATA contains attack code", true, "High", true );
1114 # check it is the correct XML format
1115 preg_match_all( "/methodCall|methodName|params|string|value|<|>/i", $HTTP_RAW_POST_DATA, $matches );
1116 $matches = array_unique( $matches[ 0 ] );
1117 if ( false !== $this->string_prop( $HTTP_RAW_POST_DATA ) && ( 7 > count( $matches ) ) ) {
1118 # content is not XML
1119 $this->karo( "HTTP_RAW_POST_DATA: Not valid XML", false, "Low", true );
1120 }
1121 }
1122 if ( defined( 'XMLRPC_REQUEST' ) ) {
1123 add_filter( 'authenticate', array( $this, 'xml_rpc_auth'), 20, 3 );
1124 }
1125 }
1126 }
1127 }
1128 }
1129 /**
1130 * Filter Wordpress login requests
1131 *
1132 * @return void
1133 */
1134 public function _LOGIN_SHIELD() {
1135 if ( false !== $this->is_wp() ) {
1136 if ( false !== ( bool ) $this->options[ 'advanced_mode' ] || false !== ( bool ) $this->_adv_mode ) {
1137 add_action( 'wp_logout', array( $this, 'on_logout' ) );
1138 add_action( 'wp_login', array( $this, 'on_login' ), 10, 2 );
1139 if ( false !== $this->cmpstr( 'POST', $_SERVER[ 'REQUEST_METHOD' ], true ) ) {
1140 if ( ( isset( $_POST[ 'log' ] ) && isset( $_POST[ 'pwd' ] ) ) ) {
1141 $this_user = $_POST[ 'log' ];
1142 $this_pass = $_POST[ 'pwd' ];
1143 if ( false !== $this->cmpstr( $this_user, '\\' ) && ( false !== strpos( $this_pass, '||' ) ) )
1144 $this->karo( "SQLi Without Quotes Injection User: '" . $this_user . "' Pass: '" . substr( $this_pass, 0, 4 ) . "'", true, "Low", true );
1145 $this->check_usernames( $this_user );
1146 }
1147 }
1148 }
1149 }
1150 return;
1151 }
1152 /**
1153 * Checks password is correct
1154 *
1155 * @params $check, $password, $hash, $user_id
1156 *
1157 * @return boolean
1158 */
1159 function pass_check( $check, $password, $hash, $user_id ) {
1160 if ( false === $check ) {
1161 if ( isset( $_POST[ 'log' ] ) ) {
1162 $this_user = $_POST[ 'log' ];
1163 $this->flood_check( $this->get_ip(), $this_user, 'WP Password', 10, 12 );
1164 }
1165 } else {
1166 $this->ip_hasher( true );
1167 }
1168 return $check;
1169 }
1170 /**
1171 * On succesful logging in, set admin IP as safe
1172 *
1173 * @return void
1174 */
1175 function on_login( $user_login, $user ) {
1176 # this only triggers on successful login
1177 # never ban the ip of the administrator
1178 if ( isset( $this->options[ 'admin_ip' ] ) ) {
1179 if ( isset( $user->caps[ 'administrator' ] ) && false !== ( bool )$user->caps[ 'administrator' ] ) {
1180 # set the admin ip to safe even before
1181 # admin accesses pareto security settings
1182 $this->update_admin_ip( $this->get_ip() );
1183 } else $this->update_admin_ip( '' );
1184 }
1185 }
1186 /**
1187 * On logging out, remove admin IP
1188 *
1189 * @return void
1190 */
1191 function on_logout() {
1192 if ( false !== $this->is_wp( false, true ) ) {
1193 if ( isset( $this->options[ 'admin_ip' ] ) ) {
1194 // unset admin IP
1195 $this->update_admin_ip( '' );
1196 }
1197 }
1198 }
1199 /**
1200 * Add or remove admin ip address from DB
1201 *
1202 * @return void
1203 */
1204 function update_admin_ip( $ip ) {
1205 update_option( $this->settings_field, array( // set defaults
1206 'advanced_mode' => $this->_adv_mode,
1207 'hard_ban_mode' => $this->_hard_ban_mode,
1208 'tor_block' => $this->_tor_block,
1209 'email_report' => $this->_email_report,
1210 'ban_mode' => $this->_ban_mode,
1211 'safe_list' => ( isset( $this->_domain_list ) ? $this->_domain_list : '' ),
1212 'admin_ip' => $ip
1213 ) );
1214 $this->options = get_option( $this->settings_field );
1215 }
1216 /**
1217 * Check if username exists in database
1218 *
1219 * @return boolean
1220 */
1221 function check_usernames( $this_user, $threshold = 3, $hard_ban = 5 ) {
1222 $get_users = array();
1223 $blogusers = get_users( array(
1224 'fields' => array(
1225 'user_login',
1226 'user_nicename',
1227 'display_name'
1228 )
1229 ) );
1230 foreach ( $blogusers as $user ) {
1231 $get_users[] = $user->user_login;
1232 $get_users[] = $user->user_nicename;
1233 $get_users[] = $user->display_name;
1234 }
1235 $get_users = array_unique( $get_users );
1236 # will report false if XML or if password is not empty
1237 $pw_test = ( bool ) ( isset( $_POST[ 'pwd' ] ) && empty( $_POST[ 'pwd' ] ) );
1238 # if user is correct, also check pass, else return false
1239 if ( in_array( $this_user, $get_users ) && false === $pw_test ) {
1240 add_filter( 'check_password', array( $this, 'pass_check' ), 10, 4 );
1241 } else {
1242 # with a DoS attack, the user could change on every request
1243 # log the ip addresses of each request
1244 $this->flood_check( $this->get_ip(), $this_user, ( false === $pw_test ) ? 'WP User' : 'Empty WP Password', $threshold, $hard_ban );
1245 }
1246 }
1247 function ip_hasher( $input = false ) {
1248 # if XML-RPC user is correct
1249 $this->_ip_array = get_option( $this->ip_hash_list );
1250 $key = sha1( $this->get_ip() );
1251 if ( false !== $input ) {
1252 if ( in_array( $key, $this->_ip_array ) ) {
1253 # if in array, remove
1254 unset( $this->_ip_array[ $key ] );
1255 }
1256 }
1257 update_option( $this->ip_hash_list, $this->_ip_array );
1258 }
1259 /**
1260 * Check if username is registered
1261 * @params $user, $username, $pass
1262 * @return string
1263 */
1264 function xml_rpc_auth( $user, $username, $pass ) {
1265 # this filter runs before the shields so will filter
1266 # incorrect usernames before _SPIDER_SHIELD filters
1267
1268 # filter username through blacklists
1269 $post_data = array();
1270 $post_data = array( $username, $pass );
1271 array_walk_recursive( $post_data, array(
1272 $this,
1273 'post_filter'
1274 ) );
1275 # Deal with invalid logins
1276 if ( array_key_exists( 'invalid_username', $user->errors ) ||
1277 array_key_exists( 'incorrect_password', $user->errors ) ) {
1278 $this->flood_check( $this->get_ip(), $username, 'XML-RPC User', 2, 3 );
1279 } else {
1280 $this->ip_hasher( true );
1281 $this->_spider_bypass = true;
1282 }
1283 return $user;
1284 }
1285 /**
1286 * Check DB for old failed IP hashes
1287 *
1288 * @return void
1289 */
1290 function iphash_db_cleanup() {
1291 $time_now = time();
1292 $this->_ip_array = get_option( $this->ip_hash_list );
1293 if ( empty( $this->_ip_array ) ) return;
1294 foreach( $this->_ip_array as $key => $val ) {
1295 $this_timestamp = ( int ) $this->_ip_array[ $key ][ 1 ];
1296 $n = ( int ) $this->_ip_array[ $key ][ 0 ];
1297 if ( $n > ( int ) $this->_hard_ban_count ) {
1298 # if time lapsed is greater than 7 days
1299 if ( ( $time_now < $this_timestamp ) > ( ( ( int ) $this->_ban_time / 24 ) * 168 ) ) {
1300 unset( $this->_ip_array[ $key ] );
1301 } else {
1302 # if count higher than threshold, htaccess is not working
1303 # so leave hash in db
1304 continue;
1305 }
1306 }
1307 # if timestamp is older than $this->_ban_time then remove entry
1308 if ( ( $time_now - ( int ) $this->_ban_time ) > $this_timestamp ) unset( $this->_ip_array[ $key ] );
1309 }
1310 update_option( $this->ip_hash_list, $this->_ip_array );
1311 return;
1312 }
1313 /**
1314 * Flood controls for incorrect usernames
1315 *
1316 * @return void
1317 */
1318 function flood_check( $ip, $uname = '', $type = '', $threshold = 5, $hard_ban = 7 ) {
1319 $uname = esc_html( $uname );
1320 if ( empty( $ip ) ) $ip = $this->get_ip();
1321 $iphash = sha1( $ip ); // hash the ip
1322 $time_now = time();
1323
1324 if ( $hard_ban <> $this->_hard_ban_count ) $this->_hard_ban_count = $hard_ban;
1325 $this->_ip_array = get_option( $this->ip_hash_list );
1326 if ( !empty( $this->_ip_array ) && false !== array_key_exists( $iphash, $this->_ip_array ) ) { // check hash in database
1327 $current_count = ( int ) $this->_ip_array[ $iphash ][ 0 ];
1328 $time_stamp = ( int ) $this->_ip_array[ $iphash ][ 1 ];
1329 # if 24 hours hasn't lapsed
1330 if ( ( $time_now - $this->_ban_time ) < $time_stamp ) {
1331 # if threshold passed then return 403
1332 if ( $current_count >= $threshold ) {
1333 # repeat failed login, restart timestamp
1334 # if $current_count > $this->_hard_ban_count within 24 hours
1335 # permanent IP ban
1336 if ( $current_count >= $this->_hard_ban_count ) {
1337 # this is a flood attack
1338 # update the DB
1339 $this->_ip_array[ $iphash ][ 0 ] = $current_count + 1;
1340 update_option( $this->ip_hash_list, $this->_ip_array );
1341 # ban to htaccess and 403
1342 $this->karo( 'User/Password Cracking Attack :: [ ' . $current_count . ' ] Failed Login Attempts', true, "Medium", true );
1343 } else {
1344 # log only until $threshold, then 403 until hard ban count
1345 $this->_ip_array[ $iphash ][ 0 ] = $current_count + 1;
1346 update_option( $this->ip_hash_list, $this->_ip_array );
1347 if ( $current_count <= ( $threshold - 1 ) ) {
1348 $this->karo( 'Error: ' . $type . ': "' . $uname . '" [ ' . $current_count . ' ] Failed Login Attempts', false, "Safe" , false );
1349 } else $this->send200(); // tricks flooders into continuing
1350 }
1351 }
1352 $this->_ip_array[ $iphash ][ 0 ] = $current_count + 1;
1353 update_option( $this->ip_hash_list, $this->_ip_array );
1354 } else {
1355 # 24 hours has lapsed so start the count again
1356 $this->_ip_array[ $iphash ] = array( 0 => 1, 1 => $time_now );
1357 update_option( $this->ip_hash_list, $this->_ip_array );
1358 }
1359 } else {
1360 # add this entry to DB
1361 $this->_ip_array[ $iphash ] = array( 0 => 1, 1 => $time_now );
1362 update_option( $this->ip_hash_list, $this->_ip_array );
1363 }
1364 }
1365 /**
1366 * Filter HTTP_HOST
1367 *
1368 * @return void
1369 */
1370 public function _HTTPHOST_SHIELD() {
1371 if ( isset( $_SERVER[ 'HTTP_HOST' ] ) ) {
1372 $this_http_host = trim( strtolower( $_SERVER[ 'HTTP_HOST' ] ) );
1373
1374 # while not optimal, there is nothing below
1375 # that can right this issue
1376 if ( empty( $this_http_host ) ) return;
1377
1378 # check for injections via HTTP_HOST
1379 $this->do_blacklists( $this_http_host, 0, "HOST", true, "High", true, false, true );
1380
1381 # low level sanitise the host
1382 $http_host = $this->host_check( strtolower( $this_http_host ) );
1383
1384 # Wordpress Host Check
1385 if ( false !== $this->is_wp() ) {
1386 if ( false !== ( bool ) $this->_adv_mode ) {
1387 # WP RCE HOST Attack
1388 preg_match_all( "/xenial|directory|usr|spool|run/i", $http_host, $matches );
1389 if ( is_array( $matches[ 0 ] ) ) {
1390 $match_list = array_unique( $matches[ 0 ] );
1391 if ( count( $match_list ) > 3 )
1392 $this->karo( "WP RCE HOST Attack: " . $http_host, ( bool ) $this->_banip, "High", true );
1393 }
1394 }
1395 }
1396 # Get URL from safe list
1397 if ( empty( $safelist_url ) ) return;
1398 # Set safe domain
1399 $this->set_safe_domain();
1400
1401 # if IPaddress:port, strip the port number
1402 $this_http_host = $this->ip_filter( $this_http_host );
1403 if( function_exists( 'filter_var' ) && defined( 'FILTER_VALIDATE_IP' ) ) {
1404 if ( false !== filter_var( $this_http_host, FILTER_VALIDATE_IP ) ) {
1405 # the SERVER_NAME is an IP so check it against SERVER_ADDR
1406 if ( false === $this->is_server( $this_http_host ) ) {
1407 $req = $this->getREQUEST_URI();
1408 // TODO: need to check if there is a domain name
1409 if ( false !== $this->cmpstr( 'CONNECT', $_SERVER[ 'REQUEST_METHOD' ], true ) ) {
1410 $this->karo( $server_ip . " :: Incorrect Server IP / possible proxying attempt - Should be " . $_SERVER[ 'SERVER_ADDR' ], true, 'Medium', true, ( isset( $this->safe_host ) ? $this->safe_host : $this_http_host ) );
1411 }
1412 }
1413 } else {
1414 # check HTTP HOST against the safe list
1415 # to prevent DNS rebinding
1416 $n = false;
1417 foreach( $safelist_url as $url ) {
1418 # check against whitelist
1419 $url = trim( $url );
1420 if ( strpos( $url, $this_http_host ) || ( false !== $this->cmpstr( $url, substr( $this_http_host , - strlen( $url ) ), true ) ) ) {
1421 $n = true;
1422 break;
1423 }
1424 # check if is server ip
1425 if ( false !== $this->is_server( $this_http_host ) ) {
1426 $n = true;
1427 break;
1428 }
1429 }
1430 if ( !in_array( $this_http_host, $safelist_url ) && $n === false ) {
1431 if ( false !== $this->cmpstr( 'HEAD', $_SERVER[ 'REQUEST_METHOD' ], true ) ) $this->karo( $this_http_host . " :: DNS rebinding attempt - Should be " . $this->safe_host . ' or ' . $this->get_serverip(), false, 'Medium', true, $this->safe_host );
1432 }
1433 }
1434 }
1435 }
1436 }
1437 function set_safe_domain() {
1438 $safelist_url = array();
1439 if ( isset( $this->options[ 'safe_list' ] ) && !empty( $this->options[ 'safe_list' ] ) ) {
1440 $server_ip = $this->get_servername(); // set something
1441 $safelist_url = array();
1442 $this_sname = $this->host_check( $this->options[ 'safe_list' ] );
1443
1444 if ( !is_array( $this_sname ) ) {
1445 # check there isn't two urls on separate lines.
1446 if ( false !== strpos( $this_sname, "\n" ) ) {
1447 $safelist_url = explode( "\n", $this_sname );
1448 } else {
1449 $safelist_url[] = $this_sname;
1450 }
1451 }
1452 $this->safe_host = $safelist_url[ 0 ];
1453 }
1454 }
1455 function ip_filter( $ip ) {
1456 $ip_port_pattern = '^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.)
1457 {3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]):(?:
1458 6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}
1459 |[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$';
1460 $ip_port_pattern = preg_replace( "/[\s\r\n]/i", '', $ip_port_pattern );
1461 if ( false !== ( bool ) preg_match( "/$ip_port_pattern/i", $ip ) ) {
1462 return trim( substr( $ip, 0, strpos( $ip, ':' ) ) );
1463 }
1464 return $ip;
1465 }
1466 function is_ip_address( $ip ) {
1467 $ip_pattern = '^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.)
1468 {3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$';
1469 $ip_pattern = preg_replace( "/[\s\r\n]/i", '', $ip_pattern );
1470
1471 $test_ip_chars = preg_replace( "/\.|:/i", '', $ip ); // leave numeric only
1472
1473 if ( false !== is_numeric( $test_ip_chars ) && false !== ( bool ) preg_match( "/$ip_pattern/i", $ip ) ) {
1474 return true;
1475 }
1476 return false;
1477 }
1478 /**
1479 * Filter array
1480 * @params $val, $key
1481 * @return void
1482 */
1483 function cookie_filter( $val, $key ) {
1484 $this->do_blacklists( $this->decode_code( $key ), 1, "Cookie", true, "High", true, true, true );
1485 $this->do_blacklists( $this->decode_code( $val ), 1, "Cookie", true, "High", true, true, true );
1486 }
1487 /**
1488 * Filter _COOKIE super global
1489 * @params $val, $key
1490 * @return void
1491 */
1492 public function _COOKIE_SHIELD() {
1493 if ( false !== empty( $_COOKIE ) )
1494 return; // of no interest to us
1495 array_walk_recursive( $_COOKIE, array(
1496 $this,
1497 'cookie_filter'
1498 ) );
1499 }
1500 /**
1501 * Filter USER-AGENT
1502 *
1503 * @return void
1504 */
1505 public function _SPIDER_SHIELD() {
1506 $user_agent = strtolower( $this->decode_code( $_SERVER[ 'HTTP_USER_AGENT' ], true ) );
1507 if ( false !== ( bool ) $this->string_prop( $user_agent, 1 ) ) {
1508 # Shellshock
1509 preg_match_all( "/echo|\(\)|\;\}\;|\/bin\/bash|-c|uname|-i|>|md5sum/i", $user_agent, $matches );
1510 if ( is_array( $matches[ 0 ] ) ) {
1511 $match_list = array_unique( $matches[ 0 ] );
1512 if ( count( $match_list ) > 4 ) {
1513 $s = strpos( $user_agent, $match_list[ 0 ] );
1514 $f = strlen( $user_agent ) - $s;
1515 $user_agent = substr( $user_agent, $s, $f );
1516 $this->karo( "USER-AGENT (Shellshock): " . $user_agent, true, "High", true );
1517 }
1518 }
1519
1520 # Mandatory filtering
1521 $this->do_blacklists( $user_agent, 3, "USER-AGENT", ( ( false !== ( bool ) $this->_adv_mode ) ? true : false ), 'Medium', true, true, true );
1522
1523 # Only if in Hard Ban Mode
1524 # xml-rpc
1525 if ( false === $this->_spider_bypass ) {
1526 $this_ip = $this->get_ip();
1527 if ( false !== $this->_adv_mode && false !== ( bool ) $this->_hard_ban_mode ) {
1528 if ( false !== strpos( $user_agent, 'mozilla' ) ) return;
1529 # Allow unsupported user-agents for XML-RPC
1530 if ( false !== $this->cmpstr( 'POST', $_SERVER[ 'REQUEST_METHOD' ], true ) ) {
1531 if ( false !== $this->cmpstr( 'xmlrpc.php', $this->get_filename(), true ) ) return;
1532 }
1533 # this allows a user added IP address to bypass the user-agent white list
1534 if ( isset( $this->options[ 'safe_list' ] ) && !empty( $this->options[ 'safe_list' ] ) ) {
1535 $ulist = explode( "\n", $this->host_check( $this->options[ 'safe_list' ] ) );
1536 if ( is_array( $ulist ) ) {
1537 foreach( $ulist as $val ) {
1538 if ( ( false !== $this->check_ip( $val, true ) ) && false !== $this->cmpstr( $val, $this_ip ) ) {
1539 return;
1540 }
1541 }
1542 }
1543 }
1544 # Only allow whitelisted user-agents
1545 $is_admin = ( isset( $this->options[ 'admin_ip' ] ) && false !== $this->cmpstr( $this_ip, $this->options[ 'admin_ip' ] ) ) ? true: false;
1546 if ( false !== $is_admin || ( false === $this->is_server() &&
1547 false === $this->cmpstr( $user_agent, "''" ) &&
1548 false === ( bool ) $this->datalist( $user_agent, 4 ) ) ) { // check if user-agent is whitelisted
1549 $this->set_safe_domain();
1550 $thisdomain = $this->get_http_host();
1551 if ( false !== $this->is_ip_address( $thisdomain ) ) {
1552 $thisdomain = ( isset( $this->safe_host ) ) ? $this->safe_host : $thisdomain;
1553 }
1554 if ( false !== $this->cmpstr( 'POST', $_SERVER[ 'REQUEST_METHOD' ], true ) ) { // if request is POST then add post variables to log
1555 $this_post = $this->flatten( $_POST );
1556 $post_str = '';
1557 foreach( $this_post as $key => $val ) {
1558 if ( !is_array( $key ) && !is_array( $val ) ) $post_str .= $post_str . $key . '=' . $val . ',';
1559 }
1560 $user_agent = $user_agent . " \$_POST: " . ( ( strlen( $post_str ) > 100 ) ? substr( $post_str, 0, 120 ) . "..." : $post_str );
1561 }
1562 if ( ( bool ) false !== $this->_banip ) {
1563 $this->karo( " Crawler/USER-AGENT: " . $user_agent, true, "Low", true, $thisdomain );
1564 }
1565 } else return;
1566 }
1567 }
1568 return;
1569 }
1570 }
1571 function flatten( $array, $prefix = '' ) {
1572 $result = array();
1573 foreach( $array as $key => $value ) {
1574 if ( is_array( $value ) ) {
1575 $result = $result + $this->flatten( $value, $prefix . $key . '.' );
1576 }
1577 else {
1578 $result[ $prefix . $key ] = $value;
1579 }
1580 }
1581 return $result;
1582 }
1583 /**
1584 * Strip port number from string
1585 *
1586 * @return string
1587 */
1588 function remove_ports( $host ) {
1589 $url_parts = parse_url( $host );
1590 if ( isset( $url_parts[ 'host' ] ) && strlen( $url_parts[ 'host' ] ) > 4 && isset( $url_parts[ 'port' ] ) && strlen( $url_parts[ 'port' ] ) > 0 ) {
1591 return $url_parts[ 'host' ];
1592 } else return $host;
1593 }
1594 /**
1595 * strip scheme from array variables
1596 * @param $host_array
1597 * @return array
1598 */
1599 function fix_hosts( $host_array ) {
1600 $updated_host = array();
1601 if ( !is_array( $host_array ) ) {
1602 return preg_replace( "/https|http|:\/\//i", "", $this->remove_ports( $host_array ) );
1603 }
1604 for ( $x = 0; $x < count( $host_array ); $x++ ) {
1605 $a = $host_array[ $x ];
1606 $a = str_replace( 'https://', '', $a );
1607 $a = str_replace( 'http://', '', $a );
1608 if ( $this->cmpstr( strlen( $a ), 4 ) && $this->cmpstr( $a, 'www.', true ) ) $a = '';
1609 $updated_host[ $x ] = $this->remove_ports( $a );
1610 }
1611 return array_unique( $updated_host );
1612 }
1613 /**
1614 * Filter HTTP_HOST
1615 * @param $domain_list
1616 * @return mixed
1617 */
1618 function host_check( $domain_list ) {
1619 $checked_list = '';
1620 $checked_list = array();
1621 if ( is_array( $domain_list ) ) {
1622 for ( $x = 0; $x < count( $domain_list ); $x++ ) {
1623 $y = $domain_list[ $x ];
1624 if ( false !== filter_var( $y, FILTER_SANITIZE_URL ) ) $y = filter_var( $y, FILTER_SANITIZE_URL );
1625 if ( false !== $this->check_ip( $y, true ) ) $y = filter_var( $y, FILTER_VALIDATE_IP );
1626 }
1627 }
1628 $domain_list = $this->fix_hosts( $domain_list );
1629 if ( is_array( $domain_list ) ) {
1630 return implode( "\n", $domain_list );
1631 } else return $domain_list;
1632 }
1633 /**
1634 * check for URL in $string
1635 * @param $string
1636 * @return array
1637 */
1638 function instr_url( $string, $domain_only = true ) {
1639 $urls = array();
1640 $dlist = ( false !== $domain_only ) ? explode( "\n", $string ) : $string;
1641 foreach ( $dlist as $domain ) {
1642 if ( false !== $domain_only ) {
1643 $domain = ( false === strpos( $string, '://' ) ) ? 'https://' . $domain : $domain;
1644 $domain = preg_replace( "/[\s]/i", " ", $domain );
1645 }
1646 preg_match_all( "/(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])/i", $domain, $matches );
1647 if ( count( $matches[ 0 ] ) > 0 ) {
1648 $domain = $matches[ 0 ][ 0 ];
1649 if ( false !== $domain_only ) {
1650 $this_match = $domain;
1651 $urls[] = $this_match;
1652 if ( false === strpos( $this_match, "www." ) ) $urls[] = "www." . $this_match;
1653 } else $urls[] = $domain;
1654 }
1655 }
1656 return array_values( array_unique( $urls ) );
1657 }
1658 /**
1659 * check if string ends in .php
1660 * @param $fname
1661 * @return boolean
1662 */
1663 function checkfilename( $fname ) {
1664 if ( false === empty( $fname ) && ( $this->cmpstr( $this->substri_count( $fname, '.php' ), 1 ) && false !== $this->cmpstr( '.php', substr( $fname, -4 ), true ) ) ) {
1665 return true;
1666 } else
1667 return false;
1668 }
1669 function get_real_path( $path ) {
1670 if ( function_exists( 'realpath' ) ) return realpath( $path );
1671 $path = str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path );
1672 $parts = array_filter(explode( DIRECTORY_SEPARATOR, $path ), 'strlen' );
1673 $absolutes = array();
1674 foreach ( $parts as $part ) {
1675 if ( '.' == $part ) continue;
1676 if ( '..' == $part ) {
1677 array_pop( $absolutes );
1678 } else {
1679 $absolutes[] = $part;
1680 }
1681 }
1682 return implode( DIRECTORY_SEPARATOR, $absolutes );
1683 }
1684 /**
1685 * set the current file name, SCRIPT_FILENAME, PHP_SELF
1686 *
1687 * @return string
1688 */
1689 function get_filename() {
1690 $filename = '';
1691 $_get_server = $_SERVER;
1692 $filename = ( ( ( strlen( @ini_get( 'cgi.fix_pathinfo' ) ) > 0 ) && ( false === ( bool ) @ini_get( 'cgi.fix_pathinfo' ) ) ) ||
1693 ( false === isset( $_get_server[ 'SCRIPT_FILENAME' ] ) && isset( $_get_server[ 'PHP_SELF' ] ) &&
1694 false !== $this->string_prop( basename( $_get_server[ 'PHP_SELF' ] ), 1 ) ) ) ? basename( $_get_server[ 'PHP_SELF' ] ) : basename( realpath( $_get_server[ 'SCRIPT_FILENAME' ] ) );
1695 preg_match( "@[a-z0-9_-]+\.php@i", $filename, $matches );
1696 if ( is_array( $matches ) && array_key_exists( 0, $matches ) && false !== $this->cmpstr( '.php', substr( $matches[ 0 ], -4, 4 ), true ) && ( false !== $this->checkfilename( $matches[ 0 ] ) ) && ( $this->get_file_perms( $matches[ 0 ], true ) ) ) {
1697 $filename = $matches[ 0 ];
1698 }
1699 return $filename;
1700 }
1701 /**
1702 * filter array
1703 *
1704 * @return string
1705 */
1706 function cleanString( $b, $s ) {
1707
1708 $s = strtolower( $this->url_decoder( $s ) );
1709 switch ( $b ) {
1710 case ( 0 ):
1711 return $s;
1712 break;
1713 case ( 1 ):
1714 return preg_replace( "/[^\s{}a-z0-9_?,()=@%:{}\/\.\-]/i", ' ', $s );
1715 break;
1716 case ( 2 ):
1717 return preg_replace( "/[^\s{}a-z0-9_?,=@%:{}\/\.\-]/i", ' ', $s );
1718 break;
1719 case ( 3 ):
1720 return preg_replace( "/[^\s=a-z0-9]/i", ' ', $s );
1721 break;
1722 case ( 4 ): // fwr_security pro
1723 return preg_replace( "/[^\s{}a-z0-9_\.\-]/i", '', $s );
1724 break;
1725 case ( 5 ):
1726 return str_replace( '//', '/', $s );
1727 break;
1728 case ( 6 ):
1729 return $this->remove_comments( $s );
1730 break;
1731 case ( 7 ):
1732 return base64_decode( $s );
1733 break;
1734 case ( 8 ):
1735 $s = preg_replace( "/[\s\r\n]/i", '', $s );
1736 $s = preg_replace( "/[^a-f0-9]/i", '', $s );
1737 return pack( "H*", $s );
1738 break;
1739 case ( 9 ):
1740 return trim( $s, " \t\n\r\0\x08\x0B" );
1741 break;
1742 case ( 10 ):
1743 return preg_replace( "/www\.|https:\/\/|http:\/\/|:\/\/|[^a-zA-Z0-9\.-]+/i", "", $s );
1744 break;
1745 case ( 11 ):
1746 return preg_replace('/[[:cntrl:]]/', '', $s ); // remove control characters
1747 break;
1748
1749 default:
1750 return $s;
1751 }
1752 }
1753 /**
1754 * process htaccess filtering
1755 *
1756 * @params $mybans, $newline
1757 *
1758 * @return array
1759 */
1760 function do_htaccess( $mybans, $newline, $thisdomain ) {
1761 $final_htaccess = array();
1762 $mybansips = array();
1763 $thisdomain = $this->get_http_host();
1764 $limitstart = "# " . $thisdomain . " Pareto Security Ban\n";
1765 $limitend = "# End of " . $thisdomain . " Pareto Security Ban\n";
1766 for ( $i = 0; $i <= count( $mybans ); $i++ ) {
1767 if ( false !== strpos( $mybans[ $i ], "deny from" ) ) {
1768 if ( false !== ( bool ) preg_match( "/^deny from \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i", $mybans[ $i ] ) ) {
1769 $mybansips[] = $mybans[ $i ];
1770 }
1771 } elseif ( false === strpos( $mybans[ $i ], " Pareto Security Ban" ) &&
1772 false === strpos( $mybans[ $i ], "order allow,deny" ) &&
1773 false === strpos( $mybans[ $i ], "allow from all" ) ) {
1774 $final_htaccess[] = $mybans[ $i ];
1775 }
1776 }
1777 # pop off the last empty lines, one at a time
1778 $n = count( $final_htaccess ) - 1;
1779 for( $x = 0; $x <= 3; $x++ ) {
1780 $n = count( $final_htaccess ) - 1;
1781 if ( strlen( $final_htaccess[ $n ] ) <= 2 ) unset( $final_htaccess[ $n ] );
1782 if ( $x > 3 ) break;
1783 }
1784 if ( count( $mybansips ) >= ( $this->_total_ips ) ) {
1785 $mybansips = array_reverse( $mybansips );
1786 $mybansips = array_splice( $mybansips, 0, $this->_total_ips - 1 );
1787 $mybansips = array_merge( $mybansips, array( $newline ) );
1788 $mybansips = array_reverse( $mybansips );
1789 } elseif ( count( $mybansips ) < $this->_total_ips ) $mybansips = array_merge( $mybansips, array( $newline ) );
1790 $final_htaccess = array_merge( $final_htaccess, array( "\n\n" ), array( $limitstart ), array( "order allow,deny\n" ), $mybansips, array( "allow from all\n" ), array( $limitend ) );
1791 return $final_htaccess;
1792 }
1793 /**
1794 * open .htaccess, create string to append
1795 *
1796 * @params $banip
1797 *
1798 * @return void
1799 */
1800 function htaccessbanip( $banip ) {
1801 # if IP is empty or too short, or .htaccess is not read/write
1802 if ( false !== empty( $banip ) || ( strlen( $banip ) < 7 ) || ( false === $this->htapath() ) ||
1803 ( isset( $this->options[ 'admin_ip' ] ) && false !== $this->cmpstr( $banip, $this->options[ 'admin_ip' ] ) ) ) {
1804 return $this->karo( "", false, "Low", true );
1805 } else {
1806 $this->set_safe_domain();
1807 $mybans = file( $this->htapath() );
1808 $thisdomain = $this->get_http_host();
1809 if ( false !== $this->is_ip_address( $thisdomain ) ) {
1810 $thisdomain = ( isset( $this->safe_host ) ) ? $this->safe_host : $thisdomain;
1811 }
1812 $limitend = "# End of " . $thisdomain . " Pareto Security Ban\n";
1813 $newline = "deny from $banip\n";
1814 $limitstart = "# " . $thisdomain . " Pareto Security Ban\n";
1815 if ( in_array( $newline, $mybans ) ) exit();
1816 if ( in_array( $limitend, $mybans ) && in_array( $limitstart, $mybans ) ) {
1817 # if Pareto Security is already present in htaccess
1818 $mybans = $this->do_htaccess( $mybans, $newline, $thisdomain );
1819 } else {
1820 array_push( $mybans, "\n", $limitstart, "order allow,deny\n", $newline, "allow from all\n", $limitend );
1821 }
1822 $this->write_htaccess( $mybans );
1823 }
1824 }
1825 /**
1826 * remove all IP addresses from .htaccess
1827 *
1828 * @param $allips = boolean,
1829 * #param $ip = string
1830 *
1831 * @return void
1832 */
1833 function htaccess_unbanip( $allips = true, $ip = '' ) {
1834 if ( false === $this->htapath() ) return;
1835 $final_htaccess = array();
1836 $mybans = file( $this->htapath() );
1837 $thisdomain = $this->get_http_host();
1838 if ( false !== $this->is_ip_address( $thisdomain ) ) {
1839 $thisdomain = ( isset( $this->safe_host ) ) ? $this->safe_host : $thisdomain;
1840 }
1841 $limitstart = "# " . $thisdomain . " Pareto Security Ban\n";
1842 $limitend = "# End of " . $thisdomain . " Pareto Security Ban\n";
1843 if ( empty( $mybans ) || false === in_array( $limitstart, $mybans ) ) return;
1844 if ( false !== $allips ) {
1845 update_option( PARETO_LOG_LIST, array(
1846 0 => SETTINGS_INSTALL_LOG ) );
1847 $logfile = SETTINGS_INSTALL_LOG;
1848 $this->logs = array();
1849 $this->logs[ 0 ] = SETTINGS_INSTALL_LOG;
1850 # Clear bans from HTACCESS
1851 for ( $i = 0; $i <= count( $mybans ); $i++ ) {
1852 if ( false === strpos( $mybans[ $i ], " Pareto Security Ban" ) &&
1853 false === strpos( $mybans[ $i ], "order allow,deny" ) &&
1854 false === strpos( $mybans[ $i ], "deny from" ) &&
1855 false === strpos( $mybans[ $i ], "allow from all" ) ) {
1856 $final_htaccess[] = $mybans[ $i ];
1857 }
1858 }
1859 # pop off the last empty lines, one at a time
1860 $n = count( $final_htaccess ) - 1;
1861 for( $x = 0; $x <= 5; $x++ ) {
1862 $n = count( $final_htaccess ) - 1;
1863 if ( strlen( $final_htaccess[ $n ] ) <= 2 ) unset( $final_htaccess[ $n ] );
1864 if ( $x > 5 ) break;
1865 }
1866 $mybans = $final_htaccess;
1867 } elseif ( strlen( $ip ) > 0 ) {
1868 # remnove a single entry
1869 for ( $x = 0; $x < count( $mybans ); $x++ ) {
1870 if ( strpos( $mybans[ $x ], $ip ) ) unset( $mybans[ $x ] );
1871 }
1872 }
1873 $this->write_htaccess( $mybans );
1874 }
1875 /**
1876 * open htaccess, write array
1877 *
1878 * @param $mybans = array
1879 *
1880 * @return void
1881 */
1882 function write_htaccess( $mybans ) {
1883 if ( !is_array( $mybans ) ) return;
1884 $orig_octal = $this->dirfile_perms( $this->htapath() );
1885 if ( false === $this->get_file_perms( $this->htapath(), true, true ) ) {
1886 chmod( $this->htapath(), 0666 );
1887 }
1888 $myfile = fopen( $this->htapath(), 'w' );
1889 fwrite( $myfile, implode( $mybans, '' ) );
1890 fclose( $myfile );
1891 chmod( $this->htapath(), 0644 );
1892 }
1893 /**
1894 * process file information
1895 *
1896 * @param $f = string
1897 * @param $r = boolean
1898 * @param $w = boolean
1899 *
1900 * @return boolean
1901 */
1902 function get_file_perms( $f = '', $r = false, $w = false ) {
1903 # f = if file exists return bool
1904 # r = if file exists & readable return bool
1905 # w = if file exists, readable & writable return bool
1906 $x = false;
1907 if ( false !== ( bool ) $w )
1908 $r = true;
1909 if ( false !== file_exists( $f ) ) {
1910 $x = true;
1911 } else
1912 return false;
1913 $x = ( false !== ( bool ) $r ) ? is_readable( $f ) : $x;
1914 $x = ( false !== ( bool ) $w ) ? is_writable( $f ) : $x;
1915 return ( bool ) $x;
1916 }
1917 function get_servername() {
1918 if ( false !== getenv( 'SERVER_NAME' ) && ( false !== ( bool ) $this->string_prop( getenv( 'SERVER_NAME' ), 2 ) ) ) {
1919 return getenv( 'SERVER_NAME' );
1920 } else {
1921 return $_SERVER[ 'SERVER_NAME' ];
1922 }
1923 }
1924 function get_serverip() {
1925 if ( false !== getenv( 'SERVER_ADDR' ) && ( false !== ( bool ) $this->string_prop( getenv( 'SERVER_ADDR' ), 2 ) ) ) {
1926 return getenv( 'SERVER_ADDR' );
1927 } else {
1928 return $_SERVER[ 'SERVER_ADDR' ];
1929 }
1930 }
1931
1932 /**
1933 * get the host name
1934 *
1935 * @param $withhttp = boolean
1936 * @param $encoding = string
1937 *
1938 * @return string
1939 */
1940 function get_http_host( $withhttp = false, $encoding = 'UTF-8' ) {
1941 $is_ip = false;
1942 $final_sname = "";
1943 $servername = $this->get_servername();
1944 $final_sname = htmlspecialchars( $servername, ( ( version_compare( phpversion(), '5.4', '>=' ) ) ? ENT_HTML5 : ENT_QUOTES ), $encoding );
1945 $final_sname = filter_var( $final_sname, FILTER_SANITIZE_URL );
1946 if ( false === $final_sname ) $this->karo( $final_sname . " :: Failed Filter Test", false, 'Low', true );
1947 $final_sname = $this->cleanString( 9, $final_sname );
1948 $final_sname = $this->cleanString( 11, $final_sname );
1949
1950 # filter domain names
1951 $http = ( false !== $withhttp ) ? ( ( ( array_key_exists( 'HTTPS', $_SERVER ) && $this->cmpstr( "on", @$_SERVER[ "HTTPS" ], true ) ) ||
1952 ( array_key_exists( 'HTTPS', getenv() ) && $this->cmpstr( "on", getenv( "HTTPS" ), true ) ) ) ? 'https://' : 'http://' ) : '';
1953 return $http . trim( $final_sname );
1954 }
1955 function _detect_tor() {
1956 $is_tor = false; // if error, always return false
1957 if ( $_SERVER[ 'SERVER_ADDR' ] ) {
1958 # give TorHS and admins a pass
1959 if ( false !== $this->cmpstr( $this->get_serverip(), '127.0.0.1' ) && false !== $this->cmpstr( $this->get_ip(), '127.0.0.1' ) ) return false; // likely either admin accessing the site or website is running on a TorHS
1960 $req = $this->_reverse_ip( $this->get_ip() ) . "." . $_SERVER[ 'SERVER_PORT' ] . "." . $this->_reverse_ip( $_SERVER[ 'SERVER_ADDR' ] ) . ".ip-port.exitlist.torproject.org.";
1961 $is_tor = ( "127.0.0.2" == gethostbyname( $req ) ) ? true : false;
1962 return $is_tor;
1963 } else return false;
1964 }
1965 function _reverse_ip( $ip ) {
1966 return implode( ".", array_reverse( explode( ".", $ip ) ) );
1967 }
1968 function tor_restrict() {
1969 if ( false === ( bool ) $this->_tor_block ||
1970 false !== $this->is_wp( true, true ) ||
1971 false === $this->is_wp() ||
1972 false !== $this->is_admin_ip() ) return;
1973
1974 $istor = ( false !== $this->_detect_tor() ) ? true : false;
1975 if ( false === $istor ) return;
1976 $_get_server = $_SERVER;
1977
1978 if ( false !== $this->cmpstr( 'POST', $_SERVER[ 'REQUEST_METHOD' ], true ) ||
1979 isset( $_REQUEST[ 's' ] ) ||
1980 false !== stripos( $this->get_filename(), 'wp-login.php' ) ||
1981 false !== stripos( $this->get_filename(), 'xmlrpc.php' ) ) exit( '<center><font face="verdana" size="1">Notice: Pareto Security plugin settings have prevent you from accessing aspects of this website using Tor</font></center>' );
1982 }
1983 function is_admin_ip() {
1984 $is_admin_ip = ( false !== isset( $this->options[ 'admin_ip' ] ) && false !== $this->cmpstr( $this->get_ip(), $this->options[ 'admin_ip' ] ) ) ? true : false;
1985 return $is_admin_ip;
1986 }
1987 /**
1988 * get the full URL
1989 *
1990 * @param $withhttp = boolean
1991 *
1992 * @return string
1993 */
1994 function getURL( $withhttp = true ) {
1995 $pre_req = $this->getREQUEST_URI();
1996 $q = ( bool ) isset( $_SERVER[ 'QUERY_STRING' ] );
1997 $is_q = ( bool ) strpos( $pre_req, "?" );
1998 $query = ( false !== $is_q ) ? "?" . $_SERVER[ 'QUERY_STRING' ] : "";
1999 $req = ( false !== $is_q ) ? $this->decode_code( substr( $pre_req, 0, strpos( $pre_req, '?' ) ) ) : $pre_req;
2000 $this->do_blacklists( $req, 1, "Request", true, "High", true, true, true );
2001 $locale = ( ( false !== $withhttp ) ? $this->get_http_host( true ) : $this->get_http_host( false ) ) . $req . $query;
2002 $locale = $this->cleanString( 9, $locale );
2003 return $locale;
2004 }
2005 /**
2006 * get the directory path to the root folder
2007 *
2008 * @return string
2009 */
2010 function get_dir() {
2011 $get_root = '';
2012 $_get_server = $_SERVER;
2013 if ( isset( $this->_doc_root ) && ( false !== ( bool ) $this->string_prop( $this->_doc_root, 2 ) ) ) {
2014 # is set by the user
2015 $get_root = $this->_doc_root;
2016 } elseif ( false !== $this->is_wp() && false !== defined( 'ABSPATH' ) ) {
2017 $get_root = ABSPATH;
2018 } elseif ( false !== strpos( $_get_server[ 'DOCUMENT_ROOT' ], 'usr/local' ) || empty( $_get_server[ 'DOCUMENT_ROOT' ] ) || strlen( $_get_server[ 'DOCUMENT_ROOT' ] ) < 4 ) {
2019 # if for some reason there is a problem with DOCUMENT_ROOT, then do this the bad way
2020 $f = dirname( __FILE__ );
2021 $sf = realpath( $_get_server[ 'SCRIPT_FILENAME' ] );
2022
2023 $fbits = explode( DIRECTORY_SEPARATOR, $f );
2024 foreach ( $fbits as $a => $b ) {
2025 if ( false === empty( $b ) && ( false === strpos( $sf, $b ) ) ) {
2026 $f = str_replace( $b, '', $f );
2027 $f = str_replace( '//', '', $f );
2028 }
2029 }
2030 $get_root = realpath( $f );
2031 } else {
2032 $get_root = realpath( $_get_server[ 'DOCUMENT_ROOT' ] ) . PHP_EOL;
2033 }
2034 if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
2035 $get_root = str_replace( '/', '\\', $get_root );
2036 } else
2037 $get_root = str_replace( '\\', '/', $get_root );
2038 if ( !defined( 'WP_PLUGIN_DIR' ) ) {
2039 return $_get_server[ 'DOCUMENT_ROOT' ];
2040 } else return $get_root;
2041 }
2042 /**
2043 * check the ip address is not the server
2044 *
2045 * @param $ip = string
2046 * @param $localhost = boolean
2047 *
2048 * @return boolean
2049 */
2050 function is_server( $ip = '', $localhost = true ) {
2051 # tests if ip address reported as _SERVER[ 'SERVER_ADDR' ]
2052 # is either server ip ( localhost access ) or is 127.0.0.1
2053 # ( i.e onion visitors )
2054 if ( $this->cmpstr( strlen( $ip ), 0 ) ) $ip = $this->get_ip();
2055 if ( false !== $this->cmpstr( $ip, $this->get_serverip() ) ) {
2056 return true;
2057 } elseif ( ( false !== $localhost ) && $this->cmpstr( '127.0.0.', substr( $ip, 0, 8 ) ) ) {
2058 return true;
2059 }
2060 return false;
2061 }
2062 /**
2063 * $this->check_ip()
2064 *
2065 * @param mixed $ip
2066 * @return boolean
2067 */
2068 function check_ip( $ip, $bypass = false ) {
2069 if ( function_exists( 'filter_var' ) && defined( 'FILTER_VALIDATE_IP' ) && defined( 'FILTER_FLAG_IPV4' ) && defined( 'FILTER_FLAG_IPV6' ) ) {
2070 if ( false === ( bool ) filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 || FILTER_FLAG_IPV6 ) ) return false;
2071 if ( false !== $this->is_server( $ip ) ) {
2072 if ( false === ( bool ) $bypass ) $this->_bypassbanip = true;
2073 }
2074 return true;
2075 } else return false;
2076 }
2077 /**
2078 * get_ip()
2079 *
2080 * @return
2081 */
2082 function get_ip() {
2083
2084 $_get_server = $_SERVER;
2085 $svars = array(
2086 'HTTP_FORWARDED_FOR',
2087 'HTTP_CLIENT_IP',
2088 'HTTP_X_CLUSTER_CLIENT_IP',
2089 'HTTP_X_ORIGINATING_IP',
2090 'HTTP_X_REMOTE_IP',
2091 'HTTP_FORWARDED',
2092 'HTTP_CF_CONNECTING_IP',
2093 'HTTP_X_FORWARDED_FOR'
2094 );
2095
2096 # the point here is to not accidentally ban an ip address that could
2097 # be an upline proxy, instead just allow an exit() action if
2098 # it turns out the request is malicious
2099 $x = 0;
2100 while ( $x < count( $svars ) ) {
2101 $iplist = array();
2102 if ( array_key_exists( $svars[ $x ], $_get_server ) ) {
2103 $the_header = $_get_server[ $svars[ $x ] ];
2104 $the_header = ( false !== strpos( $the_header, ',' ) ) ? str_replace( ' ', '', $the_header ) : str_replace( ' ', ',', $the_header );
2105 if ( false !== strpos( $the_header, ',' ) ) {
2106 $iplist = explode( ',', $the_header );
2107 # Check the validity of each ip
2108 foreach ( $iplist as $ip ) {
2109 if ( false !== $this->check_ip( $ip ) ) {
2110 $this->_bypassbanip = true;
2111 }
2112 }
2113 } elseif ( false === empty( $_get_server[ $svars[ $x ] ] ) && false !== $this->check_ip( $_get_server[ $svars[ $x ] ] ) ) {
2114 $this->_bypassbanip = true;
2115 }
2116 }
2117 $x++;
2118 }
2119 $this_ip = $this->getREMOTE_ADDR();
2120 if ( false !== $this->cmpstr( $this_ip, '::1' ) )
2121 $this_ip = '127.0.0.1';
2122 # for TorHS to prevent banning of server IP
2123 if ( false !== $this->is_server( $this_ip ) ) {
2124 $this->_bypassbanip = true;
2125 }
2126 # generally speaking, never trust any ip headers except REMOTE_ADDR
2127 return $this_ip;
2128 }
2129 /**
2130 * x_secure_headers()
2131 */
2132 function x_secure_headers() {
2133 $errlevel = @ini_get( 'error_reporting' );
2134 error_reporting( 0 );
2135 $header = array(
2136 "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload",
2137 "access-control-allow-methods: GET, POST, HEAD",
2138 "X-Frame-Options: SAMEORIGIN",
2139 "X-Content-Type-Options: nosniff",
2140 "X-Xss-Protection: 1; mode=block",
2141 "X-download-options: noopen",
2142 "X-Permitted-Cross-Domain-Policies: master-only",
2143 "Content-Type: text/html; charset=UTF-8"
2144 );
2145 if ( false !== ( bool ) @ini_get( 'expose_php' ) || false !== $this->cmpstr( 'on', @ini_get( 'expose_php' ), true ) ) {
2146 array_push( $header, "X-powered-by: Pareto Security - https://wordpress.org/plugins/pareto-security/" );
2147 }
2148 foreach ( $header as $sent ) {
2149 header( $sent );
2150 }
2151 error_reporting( $errlevel );
2152 return;
2153 }
2154 /**
2155 * substri_count()
2156 */
2157 function substri_count( $hs, $n ) {
2158 return substr_count( strtoupper( $hs ), strtoupper( $n ) );
2159 }
2160 /**
2161 * decode_code()
2162 * @return
2163 */
2164 function decode_code( $code, $escapeshell = false, $b64_decode = false, $filter = false ) {
2165 $code = ( $this->substri_count( $code, '\u00' ) > 0 ) ? str_ireplace( '\u00', '%', $code ) : $code;
2166 $code = ( $this->substri_count( $code, '&#x' ) > 0 && substr_count( $code, ';' ) > 0 ) ? str_replace( ';', '%', str_replace( '&#x', '%', $code ) ) : $code;
2167 $code = ( false !== $b64_decode ) ? base64_decode( $code ) : $code;
2168 if ( false !== $escapeshell ) {
2169 $code = str_replace( "'", "", $code );
2170 return $this->url_decoder( escapeshellarg( $code ) );
2171 } elseif ( false !== $filter ) {
2172 return filter_var( $this->url_decoder( $code ), FILTER_UNSAFE_RAW, FILTER_SANITIZE_SPECIAL_CHARS );
2173 } else
2174 return $this->url_decoder( $code );
2175 }
2176 /**
2177 * url_decoder()
2178 */
2179 function url_decoder( $var ) {
2180 return rawurldecode( urldecode( str_replace( chr( 0 ), '', $var ) ) );
2181 }
2182 function controlchar_exists( $input ) {
2183 $char_count = strlen( preg_replace( '/[^\x00-\x08\x10-\x19]/', '', $input ) ); // detect null-printing control characters
2184 if ( $char_count > 0 ) return true;
2185 return false;
2186 }
2187 function htmlentities_decode_safe( $input ) {
2188 if ( function_exists( 'utf8_encode' ) ) {
2189 return html_entity_decode( utf8_encode( $input ), ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE | ENT_DISALLOWED ); // PHP 7.2
2190 } else {
2191 return html_entity_decode( $input, ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE | ENT_DISALLOWED, 'UTF-8' ); // PHP 5.4 to 7.1
2192 }
2193 }
2194 function htmlentities_safe( $input ) {
2195 if ( function_exists( 'utf8_encode' ) ) {
2196 return htmlentities( utf8_encode( $input ), ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE | ENT_DISALLOWED ); // PHP 7.2
2197 } else {
2198 return htmlentities( $input, ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE | ENT_DISALLOWED, 'UTF-8' ); // PHP 5.4 to 7.1
2199 }
2200 }
2201 /**
2202 * getREQUEST_URI()
2203 */
2204 function getREQUEST_URI() {
2205 if ( false !== getenv( 'REQUEST_URI' ) && ( false !== ( bool ) $this->string_prop( getenv( 'REQUEST_URI' ), 2 ) ) ) {
2206 return strtolower( $this->url_decoder( getenv( 'REQUEST_URI' ) ) );
2207 } else {
2208 return strtolower( $this->url_decoder( $_SERVER[ 'REQUEST_URI' ] ) );
2209 }
2210 }
2211 /**
2212 * getREMOTE_ADDR()
2213 */
2214 function getREMOTE_ADDR() {
2215 if ( false !== getenv( 'REMOTE_ADDR' ) && ( false !== ( bool ) $this->string_prop( getenv( 'REMOTE_ADDR' ), 2 ) ) && false !== $this->check_ip( getenv( 'REMOTE_ADDR' ) ) ) {
2216 return getenv( 'REMOTE_ADDR' );
2217 } elseif ( false !== $_SERVER( 'REMOTE_ADDR' ) && ( false !== ( bool ) $this->string_prop( $_SERVER( 'REMOTE_ADDR' ), 2 ) ) && false !== $this->check_ip( $_SERVER( 'REMOTE_ADDR' ) ) ) {
2218 return $_SERVER[ 'REMOTE_ADDR' ];
2219 }
2220 }
2221 /**
2222 * getQUERY_STRING()
2223 */
2224 function getQUERY_STRING() {
2225 if ( false !== getenv( 'QUERY_STRING' ) ) {
2226 return strtolower( $this->decode_code( getenv( 'QUERY_STRING' ) ) );
2227 } elseif ( isset( $_SERVER[ 'QUERY_STRING' ] ) ) {
2228 return strtolower( $this->decode_code( $_SERVER[ 'QUERY_STRING' ] ) );
2229 } else return false;
2230 }
2231 /**
2232 * string_prop()
2233 */
2234 function string_prop( $str, $len = 0 ) {
2235 # is not an array, is a string, is of at least a specified length ( default is 0 )
2236 if ( false !== is_array( $str ) )
2237 return false;
2238 $x = false;
2239 $x = ( is_string( $str ) ) ? ( ( strlen( $str ) >= ( int ) $len ) ? true : false ) : false;
2240 return ( bool ) $x;
2241 }
2242 /**
2243 * integ_prop()
2244 */
2245 function integ_prop( $integ ) {
2246 if ( false !== ( $this->cmpstr( strval( $integ ), strval( intval( $integ ) ) ) ) && ( false !== filter_var( $integ, FILTER_VALIDATE_INT ) ) &&
2247 ( false !== ctype_digit( strval( $integ ) ) ) && ( false !== preg_match( '/^\d+$/', $integ ) ) && ( $this->cmpstr( $integ, 0 ) || false === empty( $integ ) ) &&
2248 ( false !== is_int( $integ ) ) && ( false === is_float( $integ ) ) ) {
2249 if ( function_exists( 'filter_var' ) && defined( 'FILTER_VALIDATE_INT' ) ) {
2250 return ( ( filter_var( $integ, FILTER_VALIDATE_INT ) === 0 || false !== filter_var( $integ, FILTER_VALIDATE_INT ) ) ? true : false );
2251 } else
2252 return true;
2253 } else
2254 return false;
2255 }
2256 /**
2257 * cmpstr()
2258 * @return bool
2259 */
2260 function cmpstr( $s, $c, $ci = false ) {
2261 if ( false !== $ci ) {
2262 if ( strcasecmp( $s, $c ) == 0 ) {
2263 return true;
2264 } else
2265 return false;
2266 } elseif ( false === $ci ) {
2267 if ( strcmp( $s, $c ) == 0 ) {
2268 return true;
2269 } else
2270 return false;
2271 }
2272 }
2273 function htapath() {
2274 $dir_path = NULL;
2275 if ( false !== $this->is_wp() ) {
2276 include_once( ABSPATH . 'wp-admin/includes/file.php' );
2277 if ( false !== $this->get_file_perms( ( get_home_path() . '.htaccess' ), TRUE, TRUE ) ) {
2278 $dir_path = get_home_path() . '.htaccess';
2279 }
2280 }
2281 $rpath_arr = explode( DIRECTORY_SEPARATOR, $this->get_dir() );
2282 # we don't want to test back too far.
2283 $x = 0;
2284 $root_path = NULL;
2285 While ( false === $this->cmpstr( get_current_user(), $rpath_arr[ count( $rpath_arr ) - 1 ], true ) ) {
2286 $root_path = str_replace( "//", "/", implode( DIRECTORY_SEPARATOR, $rpath_arr ) . DIRECTORY_SEPARATOR . '.htaccess' );
2287 if ( false !== $this->get_file_perms( $root_path, TRUE, TRUE ) )
2288 break;
2289 if ( false !== $this->cmpstr( $this->get_http_host(), $rpath_arr[ count( $rpath_arr ) - $x ], true ) )
2290 break;
2291 if ( $x > 20 )
2292 break; // we're likely looping :-/
2293 array_pop( $rpath_arr );
2294 $x++;
2295 }
2296 $dir_path = ( false !== is_null( $dir_path ) && false === $this->is_wp() ) ? $this->get_dir() . '/.htaccess' : $dir_path;
2297 if ( false === is_null( $root_path ) ) {
2298 return $root_path;
2299 } elseif ( false !== $this->get_file_perms( $dir_path, TRUE, TRUE ) ) {
2300 return $dir_path;
2301 } else
2302 return false;
2303 }
2304 function email_log( $pareto_report2 = '' ) {
2305 $blog_email = 'wordpress@' . $this->get_http_host();
2306 $admin_email = get_option( 'admin_email' );
2307 $blog_name = get_option( 'blogname' );
2308 $blog_url = ( false !== strpos( get_option( 'siteurl' ), $this->get_http_host() ) ) ? get_option( 'siteurl' ) : $this->get_http_host( true );
2309 $headers = array(
2310 'Content-Type: text/html; charset=UTF-8',
2311 'From: Pareto Security - ' . $blog_name . ' <' . $blog_email . '>'
2312 );
2313 $img_tag = '<img src="' . $this->ps_icon . '">';
2314
2315 $pareto_report = '
2316 <!--[if mso]>
2317 <style type="text/css">
2318 body, table, tr, td {font-size: small; font-family: Verdana, Helvetica, sans-serif !important;}
2319 </style>
2320 <![endif]-->
2321 <style>
2322 code{
2323 direction: ltr;
2324 text-align: left;
2325 }
2326 code {font-size:1.0em;
2327 margin: 0px;
2328 padding:5px;
2329 background-color:transparent;
2330 color: #3E3E3E}
2331 </style>
2332 <table style="width:100%;">
2333 <tr style="background-color:#F3F3F3;">
2334 <td>' . $img_tag . '</td><td><H2>PARETO SECURITY LOG FILE</H2></td>
2335 </tr>
2336 <tr>
2337 </table>
2338 <table style="width:100%;">
2339 <tr>
2340 <td><strong>Record of the Last 5 High/Med Severity Incidents</strong></td>
2341 </tr>
2342 <tr>
2343 </table>
2344 <table style="width:100%; text-align: left; background-color: #C9C9C9;">
2345 <tr>
2346 <td>
2347 <table style="width: 100%; text-align: left;">
2348 <tbody>
2349 <tr style="background-color:#5F607B; color: #FFFFFF;font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;">
2350 <td style="width:100px; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif; color: #FFFFF;"><b>Date-Time:</b></td>
2351 <td style="width:60px; color: #FFFFFF;font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;"><b>Severity:</b></td>
2352 <td style="width:120px; color: #FFFFFF;font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;"><b>IP Address:</b></td>
2353 <td style="width:50px; color: #FFFFFF;font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;"><b>Req:</b></td>
2354 <td style="width:80px; color: #FFFFFFfont-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;;"><b>Filename:</b></td>
2355 <td style="color: #FFFFFF;font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;"><b>Attack String:</b></td>
2356 </tr>
2357 <tr>
2358 <td></td>
2359 <td></td>
2360 <td></td>
2361 <td></td>
2362 <td></td>
2363 </tr>';
2364 $pareto_report3 = '';
2365 $mylogs = array();
2366
2367 $mylogs = get_option( PARETO_LOG_LIST );
2368 $i = 0;
2369 $text_color = "#e68735";
2370 $n = 0;
2371 while ( $i <= 99 ) {
2372 if ( isset( $mylogs[ $i ] ) && $n < 4 ) {
2373 $row_colour = ( $this->cmpstr( ( $i % 2 ), 0 ) ) ? "#E6E6F5" : "#F3F3F3";
2374 $req_var = explode( ' ', $mylogs[ $i ] );
2375 if ( preg_match( "/low|safe/i", $req_var[ 1 ] ) ) {
2376 $i++;
2377 continue;
2378 } else $n++;
2379 if ( $this->cmpstr( $req_var[ 1 ], "Medium", true ) ) {
2380 $text_color = "#e68735";
2381 } elseif ( empty( $req_var[ 1 ] ) ) {
2382 $req_var[ 1 ] = "Medium";
2383 $text_color = "#e68735";
2384 } else
2385 $text_color = "#c72b2c";
2386 $mylogs_fin[ $i ] = $mylogs[ $i ];
2387 $ip_addr = $req_var[ 2 ];
2388 $attack_string = str_replace( '%20', " ", preg_replace( "/[\n]/i", "", stripslashes( $req_var[ 5 ] ) ) );
2389 $attack_string = ( strlen( $attack_string ) > 250 ) ? wordwrap( $attack_string, 200, "<br />\n" ) : $attack_string;
2390 $this_timestamp = ( false !== is_numeric( $req_var[ 0 ] ) ) ? $this->set_timestamp( $req_var[ 0 ] ): $req_var[ 0 ];
2391 $pareto_report3 .= "\n<tr style=\"background-color: " . $row_colour . ";\">\n" .
2392 " <td style=\"vertical-align:top; width:100px; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif; white-space: nowrap\">" . $this->url_decoder( $this_timestamp ) . "</td>\n" .
2393 " <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;text-align: center; width:60px; white-space: nowrap; font-weight: bold; color:" . $text_color . "\">" . $req_var[ 1 ] . "</td>\n" .
2394 " <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:140px; white-space: nowrap\">" . $ip_addr . "</td>\n" .
2395 " <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:50px; white-space: nowrap\">" . $req_var[ 3 ] . "</td>\n" .
2396 " <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;width:100px; white-space: nowrap\">" . $req_var[ 4 ] . "</td>\n" .
2397 " <td style=\"vertical-align:top; font-size:11px;font-family:Verdana,Tahoma,Arial,sans-serif;white-space: nowrap\"><code>" . $attack_string . "</code></td>\n</tr>\n";
2398 } else
2399 break;
2400 $i++;
2401 }
2402 $pareto_report3 .= '
2403 </table>
2404 </td>
2405 </tr>
2406 </table>
2407 <br /><br />Pareto Security :: <a target=_"Blank" href="https://hokioisecurity.com/?p=17">https://hokioisecurity.com</a>
2408 <br /><br />You are receiving these because you enabled Email Notifications for Pareto Security. To disable notifications, go
2409 <a target="Blank" href="' . $blog_url . '/wp-admin/options-general.php?page=pareto_security_settings">here</a>';
2410
2411 $pareto_report_full = $pareto_report . $pareto_report2 . $pareto_report3;
2412 $status = wp_mail( $admin_email, 'Pareto Security Attack Report for ' . $blog_url, $pareto_report_full, $headers );
2413 }
2414 function get_wp_current_user() {
2415 $current_user = wp_get_current_user();
2416 $this_user = $current_user->user_login;
2417 return $this_user;
2418 }
2419 /**
2420 * check if this is Wordpress
2421 *
2422 * @param boolean $isinadmin
2423 * @param boolean $isadmin
2424 * @param boolean $is_authorised
2425 *
2426 * @return boolean
2427 */
2428 public function is_wp( $isinadmin = false, $isadmin = false, $is_authorised = false ) {
2429 if ( defined( 'WP_PLUGIN_DIR' ) && false !== function_exists( 'is_admin' ) ) {
2430 if ( false !== $isinadmin ) {
2431 if ( ( false === ( bool ) defined( 'WP_ADMIN' ) || false !== WP_ADMIN ) && false === is_admin() ) return false;
2432 }
2433 if ( false !== $isadmin ) { // current user has administrators rights
2434 $current_user = $this->get_wp_current_user();
2435 if ( false !== is_object( $current_user ) ) {
2436 $is_superadmin = ( false === function_exists( 'is_super_admin' ) && false !== is_super_admin( $current_user->ID ) ) ? true : false;
2437 if ( function_exists( 'user_can' ) && false === ( bool ) user_can( $current_user, 'administrator' ) && false === $is_superadmin ) return false;
2438 } elseif ( false === is_object( $current_user ) && false !== is_admin() && $current_user == 0 ) {
2439 if ( current_user_can( 'editor' ) || current_user_can( 'administrator' ) || current_user_can( 'setup_network' ) ) {
2440 return true;
2441 } else return false;
2442 } else return false;
2443 }
2444 if ( false !== $is_authorised ) {
2445 $current_user = $this->get_wp_current_user();
2446 if ( function_exists( 'user_can' ) && false === user_can( $current_user, 'editor' ) && false === user_can( $current_user, 'author' ) ) {
2447 return false;
2448 }
2449 }
2450 return true;
2451 }
2452 return false;
2453 }
2454 public function do_security_settings() {
2455 # defaults sets errors to production settings
2456 $this->_set_error_level();
2457 # disable assert function in local scope
2458 # shouldn't be available to production websites
2459 @ini_set( 'assert.active', '0' );
2460 # Send secure headers
2461 $this->x_secure_headers();
2462 $this->tor_restrict();
2463 if ( false !== $this->is_wp() ) {
2464 $this->settings_field = 'pareto_security_settings_options';
2465 $this->options = get_option( $this->settings_field );
2466 # set the safe domain
2467 $this->set_safe_domain();
2468 }
2469 $this->ps_icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAACXBIWXMAAAsSAAALEgHS3X78AAAHkElEQVR4nO3aW1MaaRoH8PkSe7e3c7u1tZdblUrUUVOzmamtGfGAZqICTeMRUWMwnBVoRE6CZwl4yDg4QtydqMMaVjRG8IByEEU5KGDwEGcS5AvsBTuk7SZKtCdK1Tz1v3pvnv5Vv89L09WfRdO8PrvuC7hq/QG47sIMEDp4Ez44Dr0+2ts/9IUOt4OHnuDRVujI5d/XajTqgYGenh6VSqVSKg16PVZNo1gBDt78+jmef4sAZRKhDCL09zLorw+gz0ugP+OhP+GFPDaLzXoftVqNSdN4YQM4PP4lGxDlguJ47oLiu6A4FxRnkdv+RhBxzwIG+vsxaRovbACvj06+IL0HwCXZIBLQ19eHSdN4YQbISgbIBcVfUkQcBKC3F5Om8cJoBo5/ySAmB/wjPQBvfv1LCf82QXiHIMwkCDMIwkwiFE8uIEQA+m/gFopGoxu+sGsnaN8KrLh8Fvt2IrMWO4vBuFlD3Pvjf6uFQ5QWDZGjLmX0FdO7Cxo78xtUeTQlOoX1Cg7rht0BpkqfdLsnzb2KNvQMhMPhqcnJ6d9qanJyf38/bQC9PT329fXqqqrampp4qiornU5n2gC6OjvXbLY6KrWeRouHWltrX19PG4BKqVxZWUEAbKurnw5Al49lEKAUgz5GOxQKq9WKACwvL386gHd3f9Xltdq3F2xbc8sbJovTuGCfnLP9ZE4Sg3GRxTxzjMplslevXiEAFovl9wI8eTZHl+uaZD80SEZp4qe1ohGqaKQWGqkWDlXyB8EW7fmp4A1wWEw4QCaVzs/PIwALCwsnJyeRSCQcDu/t7QWDwYODA2wArC5D6js+lRmQSaWzs7MIwPz8fHdXFxkAiAQCkUAAyeQOheLmAl68eIEAmM1mmVRKBoBE+K2t2AA43dgDjEYjAmAymUQQBAcwGQxsAC29E5gDpqemEICZmRkBnw8HND18iA2gtW8iiwhdOnfJUIoAHo8HB9Dq6qLRaCQSCYVCu7u7gUAgEol8BOD09DQWi8VisRmLq1NnunRUT6fZzDOnkFQiSQpgMBhwQHVVld/vLykuzsfh8nG4woICSCi8AOAJhDldBk6XgaEcfyQfeyQfa5KPMZTjrE7DpcNU6tislABNDx/CARQQdG9s5ONw+KKieBobGi4AWB07t8sE2UAbhvkSRP4j+xCgsaEBDgDJ5LW1NVxeXgJAAcELAEtObyYRusrIpjLEUolkenr6QgAZAJaWluCA+yUl7969SxsAQCJZFhfhgMKCghsBkEmlP6cAIBGJaMDbt28vmoFyQTbQlk1uywXbYRGjVtpzQHEOOX6V7xezyW1oAJfN4sAil8n+YzTW19U10Gjx1FFrTSZTY0M9CACJAESi1WqFD3HRhQBPIPy448dHcl21QHuPyLpHYt0jsb4isb+t5FfwBr4GOP8EuYngaZLyZtW3lYJvKvn/T0VrRctAfg0ET2GtkEbn1dG5v4XHgRSaH/5VQWOAVDpYSwep9Ara45HxyUoa4wGZCo/u3zOFpRTcfSCewlLKy9XNFZfX7Q0mB8QrFou5nI4CXF5xUWFxUWExvohEJKyv2QhlpSBASkTZoRgf0zXW1zfU0+Kh1lS7nE42k8FlsxJhMNlfkNtzwPfJJouzSCL4Sg7Yjl6JL+ZS2nMpknhywPYMIpRJhG6XCc4DRKPR9fV1+OYryM+3Wq3lZWXwPapQKAYHBxP7uJ5Gq62pcToczLMvUZoZ7BxMhyoXFGcDbRcAEAfwhwBarRYBcNwUgM2W3gCHw5HSFkIBbsoW2nS7EQfwUmqApHcg4woPs0lzp/yiIfZ6vbi8vPhjYD4OV4zHLy8tpTLEaIBAIFzbCq66dy+dTf9rX+jAG4x49177Qwd7+4fhg+MLAEdHR98/fTo4OKjVaIaHh58/f+5wONCAoaGhlO4AAUq8rP7Y3Crlj04tJr3I8wDoctjtlwZcZQaySCLd9HlvXFIF2JMBhlEAp9N5QwEbGxsIgLKjA30HXC4X5oAJ0woGgO3t7e/u3yeUlyeSFOB2uzEHGBfsGAAODw9HR0f1ev3Es2d6vX5Mp7NaLOgfss3NTQSAzmDfSflFKjq3SvlzK24MAOiKxWJqtRoB8Hg86GPU5g7Y3P5zsrYZsG/tOj27rp095/aec3vPvRPc9IU8/vB2YP/w+OR3AZyenj5BAXZ2dhCAx0zOV9Xyr2tk6HxTp8hvVBU/6n7A6CNyBigtmopW7fkbBktANBpFA7xeL/zqP3YGskii0clXnw6g0WgQAL/fz+Vw0gYwhHqUCAQCLTxe2gB0Ol0znZ5IU2NjKBjkt7amDSBpCQUCBCD1Y/RWKX/kp5fXDBBBEBzA5wuMCw7jgh2dmUXnC4vTvLzx0rZpsXtWXN41ty8cObpmgFgshgOYLG4ZayARAltNadXWQMP17d+zVBh8uoU9QCKRpDgD+Kbuq7fDHiCXydIboFAoUgR814zBh0PYA5RKZYoAgPvk6u2wB3R1dp4FcD70l7KSr716O+wB3V1dj5ubE+FwWzQT82qDud9g7tPP9o7P9uln+w1mtcE8Obd29XbYAwKBgAdWPp8P8xbw+uPT4+uu/wEGULwcmNYlVgAAAABJRU5ErkJggg==";
2470 }
2471 function set_timestamp( $unixtime ) {
2472 if ( false === is_int( (int ) $unixtime ) ) return $unixtime;
2473 $offset = $this->_time_offset;
2474 return ( false !== $this->is_wp() ) ? date_i18n( 'd-m-Y', ( $this->updated( $unixtime, $offset ) ) ) . "<br>" .
2475 date_i18n( 'h:i:sA', ( $this->updated( $unixtime, $offset ) ) ) : date( "d.m.y-G:i" );
2476 }
2477 public function advanced_mode( $mode = 0 ) {
2478 if ( false !== ( bool ) $mode ) {
2479 $this->_banip = 1;
2480 $this->_adv_mode = 1;
2481 $this->_post_filter_mode = 1;
2482 }
2483 }
2484 function is_iis() {
2485 $software = strtolower( $_SERVER[ "SERVER_SOFTWARE" ] );
2486 if ( false !== strpos( $software, "microsoft-iis") )
2487 return true;
2488 else
2489 return false;
2490 }
2491}