File "qtrAjaxHandler.php"
Full Path: /home/rrterraplen/public_html/wp-includes/wp-content/plugins/quttera-web-malware-scanner/qtrAjaxHandler.php
File size: 24.85 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* @file qtrAjaxHandler.php
* @brief This module contains AJAX callbacks
*
* @author Quttera (qtr), [email protected]
*
* @internal
* Created 01/17/2016
* Company Quttera
* Copyright Copyright (c) 2016, Quttera
*
* This source code is released for free distribution under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* =====================================================================================
*/
require_once('qtrOptions.php');
require_once('qtrExternalScanner.php');
require_once('qtrUtils.php');
require_once('qtrLogger.php');
require_once('qtrFilesScanner.php');
require_once('qtrStats.php');
require_once('qtrScanLock.php');
require_once('qtrIgnoreList.php');
require_once('qtrThreatsWhiteList.php');
require_once('qtrFilesWhiteList.php');
require_once('qtrFsSnapShot.php');
define( 'QTR_SCAN_CRON_ARGS', 'qtr_scan_cron_args');
define( 'QTR_SCAN_CRON_PERIOD', 5*60 );
define( 'QTR_SCAN_CRON_TIMEOUT', QTR_SCAN_CRON_PERIOD - 5 );
/*
* add filter to add 30 seconds cron period
*/
add_filter( 'cron_schedules', 'qtr_scanner_custom_cron_schedule' );
/*
* maps cron hook to appropriate callback to be invoked for internal scan
*/
add_action( 'qtr_internal_scan_cron_hook', 'on_qtr_scanner_internal_scan_cron_event' );
add_action( 'qtr_heur_internal_scan_cron_hook', 'on_qtr_scanner_heur_internal_scan_cron_event' );
/**
* Adds a custom cron schedule for every 5 minutes.
*
* @param array $schedules An array of non-default cron schedules.
* @return array Filtered array of non-default cron schedules.
*/
function qtr_scanner_custom_cron_schedule( $schedules ) {
$schedules[ 'qtrScanPeriod' ] = array( 'interval' => QTR_SCAN_CRON_PERIOD, 'display' => __( sprintf("Every %d seconds",QTR_SCAN_CRON_PERIOD)));
return $schedules;
}
/**
* @brief removes all instances of registered cron job
* @return nothing
*/
function clean_internal_scan_hook()
{
wp_cache_flush();
do {
$timestamp = wp_next_scheduled( 'qtr_internal_scan_cron_hook' );
wp_unschedule_event( $timestamp, 'qtr_internal_scan_cron_hook' );
}while(($timestamp = wp_next_scheduled( 'qtr_internal_scan_cron_hook' )));
wp_cache_flush();
}
/**
* @brief removes all instances of registered cron job
* @return nothing
*/
function clean_heur_internal_scan_hook()
{
wp_cache_flush();
do {
$timestamp = wp_next_scheduled( 'qtr_heur_internal_scan_cron_hook' );
wp_unschedule_event( $timestamp, 'qtr_heur_internal_scan_cron_hook' );
}while(($timestamp = wp_next_scheduled( 'qtr_heur_internal_scan_cron_hook' )));
wp_cache_flush();
}
function schedule_internal_scan_hook()
{
/*
* submit cron job event to run internal scan
*/
wp_schedule_event( time() + 10, 'qtrScanPeriod', 'qtr_internal_scan_cron_hook');
$logger = new CQtrLogger();
$logger->Info(
sprintf("Internal scan scheduled. Next run %s",
gmdate("H:i:s",
wp_next_scheduled('qtr_internal_scan_cron_hook'))));
}
function schedule_heur_internal_scan_hook()
{
/*
* submit cron job event to run internal scan
*/
wp_schedule_event( time() + 10, 'qtrScanPeriod', 'qtr_heur_internal_scan_cron_hook');
$logger = new CQtrLogger();
$logger->Info(
sprintf("High sensitive internal scan scheduled. Next run %s",
gmdate("H:i:s",
wp_next_scheduled('qtr_heur_internal_scan_cron_hook'))));
}
/**
* @brief stores provided file system snapshot (list of files to scan)
* @param[in] $snapshot - snapshot object to store
* @return FALSE on failure and TRUE on success
*/
function store_snapshot($snapshot, $logger )
{
$json = $snapshot->ToString();
$rc = FALSE;
$deprecated = null;
$autoload = 'no';
if( !$json && $snapshot->FilesCount() > 0){
$logger->Error("Failed to serialize filesystem snapshot");
}
if( defined("QTR_FS_SNAPSHOT") and QTR_FS_SNAPSHOT ){
$logger->Info("Storing file based snapshot");
$rc = CQtrOptions::SaveOption( QTR_SCAN_CRON_ARGS, $json, $deprecated, $autoload, $logger);
if(!$rc){ $logger->Error("Failed to store filesystem snapshot"); }
else{ $logger->Info("Filesystem snapshot stored successfully"); }
}else if ( CQtrOptions::GetOption( QTR_SCAN_CRON_ARGS,$logger) !== false ) {
$rc = CQtrOptions::UpdateOption( QTR_SCAN_CRON_ARGS, $json, $logger );
}else{
$deprecated = null;
$autoload = 'no';
$rc = CQtrOptions::AddOption( QTR_SCAN_CRON_ARGS, $json, $deprecated, $autoload, $logger);
}
return $rc;
}
/**
* @brief loads previously stored
* @return on error returns NULL, on success returns loaded and initialized snapshot object
*/
function load_snapshot($logger)
{
$json = NULL;
if( defined("QTR_FS_SNAPSHOT") and QTR_FS_SNAPSHOT ){
$logger->Info("Loading file based snapshot");
$json = CQtrOptions::LoadOption( QTR_SCAN_CRON_ARGS, false, $logger);
/*
$logger->Info(sprintf("Loaded json [%s]",$json));
*/
}else{
$json = CQtrOptions::GetOption( QTR_SCAN_CRON_ARGS, $logger);
}
if( !$json ){
return NULL;
}
$fs = new CQtrFsSnapShot();
$fs->FromString($json);
return $fs;
}
function force_next_cron_job(){
spawn_cron();
}
/**
* @brief cron job callback procedure
* @param[in] $args - list of input arguments
* @return nothing
*/
function on_qtr_scanner_internal_scan_cron_event($args=NULL)
{
@set_time_limit(0);
$logger = new CQtrLogger();
$snapshot = load_snapshot($logger);
if( !$snapshot ){
$logger->Error("Failed to locate filesystem snapshot");
return;
}
$logger->Info(sprintf("Snapshot %d elements", $snapshot->FilesCount()));
$stime = time();
$etime = $stime + QTR_SCAN_CRON_TIMEOUT;
$scanner = new CQtrFilesScanner(FALSE/*not-heuristic*/);
$scanner->Initialize();
while(time() < $etime){
/*
* runing limited period of time to prevent system overload
*/
$item = $snapshot->Pop();
if($item == NULL ){
/*
* snapshot is empty, all scanned
*/
$scanner->Finalize();
/*
* remove callback registration
*/
clean_internal_scan_hook();
$logger->Info(sprintf("Investigation of %s done", ABSPATH));
return;
}
/*
* In case scan of next file will take too much and this session will be killed
*/
store_snapshot($snapshot, $logger);
if(is_file($item)){
#$logger->Info(sprintf("%s Scanning $item", __FUNCTION__));
$scanner->ScanFile($item);
}else if($scanner->IsIgnored($item)){
$logger->Info(sprintf("Skipping %s"));
}else{
/*
* populate snapshot with more info
*/
$logger->Info("populate snapshot from $item");
/*
* add to snapshot all files/dirs from current location
*/
$snapshot->Populate($item);
/*
* store changes after population
*/
store_snapshot($snapshot, $logger);
}
}
if(wp_next_scheduled('qtr_internal_scan_cron_hook')){
force_next_cron_job();
}else{
/*
* This case may occure when used terminated scan from the dashboard
*/
$logger->Info(sprintf("Investigation of %s done", ABSPATH));
}
return;
}
/**
* @brief cron job callback procedure
* @param[in] $args - list of input arguments
* @return nothing
*/
function on_qtr_scanner_heur_internal_scan_cron_event($args=NULL)
{
@set_time_limit(0);
$logger = new CQtrLogger();
$snapshot = load_snapshot($logger);
if( !$snapshot ){
$logger->Error("Failed to locate filesystem snapshot");
return;
}
$logger->Info(sprintf("Snapshot %d elements", $snapshot->FilesCount()));
$stime = time();
$etime = $stime + QTR_SCAN_CRON_TIMEOUT;
$scanner = new CQtrFilesScanner(TRUE/*heuristic*/);
$scanner->Initialize();
while(time() < $etime){
/*
* runing limited period of time to prevent system overload
*/
$item = $snapshot->Pop();
if($item == NULL ){
/*
* snapshot is empty, all scanned
*/
$scanner->Finalize();
/*
* remove callback registration
*/
clean_heur_internal_scan_hook();
$logger->Info(sprintf("Investigation of %s done", ABSPATH));
return;
}
/*
* In case scan of next file will take too much and this session will be killed
*/
store_snapshot($snapshot, $logger);
if(is_file($item)){
#$logger->Info(sprintf("%s Scanning $item", __FUNCTION__));
$scanner->ScanFile($item);
}else if($scanner->IsIgnored($item)){
$logger->Info(sprintf("Skipping %s"));
}else{
/*
* populate snapshot with more info
*/
$logger->Info("populate snapshot from $item");
$snapshot->Populate($item);
/*
* store changes after population
*/
store_snapshot($snapshot, $logger);
}
}
if(wp_next_scheduled('qtr_heur_internal_scan_cron_hook')){
force_next_cron_job();
}else{
/*
* This case may occure when used terminated scan from the dashboard
*/
$logger->Info(sprintf("High Sensitive Investigation of %s done", ABSPATH));
}
return;
}
class CQtrAjaxHandler
{
private static function __can_access(){
$nonce = $_REQUEST['_wpnonce'];
if (!wp_verify_nonce( $nonce, 'quttera' ) ) {
wp_die(__('You do not have sufficient permissions to access this page.') );
}
if(!current_user_can('manage_options')){
wp_die(__('You do not have sufficient permissions to access this page.') );
}
}
public static function RunExternalScan()
{
self::__can_access();
//check_ajax_referer( 'qtr_wm_scanner-scan' );
$this_url = trim($_POST['_this']); /* domain name of this server */
$qtr_url = trim($_POST['_qtr_url']); /* quttera investigation server name */
if( empty($this_url) )
{
$this_url = CQtrUtils::GetDomainName();
}
else if( empty($qtr_url) )
{
$qtr_url = "http://wp.quttera.com";
}
if( strpos($this_url, "://" ) != false )
{
$parse = parse_url($this_url);
$this_url = $parse['host'];
}
/*
* validate input of host name
*/
if(filter_var(gethostbyname($this_url), FILTER_VALIDATE_IP) === FALSE )
{
/* send error to frontend */
echo json_encode( array( 'content' => array("state" => "Failed to access local server",
"age" => time(),
"url" => "localhost" )));
exit;
}
/* validate quttera server address */
if( filter_var($qtr_url, FILTER_VALIDATE_URL) === FALSE )
{
/* send error to fronend */
echo json_encode( array( 'content' => array("state" => "Failed to access remote server",
"age" => time(),
"url" => $this_url )));
exit;
}
$investigation_url = $qtr_url . "/wp_scan/" . $this_url;
if( filter_var($investigation_url, FILTER_VALIDATE_URL) === FALSE )
{
echo json_encode( array( 'content' => array( "state" => "Remote server address is invalid",
"age" => time(),
"url" => "<undefined>" )));
exit;
}
usleep(1000000); //sleep for a second
$output = CQtrExternalScanner::SendQuery( $investigation_url );
if( empty($output) )
{
$output = CQtrExternalScanner::SendQuery( $investigation_url );
if( $output == false )
{
echo json_encode( array( 'content' => array("state" => "Failed to access investigation server [ " . $qtr_url . " ]",
"age" => time(),
"url" => $this_url,
"img" => plugins_url( 'loader.gif', __FILE__ ))
)
);
exit;
}
}
$output = json_decode($output);
//if state is not finished sleep for a second
echo json_encode( array( 'content' => $output ) );
exit;
}
public static function IsInternalScanNowRunning()
{
self::__can_access();
if( self::IsInternalScanRunning() ){
echo "yes";
}else{
echo "no";
}
exit();
}
public static function RunInternalScan()
{
self::__can_access();
wp_cache_flush();
flush();
$logger = new CQtrLogger();
$logger->Info(sprintf("Starting investigation of %s",ABSPATH));
/*
* Check if any internal scan is running
*/
if( self::IsInternalScanRunning() ){
$logger->Info("Error, internal scan process already running");
$output = $logger->GetAllLines();
echo json_encode($output);
exit();
}
$stats = new CQtrStats();
$stats->Reset();
$report = new CQtrReport();
$report->Reset();
$scanner = new CQtrFilesScanner(FALSE/*non-heuristic*/);
$scanner->Initialize();
$path = ABSPATH;
$fs = new CQtrFsSnapShot();
$fs->Populate(ABSPATH);
/*
* store initial filesystem snapshot to be scanned
*/
store_snapshot($fs, $logger);
/*
* clean previous cron jobs if exist
*/
clean_internal_scan_hook();
/*
* Starting next scan slot
*/
schedule_internal_scan_hook();
$logger->Info("Starting internal scan of [$path]");
$output = $logger->GetAllLines();
echo json_encode($output);
exit();
}
public static function RunHeurInternalScan()
{
self::__can_access();
wp_cache_flush();
flush();
$logger = new CQtrLogger();
$logger->Info(sprintf("Starting investigation of %s",ABSPATH));
/*
* Check if any internal scan is running
*/
if( self::IsInternalScanRunning() ){
$logger->Info("Error, internal scan process already running");
$output = $logger->GetAllLines();
echo json_encode($output);
exit();
}
$stats = new CQtrStats();
$stats->Reset();
$report = new CQtrReport();
$report->Reset();
$scanner = new CQtrFilesScanner(TRUE/*heuristic*/);
$scanner->Initialize();
$path = ABSPATH;
$fs = new CQtrFsSnapShot();
$fs->Populate(ABSPATH);
/*
* store initial filesystem snapshot to be scanned
*/
store_snapshot($fs, $logger);
/*
* clean previous cron jobs if exist
*/
clean_heur_internal_scan_hook();
schedule_heur_internal_scan_hook();
$logger->Info("Starting high sensitive internal scan of [$path]");
$output = $logger->GetAllLines();
echo json_encode($output);
exit();
}
public static function StopInternalScan()
{
self::__can_access();
$logger = new CQtrLogger();
$logger->Info("Handling request to terminate internal scan");
$logger->Info("Remove cron job from scheduler");
wp_cache_flush();
clean_internal_scan_hook();
clean_heur_internal_scan_hook();
if(wp_next_scheduled('qtr_internal_scan_cron_hook')){
$logger->Error("Failed to remove scanner event from cron schedule");
}else{
$logger->Info("Cron job cleared");
}
exit();
}
public static function IsInternalScanRunning()
{
self::__can_access();
wp_cache_flush();
$timestamp = wp_next_scheduled('qtr_internal_scan_cron_hook');
if($timestamp){
return TRUE;
}
$timestamp = wp_next_scheduled('qtr_heur_internal_scan_cron_hook');
if(!$timestamp){
return FALSE;
}
return TRUE;
}
public static function GetLogLines()
{
self::__can_access();
$index = 0;
$logger = new CQtrLogger();
if( isset( $_GET['start_line']) )
{
$index = intval( $_GET['start_line']);
}
else if( isset( $_POST['start_line']) )
{
$index = intval( $_POST['start_line']);
}
$lines = $logger->GetAllLines();
echo json_encode($lines);
exit();
}
public static function CleanLogLines()
{
self::__can_access();
$index = 0;
$logger = new CQtrLogger();
$logger->Clean();
#echo __FUNCTION__ . " called\n";
exit();
}
public static function GetStats()
{
self::__can_access();
wp_cache_flush();
$report = new CQtrReport();
$stats = $report->GetStats();
$counters = $stats->GetCounters();
echo json_encode($counters);
exit();
}
public static function ScannerReport()
{
self::__can_access();
$report = new CQtrReport();
$header = $report->GenerateMeta();
$dump = $report->GetDetectedThreats();
$body = "";
foreach ( $dump as $entry){
$threat = $entry["THREAT"];
$threat = preg_replace("/\s\s*/"," ", $threat);
$threat = preg_replace("/\r\n/","", $threat);
$body .= "\r\n\r\n";
$body .= "FILE: " . $entry["FILE"] . "\r\n";
$body .= "FILE_MD5: " . $entry["FILE_MD5"] . "\r\n";
$body .= "SEVERITY: " . $entry["SEVERITY"] . "\r\n";
$body .= "ENGINE: " . $entry["ENGINE"] . "\r\n";
$body .= "THREAT_SIG: " . $entry["THREAT_SIG"] . "\r\n";
$body .= "THREAT_NAME: " . $entry["THREAT_NAME"] . "\r\n";
$body .= "THREAT: " . $threat . "\r\n";
$body .= "DETAILS: " . $entry["DETAILS"] . "\r\n";
}
echo $header . "\r\n" . $body;
exit();
}
public static function GetDetectedThreatsReport()
{
self::__can_access();
$report = new CQtrReport();
$output = $report->GetDetectedThreats();
echo json_encode($output);
exit();
}
public static function ShowFile()
{
self::__can_access();
$file = "";
$output = "";
$logger = new CQtrLogger();
if( isset( $_POST["FILE_PATH"] ) ){
$file = ABSPATH . $_POST["FILE_PATH"];
}
$logger->Info("Showing file $file");
if(!is_file($file)){
$logger->Error("Failed to locate required file [$file]");
exit();
}
//verifing that referenced file is part of the WP installation
$real_path = realpath($file);
if(strpos($real_path,ABSPATH,0) !== 0){
$logger->Error("Permission denied [$real_path]");
wp_die(__('You do not have sufficient permissions to access this page.'));
}
$output = file_get_contents($file);
echo $output;
exit();
}
public static function GetIgnoredThreatsReport()
{
self::__can_access();
$ignore_list = new CQtrIgnoreList();
$report = new CQtrReport( );
$threats = $report->Get();
$output = array();
/*
* remove all not-ignored threats
*/
foreach( $threats as $threat )
{
/*
* if threat is not part of the ignored list, remove it from output
*/
if( $ignore_list->Get( $threat["FILE_MD5"], $threat["THREAT_SIG"] ) )
{
array_push($output,$threat);
}
}
echo json_encode($output);
exit();
}
public static function IgnoreThreat()
{
self::__can_access();
$file = "";
$threat = "";
if( isset( $_POST["FILE_MD5"] ) ){
$file = $_POST["FILE_MD5"];
}
if( isset( $_POST["THREAT_SIG"] ) ){
$threat = $_POST["THREAT_SIG"];
}
$ignore_list = new CQtrIgnoreList();
if( !$ignore_list->Add($file,$threat) ){
echo json_encode("Operation failed");
}else if( $ignore_list->Get($file,$threat) == NULL ) {
echo json_encode("Failed to retrieve just added threat for $file:$threat");
}else{
echo json_encode("Operation succeeded");
}
exit();
}
public static function RemoveFromIgnoreList()
{
self::__can_access();
$file = "";
$threat = "";
if( isset( $_POST["FILE_MD5"] ) ){
$file = $_POST["FILE_MD5"];
}
if( isset( $_POST["THREAT_SIG"] ) ){
$threat = $_POST["THREAT_SIG"];
}
$ignore_list = new CQtrIgnoreList();
if( !$ignore_list->Remove($file,$threat) ){
echo json_encode("Operation failed");
}else{
echo json_encode("Operation succeeded");
}
exit();
}
public static function CleanIgnoreList()
{
self::__can_access();
$ignore_list = new CQtrIgnoreList();
$ignore_list->Clean();
echo json_encode("Operation succeeded");
exit();
}
public static function WhiteListThreat()
{
self::__can_access();
$file = "";
$threat = "";
if( isset( $_POST["FILE_MD5"] ) ){
$file = $_POST["FILE_MD5"];
}
if( isset( $_POST["THREAT_SIG"] ) ){
$threat = $_POST["THREAT_SIG"];
}
$ignore_list = new CQtrIgnoreList();
$white_list = new CQtrThreatsWhiteList();
/*
* Remove threat from ignored list if it is there
*/
$ignore_list->Remove($file,$threat);
if( !$white_list->Add($file,$threat) ){
echo json_encode("Operation failed");
}else if( $white_list->Get($file,$threat) == NULL ) {
echo json_encode("Failed to retrieve just added threat for $file:$threat");
}else{
echo json_encode("Operation succeeded for $file:$threat");
}
exit();
}
public static function CleanThreatsWhiteList()
{
self::__can_access();
$white_list = new CQtrThreatsWhiteList();
$white_list->Clean();
echo json_encode("Operation succeeded");
exit();
}
public static function WhiteListFile()
{
self::__can_access();
$file_path = NULL;
$file_sig = NULL;
if( isset($_POST["FILE_MD5"] ) ) {
$file_sig = trim($_POST["FILE_MD5"]);
}
if( isset($_POST["FILE"]) ){
$file_path = trim( $_POST["FILE"] );
}
if( $file_path == NULL && $file_sig == NULL ){
echo json_encode("provided invalid input");
exit();
}
$rc = FALSE;
$white_list = new CQtrFilesWhiteList();
$white_list->Load();
$error = "operation succeeded";
if( $file_sig ){
$rc = $white_list->AddBySig( $file_sig );
if( !$rc ){
if( $white_list->IsWhiteListed( $file_sig ) ){
$error = "$file_sig already whitelisted";
}else{
$error = "$file_sig failed due to invalid input";
}
}
}else{
$rc = $white_list->AddByPath( $file_path );
if( !$rc ){
if( $white_list->IsWhiteListedFile( $file_path ) ){
$error = "$file_path already whitelisted";
}else{
$error = "$file_path failed due to invalid input";
}
}
}
echo json_encode($error);
exit();
}
public static function CleanFilesWhiteList()
{
self::__can_access();
$white_list = new CQtrFilesWhiteList();
if( $white_list->Clean() ){
echo json_encode("Operation succeeded");
} else {
echo json_encode("Operation failed");
}
exit();
}
}
?>