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