From d173ff29ab423dbe04dcc6e0314ee5df75698940 Mon Sep 17 00:00:00 2001 From: cschantz Date: Tue, 11 Nov 2025 19:03:21 -0500 Subject: [PATCH] Add standalone malware scanner with installation guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Features: - Standalone scanner generator that runs independently in /opt - Launch in screen session for background execution - Self-contained script with no toolkit dependencies - Self-cleanup with user confirmation after completion - Scanner installation guide for ImunifyAV, ClamAV, and Maldet - Menu option 5: Launch standalone scanner - Complete scan scope selection (server/user/domain/custom path) Implementation: - Added show_scanner_installation_guide() function - Added launch_standalone_scanner_menu() function - Enhanced generate_standalone_scanner() with screen integration - Integrated with main malware scanner menu Use case: Long-running scans can be launched independently, allowing toolkit deletion while scans continue in background. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- modules/security/malware-scanner.sh | 562 +++++++++++++++++++++++++++- 1 file changed, 551 insertions(+), 11 deletions(-) diff --git a/modules/security/malware-scanner.sh b/modules/security/malware-scanner.sh index 331125c..5981b8c 100755 --- a/modules/security/malware-scanner.sh +++ b/modules/security/malware-scanner.sh @@ -38,17 +38,70 @@ detect_scanners() { if [ ${#available_scanners[@]} -eq 0 ]; then echo -e "${RED}No malware scanners detected!${NC}" echo "" - echo "Available scanners to install:" - echo " • ImunifyAV - Commercial, real-time protection" - echo " • ClamAV - Open source antivirus" - echo " • Maldet (LMD) - Linux Malware Detect" - echo "" + show_scanner_installation_guide return 1 fi return 0 } +# Show installation instructions for missing scanners +show_scanner_installation_guide() { + echo -e "${YELLOW}Available Malware Scanners:${NC}" + echo "" + + # Check ImunifyAV + if ! command -v imunify-antivirus &>/dev/null; then + echo -e "${CYAN}ImunifyAV${NC} - Commercial real-time protection" + echo " Status: Not installed" + echo " Installation:" + echo " wget https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh" + echo " bash imav-deploy.sh" + echo " Docs: https://docs.imunify360.com/" + echo "" + else + echo -e "${GREEN}✓ ImunifyAV${NC} - Installed" + echo "" + fi + + # Check ClamAV + if ! command -v clamscan &>/dev/null; then + echo -e "${CYAN}ClamAV${NC} - Open source antivirus engine" + echo " Status: Not installed" + echo " Installation (cPanel):" + echo " /scripts/update_local_rpm_versions --edit target_settings.clamav installed" + echo " /scripts/check_cpanel_rpms --fix --targets=clamav" + echo " Installation (manual):" + echo " yum install clamav clamav-update # RHEL/CentOS" + echo " apt-get install clamav clamav-daemon # Debian/Ubuntu" + echo " freshclam # Update virus definitions" + echo "" + else + echo -e "${GREEN}✓ ClamAV${NC} - Installed" + echo "" + fi + + # Check Maldet + if ! command -v maldet &>/dev/null; then + echo -e "${CYAN}Maldet (LMD)${NC} - Linux Malware Detect" + echo " Status: Not installed" + echo " Installation:" + echo " cd /tmp" + echo " wget http://www.rfxn.com/downloads/maldetect-current.tar.gz" + echo " tar -xzf maldetect-current.tar.gz" + echo " cd maldetect-*" + echo " ./install.sh" + echo " Docs: https://www.rfxn.com/projects/linux-malware-detect/" + echo "" + else + echo -e "${GREEN}✓ Maldet${NC} - Installed" + echo "" + fi + + echo -e "${YELLOW}Recommendation:${NC} Install at least ClamAV (free) for basic scanning" + echo "" +} + # Detect control panel and gather docroots detect_control_panel() { docroot_array=() @@ -400,6 +453,333 @@ run_all_scanners() { read -p "Press Enter to continue..." } +# Generate standalone malware scan script +generate_standalone_scanner() { + local scan_paths=("$@") + + if [ ${#scan_paths[@]} -eq 0 ]; then + echo -e "${RED}No paths to scan${NC}" + return 1 + fi + + # Create session ID and directory + local session_id="malware-$(date +%Y%m%d-%H%M%S)" + local session_dir="/opt/${session_id}" + + echo "" + print_header "Generating Standalone Scanner" + echo "Session ID: $session_id" + echo "Location: $session_dir" + echo "" + + # Create directory structure + mkdir -p "$session_dir"/{logs,results} + + # Create standalone scan script + cat > "$session_dir/scan.sh" << 'STANDALONE_EOF' +#!/bin/bash + +################################################################################ +# Standalone Malware Scanner +################################################################################ +# Auto-generated by Server Management Toolkit +# This script is self-contained and can run independently +################################################################################ + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# Get script directory +SCAN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCAN_DIR/logs" +RESULTS_DIR="$SCAN_DIR/results" + +# Session info +SESSION_LOG="$LOG_DIR/session.log" +SUMMARY_FILE="$RESULTS_DIR/summary.txt" +INFECTED_LIST="$RESULTS_DIR/infected_files.txt" + +# Logging function +log_message() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$SESSION_LOG" +} + +# Banner +clear +echo "========================================" +echo "Standalone Malware Scanner" +echo "========================================" +echo "Session: $(basename $SCAN_DIR)" +echo "Started: $(date)" +echo "========================================" +echo "" + +log_message "Scan session started" + +# Detect available scanners +AVAILABLE_SCANNERS=() + +if command -v imunify-antivirus &>/dev/null; then + AVAILABLE_SCANNERS+=("imunify") + log_message "Detected: ImunifyAV" +fi + +if command -v clamscan &>/dev/null; then + AVAILABLE_SCANNERS+=("clamav") + log_message "Detected: ClamAV" +fi + +if command -v maldet &>/dev/null; then + AVAILABLE_SCANNERS+=("maldet") + log_message "Detected: Maldet" +fi + +if [ ${#AVAILABLE_SCANNERS[@]} -eq 0 ]; then + log_message "ERROR: No scanners found!" + echo -e "${RED}No malware scanners detected!${NC}" + exit 1 +fi + +log_message "Found ${#AVAILABLE_SCANNERS[@]} scanner(s): ${AVAILABLE_SCANNERS[*]}" + +# Scan paths (will be replaced) +SCAN_PATHS=() +PLACEHOLDER_SCAN_PATHS + +log_message "Scanning ${#SCAN_PATHS[@]} path(s)" + +# Initialize summary +{ + echo "==========================================" + echo "Malware Scan Summary Report" + echo "==========================================" + echo "Session: $(basename $SCAN_DIR)" + echo "Started: $(date)" + echo "Scanners: ${AVAILABLE_SCANNERS[*]}" + echo "Paths: ${#SCAN_PATHS[@]}" + echo "" + printf '%s\n' "${SCAN_PATHS[@]}" + echo "" + echo "==========================================" + echo "" +} > "$SUMMARY_FILE" + +# Track completion +SCANNERS_COMPLETED=0 +TOTAL_SCANNERS=${#AVAILABLE_SCANNERS[@]} + +# Run each scanner +for scanner in "${AVAILABLE_SCANNERS[@]}"; do + SCANNER_NUM=$((SCANNERS_COMPLETED + 1)) + + echo "" + echo -e "${CYAN}[$SCANNER_NUM/$TOTAL_SCANNERS] Starting ${scanner^} scan...${NC}" + log_message "Starting ${scanner} scan" + + { + echo "Scanner: ${scanner^}" + echo "Started: $(date)" + echo "---" + } >> "$SUMMARY_FILE" + + case "$scanner" in + imunify) + log_message "ImunifyAV: Updating signatures" + imunify-antivirus update &>> "$LOG_DIR/imunify.log" + + for path in "${SCAN_PATHS[@]}"; do + if [ -d "$path" ]; then + log_message "ImunifyAV: Queuing $path" + imunify-antivirus malware on-demand queue put "$path" &>> "$LOG_DIR/imunify.log" + fi + done + + echo "✓ ImunifyAV scans queued" | tee -a "$SUMMARY_FILE" + log_message "ImunifyAV: Scans queued successfully" + ;; + + clamav) + if command -v freshclam &>/dev/null; then + log_message "ClamAV: Updating signatures" + freshclam &>> "$LOG_DIR/clamav.log" + fi + + log_message "ClamAV: Starting scan" + clamscan --infected --recursive "${SCAN_PATHS[@]}" &>> "$LOG_DIR/clamav.log" + + # Extract infected files + grep "FOUND" "$LOG_DIR/clamav.log" | cut -d: -f1 >> "$INFECTED_LIST" 2>/dev/null + + CLAM_INFECTED=$(grep -c "FOUND" "$LOG_DIR/clamav.log" 2>/dev/null || echo 0) + echo "✓ ClamAV scan complete - Found: $CLAM_INFECTED" | tee -a "$SUMMARY_FILE" + log_message "ClamAV: Scan complete - $CLAM_INFECTED infected files" + ;; + + maldet) + log_message "Maldet: Updating signatures" + maldet -u &>> "$LOG_DIR/maldet.log" + + # Create temp path list + TEMP_PATHLIST="/tmp/maldet_paths_$$.txt" + printf '%s\n' "${SCAN_PATHS[@]}" > "$TEMP_PATHLIST" + + log_message "Maldet: Starting scan" + maldet -b -f "$TEMP_PATHLIST" &>> "$LOG_DIR/maldet.log" + + rm -f "$TEMP_PATHLIST" + + echo "✓ Maldet scan complete" | tee -a "$SUMMARY_FILE" + log_message "Maldet: Scan complete" + ;; + esac + + echo "" | tee -a "$SUMMARY_FILE" + ((SCANNERS_COMPLETED++)) + + # Wait between scanners + if [ $SCANNERS_COMPLETED -lt $TOTAL_SCANNERS ]; then + echo "Waiting 3 seconds before next scanner..." + sleep 3 + fi +done + +# Finalize report +{ + echo "==========================================" + echo "Scan Session Complete" + echo "Completed: $(date)" + echo "==========================================" + echo "" + + if [ -f "$INFECTED_LIST" ] && [ -s "$INFECTED_LIST" ]; then + echo "INFECTED FILES DETECTED:" + echo "" + sort -u "$INFECTED_LIST" + else + echo "No infected files detected by automated scan." + echo "Review individual scanner logs for details." + fi +} >> "$SUMMARY_FILE" + +log_message "All scans completed successfully" + +# Display completion +clear +echo "==========================================" +echo -e "${GREEN}Malware Scan Complete!${NC}" +echo "==========================================" +echo "Session: $(basename $SCAN_DIR)" +echo "Completed: $(date)" +echo "" +echo "Results saved to:" +echo " Summary: $SUMMARY_FILE" +echo " Logs: $LOG_DIR/" +echo "" + +# Show summary +cat "$SUMMARY_FILE" + +echo "" +echo "==========================================" +echo "" + +# Prompt for cleanup +read -p "Delete scan directory and all results? (yes/no): " cleanup_choice + +if [ "$cleanup_choice" = "yes" ]; then + log_message "User requested cleanup - deleting scan directory" + echo "" + echo "Removing scan directory..." + cd / + rm -rf "$SCAN_DIR" + echo -e "${GREEN}✓ Scan directory deleted${NC}" + echo "" + echo "This screen session will now close." + sleep 2 +else + log_message "User chose to keep results" + echo "" + echo "Results preserved at: $SCAN_DIR" + echo "" + echo "You can:" + echo " • Review logs: ls $LOG_DIR" + echo " • View summary: cat $SUMMARY_FILE" + echo " • Delete manually: rm -rf $SCAN_DIR" + echo "" + echo "Press Ctrl+A then D to detach from this screen session" + echo "" +fi + +log_message "Scan session ended" +STANDALONE_EOF + + # Replace placeholder with actual paths + local paths_declaration="SCAN_PATHS=(" + for path in "${scan_paths[@]}"; do + paths_declaration+="\"$path\" " + done + paths_declaration+=")" + + sed -i "s|PLACEHOLDER_SCAN_PATHS|$paths_declaration|" "$session_dir/scan.sh" + + # Make executable + chmod +x "$session_dir/scan.sh" + + # Check if screen is installed + if ! command -v screen &>/dev/null; then + echo -e "${YELLOW}Warning: 'screen' not installed${NC}" + echo "Install with: yum install screen OR apt-get install screen" + echo "" + echo "Script created at: $session_dir/scan.sh" + echo "Run manually with: bash $session_dir/scan.sh" + read -p "Press Enter to continue..." + return 1 + fi + + # Launch in screen session + echo "Launching scan in screen session..." + screen -dmS "$session_id" bash "$session_dir/scan.sh" + + sleep 1 + + # Verify screen started + if screen -list | grep -q "$session_id"; then + echo "" + echo -e "${GREEN}✓ Standalone scanner started successfully!${NC}" + echo "" + echo "Session ID: $session_id" + echo "Screen session: $session_id" + echo "Results directory: $session_dir/results/" + echo "" + echo -e "${CYAN}Monitor the scan:${NC}" + echo " screen -r $session_id" + echo "" + echo -e "${CYAN}Check progress:${NC}" + echo " tail -f $session_dir/logs/session.log" + echo "" + echo -e "${CYAN}Detach from screen:${NC}" + echo " Press: Ctrl+A then D" + echo "" + echo -e "${GREEN}You can now safely delete the toolkit.${NC}" + echo -e "${GREEN}The scan will continue running independently.${NC}" + echo "" + + # Store session info in reference database + store_reference "malware_standalone_latest" "$session_id" + store_reference "malware_standalone_${session_id}_dir" "$session_dir" + + else + echo -e "${RED}Failed to start screen session${NC}" + echo "Run manually: bash $session_dir/scan.sh" + fi + + read -p "Press Enter to continue..." +} + # Compare results from multiple scanners compare_scan_results() { echo "" @@ -430,6 +810,162 @@ compare_scan_results() { read -p "Press Enter to continue..." } +# Launch standalone scanner menu +launch_standalone_scanner_menu() { + echo "" + print_header "Launch Standalone Scanner" + + echo "This will create a self-contained scanner in /opt/ that runs" + echo "independently in a screen session. You can safely delete the" + echo "toolkit after launching - the scan will continue running." + echo "" + + if ! detect_control_panel; then + read -p "Press Enter to continue..." + return 1 + fi + + echo "Control Panel: ${CONTROL_PANEL^}" + echo "Available Scanners: ${available_scanners[*]}" + echo "" + + echo "Select scan scope:" + echo " 1. Entire server (all docroots)" + echo " 2. Specific user account" + echo " 3. Specific domain" + echo " 4. Custom path" + echo " 0. Cancel" + echo "" + + read -p "Select option: " scope_choice + + local scan_paths=() + local scan_description="" + + case $scope_choice in + 1) + # Entire server + scan_paths=("${sanitized_docroot[@]}") + scan_description="full server scan" + + if [ ${#scan_paths[@]} -eq 0 ]; then + echo -e "${RED}No docroots found!${NC}" + read -p "Press Enter to continue..." + return 1 + fi + + echo "" + echo "Scan paths: ${#scan_paths[@]} docroots" + ;; + + 2) + # Specific user + echo "" + echo "Available users:" + select_user_interactive "Select user account to scan" + + if [ -z "$SELECTED_USER" ]; then + echo "No user selected." + read -p "Press Enter to continue..." + return 1 + fi + + # Get user's docroots + for docroot in "${sanitized_docroot[@]}"; do + if [[ "$docroot" == *"/$SELECTED_USER/"* ]]; then + scan_paths+=("$docroot") + fi + done + + if [ ${#scan_paths[@]} -eq 0 ]; then + echo -e "${RED}No docroots found for user: $SELECTED_USER${NC}" + read -p "Press Enter to continue..." + return 1 + fi + + scan_description="user $SELECTED_USER" + echo "Found ${#scan_paths[@]} docroots for $SELECTED_USER" + ;; + + 3) + # Specific domain + echo "" + read -p "Enter domain name: " domain + + if [ -z "$domain" ]; then + echo "No domain entered." + read -p "Press Enter to continue..." + return 1 + fi + + # Find docroot for domain + for docroot in "${sanitized_docroot[@]}"; do + if [[ "$docroot" == *"/$domain"* ]] || [[ "$docroot" == *"/$domain/"* ]]; then + scan_paths+=("$docroot") + fi + done + + if [ ${#scan_paths[@]} -eq 0 ]; then + echo -e "${RED}No docroot found for domain: $domain${NC}" + read -p "Press Enter to continue..." + return 1 + fi + + scan_description="domain $domain" + echo "Found docroot: ${scan_paths[0]}" + ;; + + 4) + # Custom path + echo "" + read -p "Enter path to scan: " custom_path + + if [ -z "$custom_path" ]; then + echo "No path entered." + read -p "Press Enter to continue..." + return 1 + fi + + if [ ! -d "$custom_path" ]; then + echo -e "${RED}Path does not exist: $custom_path${NC}" + read -p "Press Enter to continue..." + return 1 + fi + + scan_paths=("$custom_path") + scan_description="custom path $custom_path" + ;; + + 0) + return 0 + ;; + + *) + echo -e "${RED}Invalid option${NC}" + sleep 1 + return 1 + ;; + esac + + # Confirm before generating + echo "" + echo -e "${YELLOW}Ready to generate standalone scanner${NC}" + echo "Scope: $scan_description" + echo "Paths: ${#scan_paths[@]}" + echo "Scanners: ${available_scanners[*]}" + echo "" + read -p "Generate and launch? (yes/no): " confirm + + if [ "$confirm" != "yes" ]; then + echo "Cancelled." + read -p "Press Enter to continue..." + return 0 + fi + + # Generate and launch standalone scanner + generate_standalone_scanner "${scan_paths[@]}" +} + # Main scan menu show_scan_menu() { while true; do @@ -447,10 +983,13 @@ show_scan_menu() { echo " 3. Scan specific domain" echo " 4. Scan custom path" echo "" + echo -e "${GREEN}Standalone Scanner:${NC}" + echo " 5. Launch standalone scanner (runs in background, toolkit-independent)" + echo "" echo "Results & Management:" - echo " 5. View scan results" - echo " 6. Compare scanner results" - echo " 7. Scanner settings" + echo " 6. View scan results" + echo " 7. Compare scanner results" + echo " 8. Scanner settings" echo "" echo " 0. Back to main menu" echo "" @@ -462,9 +1001,10 @@ show_scan_menu() { 2) scan_user_account ;; 3) scan_domain ;; 4) scan_custom_path ;; - 5) view_scan_results ;; - 6) compare_scan_results ;; - 7) scanner_settings ;; + 5) launch_standalone_scanner_menu ;; + 6) view_scan_results ;; + 7) compare_scan_results ;; + 8) scanner_settings ;; 0) return 0 ;; *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; esac