How to Scan Your WordPress Site for 0-Day Vulnerabilities (Before They Go Public)

Table of Contents
0-24
Hours to Exploit
94%
Attacks Within 48hrs
5
Detection Methods
24/7
Monitoring Required
🚨 CRITICAL: Why 0-Day Detection Matters
Zero-day vulnerabilities are exploited within hours of discovery—often before security patches are available. A single undetected 0-day can compromise your entire WordPress installation. The window between public disclosure and mass exploitation is typically 24 hours or less. This guide provides proactive detection strategies to identify suspicious patterns before they become known threats.

01 Understanding 0-Day Vulnerabilities

A zero-day vulnerability is a security flaw unknown to the software vendor and for which no patch exists. Unlike known vulnerabilities listed in CVE databases, 0-days cannot be detected through signature matching. They require behavioral analysis, anomaly detection, and heuristic scanning.

Why Traditional Scanners Miss 0-Days

Scanner Type Detection Method 0-Day Effectiveness Limitation
Signature-Based Pattern matching against known exploits 0% Requires prior knowledge
CVE Database Checks against published vulnerabilities 0% 0-days aren’t published yet
Behavioral Analysis Monitors suspicious activity patterns 65% High false positive rate
Anomaly Detection Baseline deviation analysis 80% Requires training period
Heuristic Scanning Code analysis for suspicious patterns 75% Resource intensive

0-Day Attack Timeline

Understanding the typical 0-day exploitation timeline is crucial for implementing effective detection:

  • Hour 0: Vulnerability discovered by researcher or attacker
  • Hours 0-24: Initial proof-of-concept developed (if ethical disclosure) OR immediate exploitation begins (if malicious)
  • Hours 24-48: Automated scanning tools adapted to target vulnerability
  • Days 2-7: Mass exploitation begins across vulnerable sites
  • Days 7-30: Vendor develops and releases patch
  • Days 30+: Becomes a “known” vulnerability in CVE databases
⚠️ THE CRITICAL WINDOW
94% of 0-day attacks occur within the first 48 hours of discovery. Your detection system must identify suspicious behavior before the vulnerability becomes publicly known. Waiting for CVE publication means you’re already compromised.

02 Detection Strategies for Unknown Threats

Since we can’t rely on signatures, we must implement multiple detection layers that identify suspicious behavior rather than known exploits.

Five-Layer Detection Framework

Detection Layer What It Monitors Detection Rate Implementation Priority
Request Analysis HTTP requests for anomalous patterns 70% Critical
File Monitoring Unauthorized file changes 85% Critical
Database Activity Unusual query patterns 65% High
User Behavior Privilege escalation attempts 75% High
Code Execution Unauthorized code execution 90% Critical

03 Behavioral Monitoring Implementation

Behavioral monitoring tracks how your WordPress site behaves and alerts on deviations from normal patterns. This catches 0-day exploits that signature-based scanners miss.

Request Pattern Monitor

This scanner monitors HTTP requests for suspicious patterns that often indicate exploitation attempts:

PHP
<?php
/**
 * Behavioral request pattern monitor.
 *
 * Detects suspicious HTTP request patterns that may indicate
 * 0-day exploitation attempts.
 *
 * @package WordPress_Security
 * @since   1.0.0
 */
class Request_Pattern_Monitor {
    /**
     * Suspicious request patterns.
     *
     * @var array
     */
    private $suspicious_patterns = array(
        'sql_injection'    => array(
            '/union\s+select/i',
            '/concat\s*\(/i',
            '/0x[0-9a-f]{2,}/i',
            '/benchmark\s*\(/i',
        ),
        'code_injection'   => array(
            '/eval\s*\(/i',
            '/base64_decode/i',
            '/system\s*\(/i',
            '/exec\s*\(/i',
        ),
        'path_traversal'   => array(
            '/\.\.\//i',
            '/\.\.\\/i',
            '/%2e%2e%2f/i',
        ),
        'xss_attempt'      => array(
            '/<script[^>]*>/i',
            '/javascript:/i',
            '/onerror\s*=/i',
            '/onload\s*=/i',
        ),
        'file_inclusion'   => array(
            '/php:\/\//i',
            '/file:\/\//i',
            '/data:\/\//i',
            '/expect:\/\//i',
        ),
    );

    /**
     * Monitor incoming request.
     *
     * @return array Detection results.
     */
    public function monitor_request() {
        $results = array(
            'timestamp'         => \current_time( 'mysql' ),
            'ip_address'        => $this->get_client_ip(),
            'request_uri'       => isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '',
            'user_agent'        => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( \wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
            'threats_detected'  => array(),
            'threat_score'      => 0,
            'action_taken'      => 'none',
        );

        // Analyze GET parameters.
        if ( ! empty( $_GET ) ) {
            foreach ( $_GET as $key => $value ) {
                $this->analyze_parameter( $key, $value, $results );
            }
        }

        // Analyze POST data.
        if ( ! empty( $_POST ) ) {
            foreach ( $_POST as $key => $value ) {
                $this->analyze_parameter( $key, $value, $results );
            }
        }

        // Analyze request headers.
        $this->analyze_headers( $results );

        // Check for rapid-fire requests (rate limiting).
        $this->check_request_rate( $results );

        // Take action if threat score is high.
        if ( $results['threat_score'] >= 100 ) {
            $this->block_request( $results );
        } elseif ( $results['threat_score'] >= 50 ) {
            $this->log_suspicious_request( $results );
        }

        return $results;
    }

    /**
     * Analyze parameter for suspicious patterns.
     *
     * @param string $key     Parameter key.
     * @param mixed  $value   Parameter value.
     * @param array  $results Results array (passed by reference).
     */
    private function analyze_parameter( $key, $value, &$results ) {
        $value_str = is_array( $value ) ? \wp_json_encode( $value ) : (string) $value;

        foreach ( $this->suspicious_patterns as $threat_type => $patterns ) {
            foreach ( $patterns as $pattern ) {
                if ( preg_match( $pattern, $value_str ) || preg_match( $pattern, $key ) ) {
                    $results['threats_detected'][] = array(
                        'type'      => $threat_type,
                        'parameter' => $key,
                        'pattern'   => $pattern,
                        'severity'  => $this->get_severity( $threat_type ),
                    );

                    $results['threat_score'] += $this->get_threat_score( $threat_type );
                }
            }
        }
    }

    /**
     * Analyze request headers for anomalies.
     *
     * @param array $results Results array (passed by reference).
     */
    private function analyze_headers( &$results ) {
        // Check for missing or suspicious User-Agent.
        if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
            $results['threats_detected'][] = array(
                'type'     => 'missing_user_agent',
                'severity' => 'medium',
            );
            $results['threat_score'] += 20;
        }

        // Check for curl/wget/scanner user agents.
        $suspicious_agents = array( 'curl', 'wget', 'python', 'nikto', 'sqlmap', 'nmap' );
        foreach ( $suspicious_agents as $agent ) {
            if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== stripos( $_SERVER['HTTP_USER_AGENT'], $agent ) ) {
                $results['threats_detected'][] = array(
                    'type'       => 'suspicious_user_agent',
                    'user_agent' => $agent,
                    'severity'   => 'high',
                );
                $results['threat_score'] += 40;
            }
        }

        // Check for unusual headers that might indicate exploitation.
        $unusual_headers = array( 'X-Forwarded-Host', 'X-Original-URL', 'X-Rewrite-URL' );
        foreach ( $unusual_headers as $header ) {
            $header_key = 'HTTP_' . str_replace( '-', '_', strtoupper( $header ) );
            if ( isset( $_SERVER[ $header_key ] ) ) {
                $results['threats_detected'][] = array(
                    'type'     => 'unusual_header',
                    'header'   => $header,
                    'severity' => 'medium',
                );
                $results['threat_score'] += 15;
            }
        }
    }

    /**
     * Check request rate for potential DDoS or brute force.
     *
     * @param array $results Results array (passed by reference).
     */
    private function check_request_rate( &$results ) {
        $ip = $this->get_client_ip();
        $transient_key = 'request_count_' . md5( $ip );
        $request_count = get_transient( $transient_key );

        if ( false === $request_count ) {
            set_transient( $transient_key, 1, MINUTE_IN_SECONDS );
        } else {
            $request_count++;
            set_transient( $transient_key, $request_count, MINUTE_IN_SECONDS );

            // More than 60 requests per minute is suspicious.
            if ( $request_count > 60 ) {
                $results['threats_detected'][] = array(
                    'type'          => 'rate_limit_exceeded',
                    'request_count' => $request_count,
                    'severity'      => 'high',
                );
                $results['threat_score'] += 50;
            }
        }
    }

    /**
     * Get client IP address.
     *
     * @return string IP address.
     */
    private function get_client_ip() {
        $ip = '';

        if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
        } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
        } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
        }

        return $ip;
    }

    /**
     * Get severity level for threat type.
     *
     * @param string $threat_type Threat type.
     * @return string Severity level.
     */
    private function get_severity( $threat_type ) {
        $severities = array(
            'sql_injection'  => 'critical',
            'code_injection' => 'critical',
            'file_inclusion' => 'critical',
            'path_traversal' => 'high',
            'xss_attempt'    => 'high',
        );

        return isset( $severities[ $threat_type ] ) ? $severities[ $threat_type ] : 'medium';
    }

    /**
     * Get numeric threat score for threat type.
     *
     * @param string $threat_type Threat type.
     * @return int Threat score.
     */
    private function get_threat_score( $threat_type ) {
        $scores = array(
            'sql_injection'  => 100,
            'code_injection' => 100,
            'file_inclusion' => 100,
            'path_traversal' => 75,
            'xss_attempt'    => 50,
        );

        return isset( $scores[ $threat_type ] ) ? $scores[ $threat_type ] : 25;
    }

    /**
     * Block request and send 403 response.
     *
     * @param array $results Detection results.
     */
    private function block_request( $results ) {
        // Log the blocked request.
        $this->log_blocked_request( $results );

        // Send notification.
        $this->send_security_alert( $results );

        // Block the request.
        \wp_die(
            esc_html__( 'Access Denied: Suspicious activity detected.', 'security' ),
            esc_html__( 'Security Alert', 'security' ),
            array(
                'response'  => 403,
                'back_link' => false,
            )
        );
    }

    /**
     * Log suspicious request for analysis.
     *
     * @param array $results Detection results.
     */
    private function log_suspicious_request( $results ) {
        global $wpdb;

        $table_name = $wpdb->prefix . 'security_requests';

        $wpdb->insert(
            $table_name,
            array(
                'timestamp'        => $results['timestamp'],
                'ip_address'       => $results['ip_address'],
                'request_uri'      => $results['request_uri'],
                'user_agent'       => $results['user_agent'],
                'threat_score'     => $results['threat_score'],
                'threats_detected' => \wp_json_encode( $results['threats_detected'] ),
                'action_taken'     => 'logged',
            ),
            array( '%s', '%s', '%s', '%s', '%d', '%s', '%s' )
        );
    }

    /**
     * Log blocked request.
     *
     * @param array $results Detection results.
     */
    private function log_blocked_request( $results ) {
        $results['action_taken'] = 'blocked';
        $this->log_suspicious_request( $results );
    }

    /**
     * Send security alert email.
     *
     * @param array $results Detection results.
     */
    private function send_security_alert( $results ) {
        $to = get_option( 'admin_email' );
        $subject = sprintf(
            '[SECURITY ALERT] Potential 0-Day Exploitation Attempt Blocked - %s',
            get_bloginfo( 'name' )
        );

        $message = sprintf(
            "A high-threat security event was detected and blocked on your WordPress site.\n\n" .
            "Timestamp: %s\n" .
            "IP Address: %s\n" .
            "Threat Score: %d/100\n" .
            "Request URI: %s\n" .
            "User Agent: %s\n\n" .
            "Threats Detected:\n%s\n\n" .
            "The request was automatically blocked. Please review your security logs.",
            $results['timestamp'],
            $results['ip_address'],
            $results['threat_score'],
            $results['request_uri'],
            $results['user_agent'],
            \wp_json_encode( $results['threats_detected'], JSON_PRETTY_PRINT )
        );

        \wp_mail( $to, $subject, $message );
    }
}

/**
 * Initialize request monitoring.
 *
 * @since 1.0.0
 */
function init_request_monitoring() {
    // Don't monitor admin requests for logged-in users.
    if ( is_admin() && is_user_logged_in() ) {
        return;
    }

    $monitor = new Request_Pattern_Monitor();
    $monitor->monitor_request();
}
\add_action( 'init', 'init_request_monitoring', 1 );
💡 HOW THIS WORKS
This monitor analyzes every incoming HTTP request for suspicious patterns. It assigns a threat score based on detected anomalies. Requests scoring 100+ are immediately blocked, while scores of 50-99 are logged for review. This catches exploitation attempts that don’t match any known CVE signature.

04 Anomaly Detection System

Anomaly detection establishes a baseline of normal behavior and alerts when activities deviate significantly. This is crucial for 0-day detection since we don’t know what the attack looks like—we only know it’s different from normal operations.

File System Anomaly Detector

PHP
<?php
/**
 * File system anomaly detection.
 *
 * Monitors file system changes and detects unusual patterns
 * that may indicate 0-day exploitation.
 *
 * @package WordPress_Security
 * @since   1.0.0
 */
class File_System_Anomaly_Detector {
    /**
     * Baseline snapshot transient key.
     *
     * @var string
     */
    private $baseline_key = 'fs_baseline_snapshot';

    /**
     * Create baseline snapshot of file system.
     *
     * @return array Baseline data.
     */
    public function create_baseline() {
        $baseline = array(
            'created_at'    => \current_time( 'mysql' ),
            'core_files'    => $this->scan_directory( ABSPATH . 'wp-admin' ),
            'includes'      => $this->scan_directory( ABSPATH . 'wp-includes' ),
            'plugins'       => $this->scan_directory( WP_PLUGIN_DIR ),
            'themes'        => $this->scan_directory( get_theme_root() ),
            'uploads'       => $this->scan_directory( \wp_upload_dir()['basedir'], 'uploads' ),
        );

        set_transient( $this->baseline_key, $baseline, WEEK_IN_SECONDS );

        return $baseline;
    }

    /**
     * Detect anomalies against baseline.
     *
     * @return array Anomalies detected.
     */
    public function detect_anomalies() {
        $baseline = get_transient( $this->baseline_key );

        if ( ! $baseline ) {
            return array(
                'error' => 'No baseline exists. Run create_baseline() first.',
            );
        }

        $anomalies = array(
            'timestamp'     => \current_time( 'mysql' ),
            'new_files'     => array(),
            'modified_files' => array(),
            'deleted_files' => array(),
            'suspicious'    => array(),
        );

        // Check each directory against baseline.
        $directories = array( 'core_files', 'includes', 'plugins', 'themes', 'uploads' );

        foreach ( $directories as $dir_key ) {
            if ( ! isset( $baseline[ $dir_key ] ) ) {
                continue;
            }

            $current = $this->scan_directory( $this->get_directory_path( $dir_key ), $dir_key );
            $this->compare_snapshots( $baseline[ $dir_key ], $current, $anomalies, $dir_key );
        }

        // Analyze patterns for 0-day indicators.
        $this->analyze_anomaly_patterns( $anomalies );

        return $anomalies;
    }

    /**
     * Scan directory and create fingerprint.
     *
     * @param string $directory Directory path.
     * @param string $type      Directory type (optional).
     * @return array File fingerprints.
     */
    private function scan_directory( $directory, $type = '' ) {
        $files = array();

        if ( ! is_dir( $directory ) ) {
            return $files;
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator( $directory, RecursiveDirectoryIterator::SKIP_DOTS ),
            RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ( $iterator as $file ) {
            if ( ! $file->isFile() ) {
                continue;
            }

            $relative_path = str_replace( ABSPATH, '', $file->getPathname() );
            $extension = strtolower( $file->getExtension() );

            $files[ $relative_path ] = array(
                'size'      => $file->getSize(),
                'modified'  => $file->getMTime(),
                'hash'      => 'uploads' !== $type ? md5_file( $file->getPathname() ) : null,
                'extension' => $extension,
            );
        }

        return $files;
    }

    /**
     * Compare baseline and current snapshots.
     *
     * @param array  $baseline  Baseline snapshot.
     * @param array  $current   Current snapshot.
     * @param array  $anomalies Anomalies array (passed by reference).
     * @param string $dir_key   Directory key.
     */
    private function compare_snapshots( $baseline, $current, &$anomalies, $dir_key ) {
        // Check for new files.
        foreach ( $current as $file_path => $file_data ) {
            if ( ! isset( $baseline[ $file_path ] ) ) {
                $anomalies['new_files'][] = array(
                    'file'      => $file_path,
                    'directory' => $dir_key,
                    'size'      => $file_data['size'],
                    'extension' => $file_data['extension'],
                    'severity'  => $this->assess_new_file_severity( $file_path, $file_data, $dir_key ),
                );
            } elseif ( $file_data['hash'] && $baseline[ $file_path ]['hash'] !== $file_data['hash'] ) {
                // File modified.
                $anomalies['modified_files'][] = array(
                    'file'         => $file_path,
                    'directory'    => $dir_key,
                    'old_hash'     => $baseline[ $file_path ]['hash'],
                    'new_hash'     => $file_data['hash'],
                    'size_change'  => $file_data['size'] - $baseline[ $file_path ]['size'],
                    'severity'     => $this->assess_modification_severity( $file_path, $dir_key ),
                );
            }
        }

        // Check for deleted files.
        foreach ( $baseline as $file_path => $file_data ) {
            if ( ! isset( $current[ $file_path ] ) ) {
                $anomalies['deleted_files'][] = array(
                    'file'      => $file_path,
                    'directory' => $dir_key,
                    'severity'  => 'uploads' === $dir_key ? 'low' : 'high',
                );
            }
        }
    }

    /**
     * Assess severity of new file.
     *
     * @param string $file_path File path.
     * @param array  $file_data File data.
     * @param string $dir_key   Directory key.
     * @return string Severity level.
     */
    private function assess_new_file_severity( $file_path, $file_data, $dir_key ) {
        // PHP files in uploads directory are critical.
        if ( 'uploads' === $dir_key && 'php' === $file_data['extension'] ) {
            return 'critical';
        }

        // New files in core directories are highly suspicious.
        if ( in_array( $dir_key, array( 'core_files', 'includes' ), true ) ) {
            return 'critical';
        }

        // Suspicious extensions.
        $suspicious_ext = array( 'phtml', 'php3', 'php4', 'php5', 'phar', 'phps' );
        if ( in_array( $file_data['extension'], $suspicious_ext, true ) ) {
            return 'high';
        }

        return 'medium';
    }

    /**
     * Assess severity of file modification.
     *
     * @param string $file_path File path.
     * @param string $dir_key   Directory key.
     * @return string Severity level.
     */
    private function assess_modification_severity( $file_path, $dir_key ) {
        // Core file modifications are critical.
        if ( in_array( $dir_key, array( 'core_files', 'includes' ), true ) ) {
            return 'critical';
        }

        // Plugin/theme modifications could be legitimate updates.
        if ( in_array( $dir_key, array( 'plugins', 'themes' ), true ) ) {
            return 'medium';
        }

        return 'low';
    }

    /**
     * Analyze anomaly patterns for 0-day indicators.
     *
     * @param array $anomalies Anomalies array (passed by reference).
     */
    private function analyze_anomaly_patterns( &$anomalies ) {
        // Pattern 1: Multiple PHP files created in short timespan.
        $php_files = array_filter(
            $anomalies['new_files'],
            function( $file ) {
                return 'php' === $file['extension'];
            }
        );

        if ( count( $php_files ) >= 3 ) {
            $anomalies['suspicious'][] = array(
                'pattern'     => 'mass_php_creation',
                'description' => sprintf( '%d PHP files created', count( $php_files ) ),
                'severity'    => 'critical',
                'indicator'   => 'Possible backdoor deployment',
            );
        }

        // Pattern 2: Core file modifications.
        $core_mods = array_filter(
            $anomalies['modified_files'],
            function( $file ) {
                return in_array( $file['directory'], array( 'core_files', 'includes' ), true );
            }
        );

        if ( ! empty( $core_mods ) ) {
            $anomalies['suspicious'][] = array(
                'pattern'     => 'core_modification',
                'description' => sprintf( '%d core files modified', count( $core_mods ) ),
                'severity'    => 'critical',
                'indicator'   => 'Possible core compromise',
            );
        }

        // Pattern 3: Files created outside business hours.
        $this->check_temporal_anomalies( $anomalies );
    }

    /**
     * Check for temporal anomalies (files created at unusual times).
     *
     * @param array $anomalies Anomalies array (passed by reference).
     */
    private function check_temporal_anomalies( &$anomalies ) {
        $current_hour = (int) \current_time( 'H' );

        // Define business hours (8 AM - 6 PM).
        $is_business_hours = ( $current_hour >= 8 && $current_hour < 18 );

        if ( ! $is_business_hours && ! empty( $anomalies['new_files'] ) ) {
            $anomalies['suspicious'][] = array(
                'pattern'     => 'after_hours_activity',
                'description' => 'Files created outside business hours',
                'severity'    => 'high',
                'indicator'   => 'Unusual timing may indicate automated attack',
            );
        }
    }

    /**
     * Get directory path from key.
     *
     * @param string $dir_key Directory key.
     * @return string Directory path.
     */
    private function get_directory_path( $dir_key ) {
        $paths = array(
            'core_files' => ABSPATH . 'wp-admin',
            'includes'   => ABSPATH . 'wp-includes',
            'plugins'    => WP_PLUGIN_DIR,
            'themes'     => get_theme_root(),
            'uploads'    => \wp_upload_dir()['basedir'],
        );

        return isset( $paths[ $dir_key ] ) ? $paths[ $dir_key ] : '';
    }
}

/**
 * Schedule baseline creation and anomaly detection.
 *
 * @since 1.0.0
 */
function schedule_anomaly_detection() {
    // Create baseline weekly.
    if ( ! \wp_next_scheduled( 'create_fs_baseline' ) ) {
        \wp_schedule_event( time(), 'weekly', 'create_fs_baseline' );
    }

    // Check for anomalies hourly.
    if ( ! \wp_next_scheduled( 'detect_fs_anomalies' ) ) {
        \wp_schedule_event( time(), 'hourly', 'detect_fs_anomalies' );
    }
}
\add_action( 'init', 'schedule_anomaly_detection' );

/**
 * Create baseline snapshot.
 *
 * @since 1.0.0
 */
function create_baseline_snapshot() {
    $detector = new File_System_Anomaly_Detector();
    $detector->create_baseline();
}
\add_action( 'create_fs_baseline', 'create_baseline_snapshot' );

/**
 * Detect and report anomalies.
 *
 * @since 1.0.0
 */
function detect_and_report_anomalies() {
    $detector = new File_System_Anomaly_Detector();
    $anomalies = $detector->detect_anomalies();

    // Count critical issues.
    $critical_count = 0;

    foreach ( array( 'new_files', 'modified_files' ) as $type ) {
        if ( ! empty( $anomalies[ $type ] ) ) {
            foreach ( $anomalies[ $type ] as $item ) {
                if ( 'critical' === $item['severity'] ) {
                    $critical_count++;
                }
            }
        }
    }

    // Send alert if critical anomalies detected.
    if ( $critical_count > 0 || ! empty( $anomalies['suspicious'] ) ) {
        send_anomaly_alert( $anomalies );
    }
}
\add_action( 'detect_fs_anomalies', 'detect_and_report_anomalies' );

/**
 * Send anomaly detection alert.
 *
 * @param array $anomalies Detected anomalies.
 * @since 1.0.0
 */
function send_anomaly_alert( $anomalies ) {
    $to = get_option( 'admin_email' );
    $subject = '[SECURITY] File System Anomalies Detected - Possible 0-Day Exploit';

    $message = "File system anomalies have been detected that may indicate a 0-day exploitation attempt.\n\n";

    if ( ! empty( $anomalies['new_files'] ) ) {
        $message .= sprintf( "New Files: %d\n", count( $anomalies['new_files'] ) );
    }

    if ( ! empty( $anomalies['modified_files'] ) ) {
        $message .= sprintf( "Modified Files: %d\n", count( $anomalies['modified_files'] ) );
    }

    if ( ! empty( $anomalies['suspicious'] ) ) {
        $message .= "\nSuspicious Patterns:\n";
        foreach ( $anomalies['suspicious'] as $pattern ) {
            $message .= sprintf(
                "- [%s] %s: %s\n",
                strtoupper( $pattern['severity'] ),
                $pattern['pattern'],
                $pattern['description']
            );
        }
    }

    $message .= "\nPlease investigate immediately.\n";

    \wp_mail( $to, $subject, $message );
}
⏱️ TIMING IS EVERYTHING
This detector runs hourly scans against a baseline snapshot. It flags files created at unusual times (e.g., 3 AM on weekends) which often indicates automated exploitation. The baseline is refreshed weekly to account for legitimate updates.

05 Code Pattern Analysis

Heuristic code analysis examines PHP files for suspicious patterns that don’t match specific signatures but indicate malicious intent. This is particularly effective against 0-day backdoors.

Heuristic Code Scanner

PHP
<?php
/**
 * Heuristic code pattern analyzer.
 *
 * Scans PHP code for suspicious patterns that may indicate
 * 0-day backdoors or unknown exploits.
 *
 * @package WordPress_Security
 * @since   1.0.0
 */
class Heuristic_Code_Scanner {
    /**
     * Suspicious code patterns (heuristics).
     *
     * @var array
     */
    private $heuristic_patterns = array(
        'obfuscation'       => array(
            'base64_chain'       => '/base64_decode\s*\(\s*base64_decode/',
            'gzinflate_chain'    => '/gzinflate\s*\(\s*base64_decode/',
            'str_rot13_chain'    => '/str_rot13\s*\(\s*base64_decode/',
            'variable_functions' => '/\$[a-zA-Z_]+\s*\(\s*\$/',
        ),
        'dynamic_execution' => array(
            'eval_variable'      => '/eval\s*\(\s*\$/',
            'assert_variable'    => '/assert\s*\(\s*\$/',
            'create_function'    => '/create_function/',
            'preg_replace_e'     => '/preg_replace\s*\([^)]*\/[imsxeADSUXJu]*e/',
        ),
        'remote_control'    => array(
            'file_get_contents_var' => '/file_get_contents\s*\(\s*\$_(GET|POST|REQUEST)/',
            'curl_exec_post'     => '/curl_exec\s*\(.*\$_(GET|POST|REQUEST)/',
            'fopen_remote'       => '/fopen\s*\(\s*[\'"]https?:\/\//',
        ),
        'data_exfiltration' => array(
            'mail_post'          => '/mail\s*\(.*\$_(POST|REQUEST|COOKIE)/',
            'file_put_post'      => '/file_put_contents\s*\(.*\$_(POST|REQUEST)/',
        ),
    );

    /**
     * Scan file for suspicious code patterns.
     *
     * @param string $file_path Path to PHP file.
     * @return array Detection results.
     */
    public function scan_file( $file_path ) {
        $results = array(
            'file'              => $file_path,
            'scan_time'         => \current_time( 'mysql' ),
            'risk_score'        => 0,
            'patterns_detected' => array(),
            'code_metrics'      => array(),
        );

        if ( ! file_exists( $file_path ) || 'php' !== pathinfo( $file_path, PATHINFO_EXTENSION ) ) {
            return $results;
        }

        $code = file_get_contents( $file_path );

        if ( false === $code ) {
            return $results;
        }

        // Scan for heuristic patterns.
        foreach ( $this->heuristic_patterns as $category => $patterns ) {
            foreach ( $patterns as $pattern_name => $regex ) {
                if ( preg_match( $regex, $code, $matches ) ) {
                    $results['patterns_detected'][] = array(
                        'category' => $category,
                        'pattern'  => $pattern_name,
                        'match'    => substr( $matches[0], 0, 100 ),
                        'severity' => $this->get_pattern_severity( $category ),
                    );

                    $results['risk_score'] += $this->get_pattern_score( $category );
                }
            }
        }

        // Calculate code metrics.
        $results['code_metrics'] = $this->analyze_code_metrics( $code );

        // Adjust risk score based on metrics.
        $results['risk_score'] += $this->calculate_metric_risk( $results['code_metrics'] );

        return $results;
    }

    /**
     * Analyze code metrics for suspiciousness.
     *
     * @param string $code PHP code.
     * @return array Code metrics.
     */
    private function analyze_code_metrics( $code ) {
        return array(
            'file_size'         => strlen( $code ),
            'base64_count'      => substr_count( $code, 'base64_decode' ),
            'eval_count'        => substr_count( $code, 'eval(' ),
            'obfuscation_ratio' => $this->calculate_obfuscation_ratio( $code ),
            'comment_ratio'     => $this->calculate_comment_ratio( $code ),
            'has_long_strings'  => preg_match( '/[\'"][a-zA-Z0-9+\/]{200,}[\'"]/', $code ) ? true : false,
            'has_hex_strings'   => preg_match( '/\\\\x[0-9a-f]{2}/i', $code ) ? true : false,
        );
    }

    /**
     * Calculate obfuscation ratio.
     *
     * @param string $code PHP code.
     * @return float Obfuscation ratio (0-1).
     */
    private function calculate_obfuscation_ratio( $code ) {
        $total_length = strlen( $code );

        if ( 0 === $total_length ) {
            return 0;
        }

        // Count readable characters.
        $readable = preg_match_all( '/[a-zA-Z0-9\s\.\,\;\:\!\?]/', $code );

        return 1 - ( $readable / $total_length );
    }

    /**
     * Calculate comment ratio.
     *
     * @param string $code PHP code.
     * @return float Comment ratio (0-1).
     */
    private function calculate_comment_ratio( $code ) {
        $total_lines = substr_count( $code, "\n" ) + 1;

        if ( 0 === $total_lines ) {
            return 0;
        }

        $comment_lines = preg_match_all( '/^\s*(\/\/|\/\*|\*)/m', $code );

        return $comment_lines / $total_lines;
    }

    /**
     * Calculate risk score from code metrics.
     *
     * @param array $metrics Code metrics.
     * @return int Additional risk score.
     */
    private function calculate_metric_risk( $metrics ) {
        $risk = 0;

        // Excessive base64 usage.
        if ( $metrics['base64_count'] > 5 ) {
            $risk += 30;
        }

        // Multiple eval calls.
        if ( $metrics['eval_count'] > 2 ) {
            $risk += 40;
        }

        // High obfuscation.
        if ( $metrics['obfuscation_ratio'] > 0.7 ) {
            $risk += 50;
        }

        // No comments (unusual for legitimate code).
        if ( $metrics['comment_ratio'] < 0.05 && $metrics['file_size'] > 1000 ) {
            $risk += 20;
        }

        // Long encoded strings.
        if ( $metrics['has_long_strings'] ) {
            $risk += 25;
        }

        // Hex-encoded strings.
        if ( $metrics['has_hex_strings'] ) {
            $risk += 15;
        }

        return $risk;
    }

    /**
     * Get severity level for pattern category.
     *
     * @param string $category Pattern category.
     * @return string Severity level.
     */
    private function get_pattern_severity( $category ) {
        $severities = array(
            'obfuscation'       => 'high',
            'dynamic_execution' => 'critical',
            'remote_control'    => 'critical',
            'data_exfiltration' => 'critical',
        );

        return isset( $severities[ $category ] ) ? $severities[ $category ] : 'medium';
    }

    /**
     * Get risk score for pattern category.
     *
     * @param string $category Pattern category.
     * @return int Risk score.
     */
    private function get_pattern_score( $category ) {
        $scores = array(
            'obfuscation'       => 40,
            'dynamic_execution' => 60,
            'remote_control'    => 80,
            'data_exfiltration' => 70,
        );

        return isset( $scores[ $category ] ) ? $scores[ $category ] : 25;
    }

    /**
     * Scan directory recursively.
     *
     * @param string $directory Directory path.
     * @return array Scan results for all files.
     */
    public function scan_directory( $directory ) {
        $results = array(
            'scan_time'       => \current_time( 'mysql' ),
            'directory'       => $directory,
            'files_scanned'   => 0,
            'suspicious_files' => array(),
        );

        if ( ! is_dir( $directory ) ) {
            return $results;
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator( $directory, RecursiveDirectoryIterator::SKIP_DOTS )
        );

        foreach ( $iterator as $file ) {
            if ( ! $file->isFile() || 'php' !== strtolower( $file->getExtension() ) ) {
                continue;
            }

            $results['files_scanned']++;

            $scan_result = $this->scan_file( $file->getPathname() );

            // Flag files with risk score >= 50.
            if ( $scan_result['risk_score'] >= 50 ) {
                $results['suspicious_files'][] = array(
                    'file'       => str_replace( ABSPATH, '', $file->getPathname() ),
                    'risk_score' => $scan_result['risk_score'],
                    'patterns'   => count( $scan_result['patterns_detected'] ),
                    'details'    => $scan_result,
                );
            }
        }

        return $results;
    }
}

/**
 * WP-CLI command for heuristic scanning.
 *
 * Usage: wp security-scan heuristic <directory>
 *
 * @param array $args Positional arguments.
 * @since 1.0.0
 */
function cli_heuristic_scan( $args ) {
    if ( empty( $args[0] ) ) {
        WP_CLI::error( 'Please specify a directory to scan' );
        return;
    }

    $directory = $args[0];

    WP_CLI::log( sprintf( 'Running heuristic scan on: %s', $directory ) );

    $scanner = new Heuristic_Code_Scanner();
    $results = $scanner->scan_directory( $directory );

    WP_CLI::log( sprintf( "\nFiles Scanned: %d", $results['files_scanned'] ) );

    if ( ! empty( $results['suspicious_files'] ) ) {
        WP_CLI::error(
            sprintf( "\nSuspicious Files: %d", count( $results['suspicious_files'] ) ),
            false
        );

        foreach ( $results['suspicious_files'] as $file ) {
            WP_CLI::log( sprintf(
                "\n  File: %s\n  Risk Score: %d/100\n  Patterns Detected: %d",
                $file['file'],
                $file['risk_score'],
                $file['patterns']
            ) );
        }
    } else {
        WP_CLI::success( 'No suspicious files detected' );
    }
}

if ( defined( 'WP_CLI' ) && WP_CLI ) {
    WP_CLI::add_command( 'security-scan heuristic', 'cli_heuristic_scan' );
}
✅ HEURISTIC ADVANTAGE
Unlike signature-based scanning, heuristic analysis can detect never-before-seen backdoors. A file with high obfuscation, multiple base64_decode chains, and no comments scores 100+ regardless of whether it matches any known malware signature.

06 REST API & AJAX Monitoring

WordPress REST API and AJAX endpoints are common 0-day exploitation vectors. Monitoring these for unusual activity can catch exploits early.

API Request Monitor

PHP
<?php
/**
 * REST API and AJAX endpoint monitor.
 *
 * Tracks API usage patterns and detects anomalous behavior
 * that may indicate 0-day exploitation.
 *
 * @package WordPress_Security
 * @since   1.0.0
 */
class API_Endpoint_Monitor {
    /**
     * Monitor REST API request.
     *
     * @param WP_REST_Response $response Response object.
     * @param WP_REST_Server   $server   Server instance.
     * @param WP_REST_Request  $request  Request object.
     * @return WP_REST_Response Response object.
     */
    public function monitor_rest_request( $response, $server, $request ) {
        $endpoint = $request->get_route();
        $method = $request->get_method();
        $params = $request->get_params();

        $log_entry = array(
            'timestamp' => \current_time( 'mysql' ),
            'type'      => 'rest_api',
            'endpoint'  => $endpoint,
            'method'    => $method,
            'ip'        => $this->get_client_ip(),
            'user_id'   => get_current_user_id(),
            'params'    => \wp_json_encode( $params ),
        );

        // Check for suspicious patterns.
        $is_suspicious = $this->analyze_api_request( $endpoint, $method, $params );

        if ( $is_suspicious ) {
            $this->log_suspicious_api_request( $log_entry );
            $this->send_api_alert( $log_entry );
        }

        return $response;
    }

    /**
     * Monitor AJAX request.
     */
    public function monitor_ajax_request() {
        $action = isset( $_REQUEST['action'] ) ? sanitize_text_field( \wp_unslash( $_REQUEST['action'] ) ) : '';

        $log_entry = array(
            'timestamp' => \current_time( 'mysql' ),
            'type'      => 'ajax',
            'action'    => $action,
            'ip'        => $this->get_client_ip(),
            'user_id'   => get_current_user_id(),
            'params'    => \wp_json_encode( $_REQUEST ),
        );

        // Check for suspicious AJAX actions.
        if ( $this->is_suspicious_ajax_action( $action ) ) {
            $this->log_suspicious_api_request( $log_entry );
        }

        // Check request rate.
        $this->check_ajax_rate_limit( $action, $log_entry );
    }

    /**
     * Analyze API request for suspicious patterns.
     *
     * @param string $endpoint Endpoint route.
     * @param string $method   HTTP method.
     * @param array  $params   Request parameters.
     * @return bool True if suspicious.
     */
    private function analyze_api_request( $endpoint, $method, $params ) {
        // Check for unauthorized user endpoints.
        if ( preg_match( '/\/wp\/v2\/users/', $endpoint ) && ! is_user_logged_in() ) {
            return true;
        }

        // POST/PUT/DELETE requests to sensitive endpoints.
        if ( in_array( $method, array( 'POST', 'PUT', 'DELETE', 'PATCH' ), true ) ) {
            $sensitive_endpoints = array( '/wp/v2/users', '/wp/v2/settings', '/wp/v2/plugins' );

            foreach ( $sensitive_endpoints as $sensitive ) {
                if ( false !== strpos( $endpoint, $sensitive ) ) {
                    return true;
                }
            }
        }

        // Check for SQL injection patterns in parameters.
        foreach ( $params as $key => $value ) {
            if ( is_string( $value ) && preg_match( '/union\s+select|concat\s*\(/i', $value ) ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if AJAX action is suspicious.
     *
     * @param string $action AJAX action name.
     * @return bool True if suspicious.
     */
    private function is_suspicious_ajax_action( $action ) {
        // Unknown actions.
        if ( empty( $action ) ) {
            return true;
        }

        // Actions that shouldn't be called by unauthenticated users.
        $privileged_actions = array(
            'edit_theme',
            'edit_plugin',
            'install_plugin',
            'delete_plugin',
            'update_plugin',
            'create_user',
            'delete_user',
        );

        if ( in_array( $action, $privileged_actions, true ) && ! is_user_logged_in() ) {
            return true;
        }

        return false;
    }

    /**
     * Check AJAX request rate limit.
     *
     * @param string $action    AJAX action.
     * @param array  $log_entry Log entry data.
     */
    private function check_ajax_rate_limit( $action, $log_entry ) {
        $ip = $this->get_client_ip();
        $transient_key = 'ajax_rate_' . md5( $ip . $action );
        $request_count = get_transient( $transient_key );

        if ( false === $request_count ) {
            set_transient( $transient_key, 1, MINUTE_IN_SECONDS );
        } else {
            $request_count++;
            set_transient( $transient_key, $request_count, MINUTE_IN_SECONDS );

            // More than 30 identical AJAX requests per minute is suspicious.
            if ( $request_count > 30 ) {
                $log_entry['reason'] = sprintf( 'Rate limit exceeded: %d requests/min', $request_count );
                $this->log_suspicious_api_request( $log_entry );
                $this->send_api_alert( $log_entry );
            }
        }
    }

    /**
     * Get client IP address.
     *
     * @return string IP address.
     */
    private function get_client_ip() {
        $ip = '';

        if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
        } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
        } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
        }

        return $ip;
    }

    /**
     * Log suspicious API request.
     *
     * @param array $log_entry Log entry data.
     */
    private function log_suspicious_api_request( $log_entry ) {
        global $wpdb;

        $table_name = $wpdb->prefix . 'security_api_requests';

        $wpdb->insert(
            $table_name,
            array(
                'timestamp' => $log_entry['timestamp'],
                'type'      => $log_entry['type'],
                'endpoint'  => $log_entry['endpoint'] ?? $log_entry['action'],
                'method'    => $log_entry['method'] ?? 'AJAX',
                'ip'        => $log_entry['ip'],
                'user_id'   => $log_entry['user_id'],
                'params'    => $log_entry['params'],
                'reason'    => $log_entry['reason'] ?? 'Suspicious pattern detected',
            ),
            array( '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s' )
        );
    }

    /**
     * Send API security alert.
     *
     * @param array $log_entry Log entry data.
     */
    private function send_api_alert( $log_entry ) {
        $to = get_option( 'admin_email' );
        $subject = '[SECURITY] Suspicious API Activity Detected';

        $message = sprintf(
            "Suspicious API activity has been detected.\n\n" .
            "Type: %s\n" .
            "Endpoint/Action: %s\n" .
            "IP Address: %s\n" .
            "User ID: %s\n" .
            "Timestamp: %s\n" .
            "Reason: %s\n\n" .
            "This may indicate a 0-day exploitation attempt. Please investigate.",
            $log_entry['type'],
            $log_entry['endpoint'] ?? $log_entry['action'],
            $log_entry['ip'],
            $log_entry['user_id'] ? $log_entry['user_id'] : 'Guest',
            $log_entry['timestamp'],
            $log_entry['reason'] ?? 'Suspicious pattern detected'
        );

        \wp_mail( $to, $subject, $message );
    }
}

/**
 * Initialize API monitoring.
 *
 * @since 1.0.0
 */
function init_api_monitoring() {
    $monitor = new API_Endpoint_Monitor();

    // Monitor REST API requests.
    \add_filter( 'rest_post_dispatch', array( $monitor, 'monitor_rest_request' ), 10, 3 );

    // Monitor AJAX requests.
    \add_action( 'admin_init', array( $monitor, 'monitor_ajax_request' ), 1 );
    \add_action( 'wp_ajax_nopriv_*', array( $monitor, 'monitor_ajax_request' ), 1 );
}
\add_action( 'init', 'init_api_monitoring' );
🔍 API ATTACK PATTERNS
0-day exploits often target WordPress REST API endpoints because they’re externally accessible and can be exploited without authentication. Common patterns include:

  • Unauthenticated access to /wp/v2/users endpoint
  • Rapid-fire AJAX requests testing different payloads
  • POST requests to /wp/v2/settings from unknown IPs
  • Parameter tampering with SQL injection patterns

07 Automated Threat Response

When a potential 0-day exploit is detected, immediate automated response can prevent or limit damage while human administrators are alerted.

Automated Response Framework

Threat Level Automatic Actions Response Time Manual Review
Low (0-25) Log only, no action N/A Weekly review
Medium (26-50) Log + email alert Within 1 hour Daily review
High (51-75) Block IP + alert + increase monitoring Immediate Immediate
Critical (76-100) Block IP + quarantine files + alert + optional site lockdown Immediate Immediate

Incident Response Automation

PHP
<?php
/**
 * Automated incident response system.
 *
 * Takes automated actions when threats are detected.
 *
 * @package WordPress_Security
 * @since   1.0.0
 */
class Automated_Incident_Response {
    /**
     * Handle detected threat.
     *
     * @param array $threat_data Threat detection data.
     */
    public function handle_threat( $threat_data ) {
        $threat_level = $this->calculate_threat_level( $threat_data );

        $response = array(
            'threat_id'     => uniqid( 'threat_' ),
            'timestamp'     => \current_time( 'mysql' ),
            'threat_level'  => $threat_level,
            'threat_data'   => $threat_data,
            'actions_taken' => array(),
        );

        // Take action based on threat level.
        if ( $threat_level >= 76 ) {
            $this->handle_critical_threat( $threat_data, $response );
        } elseif ( $threat_level >= 51 ) {
            $this->handle_high_threat( $threat_data, $response );
        } elseif ( $threat_level >= 26 ) {
            $this->handle_medium_threat( $threat_data, $response );
        } else {
            $this->handle_low_threat( $threat_data, $response );
        }

        // Log response.
        $this->log_incident_response( $response );

        return $response;
    }

    /**
     * Calculate overall threat level.
     *
     * @param array $threat_data Threat data.
     * @return int Threat level (0-100).
     */
    private function calculate_threat_level( $threat_data ) {
        $level = 0;

        // Add scores from different detection methods.
        if ( isset( $threat_data['threat_score'] ) ) {
            $level += $threat_data['threat_score'];
        }

        if ( isset( $threat_data['risk_score'] ) ) {
            $level += $threat_data['risk_score'];
        }

        // Check for multiple simultaneous threats.
        if ( isset( $threat_data['threats_detected'] ) && count( $threat_data['threats_detected'] ) > 3 ) {
            $level += 25;
        }

        return min( 100, $level );
    }

    /**
     * Handle critical threat (76-100).
     *
     * @param array $threat_data Threat data.
     * @param array $response    Response data (passed by reference).
     */
    private function handle_critical_threat( $threat_data, &$response ) {
        // Block IP immediately.
        $ip = $threat_data['ip_address'] ?? $this->get_client_ip();
        $this->block_ip_address( $ip );
        $response['actions_taken'][] = 'ip_blocked';

        // Quarantine suspicious files if file-based threat.
        if ( isset( $threat_data['file'] ) ) {
            $this->quarantine_file( $threat_data['file'] );
            $response['actions_taken'][] = 'file_quarantined';
        }

        // Send immediate critical alert.
        $this->send_critical_alert( $threat_data );
        $response['actions_taken'][] = 'critical_alert_sent';

        // Optional: Enable maintenance mode.
        $auto_lockdown = get_option( 'security_auto_lockdown', false );
        if ( $auto_lockdown ) {
            $this->enable_maintenance_mode();
            $response['actions_taken'][] = 'maintenance_mode_enabled';
        }

        // Increase monitoring frequency.
        $this->increase_monitoring_frequency();
        $response['actions_taken'][] = 'monitoring_increased';
    }

    /**
     * Handle high threat (51-75).
     *
     * @param array $threat_data Threat data.
     * @param array $response    Response data (passed by reference).
     */
    private function handle_high_threat( $threat_data, &$response ) {
        // Block IP.
        $ip = $threat_data['ip_address'] ?? $this->get_client_ip();
        $this->block_ip_address( $ip );
        $response['actions_taken'][] = 'ip_blocked';

        // Send high-priority alert.
        $this->send_high_priority_alert( $threat_data );
        $response['actions_taken'][] = 'high_priority_alert_sent';

        // Increase monitoring.
        $this->increase_monitoring_frequency();
        $response['actions_taken'][] = 'monitoring_increased';
    }

    /**
     * Handle medium threat (26-50).
     *
     * @param array $threat_data Threat data.
     * @param array $response    Response data (passed by reference).
     */
    private function handle_medium_threat( $threat_data, &$response ) {
        // Send standard alert.
        $this->send_standard_alert( $threat_data );
        $response['actions_taken'][] = 'alert_sent';

        // Log for review.
        $response['actions_taken'][] = 'logged';
    }

    /**
     * Handle low threat (0-25).
     *
     * @param array $threat_data Threat data.
     * @param array $response    Response data (passed by reference).
     */
    private function handle_low_threat( $threat_data, &$response ) {
        // Log only.
        $response['actions_taken'][] = 'logged';
    }

    /**
     * Block IP address.
     *
     * @param string $ip IP address to block.
     */
    private function block_ip_address( $ip ) {
        $blocked_ips = get_option( 'security_blocked_ips', array() );

        if ( ! in_array( $ip, $blocked_ips, true ) ) {
            $blocked_ips[] = $ip;
            update_option( 'security_blocked_ips', $blocked_ips );
        }
    }

    /**
     * Quarantine suspicious file.
     *
     * @param string $file_path Path to file.
     */
    private function quarantine_file( $file_path ) {
        $quarantine_dir = WP_CONTENT_DIR . '/security-quarantine';

        if ( ! is_dir( $quarantine_dir ) ) {
            \wp_mkdir_p( $quarantine_dir );
            file_put_contents( $quarantine_dir . '/.htaccess', 'Deny from all' );
        }

        $quarantine_path = $quarantine_dir . '/' . basename( $file_path ) . '.' . time();

        if ( file_exists( $file_path ) ) {
            rename( $file_path, $quarantine_path );
        }
    }

    /**
     * Enable WordPress maintenance mode.
     */
    private function enable_maintenance_mode() {
        file_put_contents(
            ABSPATH . '.maintenance',
            '<?php $upgrading = ' . time() . '; ?>'
        );
    }

    /**
     * Increase monitoring frequency.
     */
    private function increase_monitoring_frequency() {
        // Clear existing schedules.
        \wp_clear_scheduled_hook( 'detect_fs_anomalies' );

        // Reschedule to run every 15 minutes instead of hourly.
        \wp_schedule_event( time(), '15min', 'detect_fs_anomalies' );

        // Auto-restore normal frequency after 24 hours.
        \wp_schedule_single_event( time() + DAY_IN_SECONDS, 'restore_normal_monitoring' );
    }

    /**
     * Send critical security alert.
     *
     * @param array $threat_data Threat data.
     */
    private function send_critical_alert( $threat_data ) {
        $to = get_option( 'admin_email' );
        $subject = '[CRITICAL SECURITY] Potential 0-Day Exploitation - IMMEDIATE ACTION REQUIRED';

        $message = $this->format_alert_message( $threat_data, 'CRITICAL' );

        \wp_mail( $to, $subject, $message );

        // Optionally send SMS alert if configured.
        $sms_number = get_option( 'security_sms_alert_number' );
        if ( $sms_number ) {
            $this->send_sms_alert( $sms_number, 'CRITICAL security threat detected on ' . get_bloginfo( 'name' ) );
        }
    }

    /**
     * Send high-priority alert.
     *
     * @param array $threat_data Threat data.
     */
    private function send_high_priority_alert( $threat_data ) {
        $to = get_option( 'admin_email' );
        $subject = '[HIGH PRIORITY] Security Threat Detected';

        $message = $this->format_alert_message( $threat_data, 'HIGH' );

        \wp_mail( $to, $subject, $message );
    }

    /**
     * Send standard alert.
     *
     * @param array $threat_data Threat data.
     */
    private function send_standard_alert( $threat_data ) {
        $to = get_option( 'admin_email' );
        $subject = '[SECURITY] Suspicious Activity Detected';

        $message = $this->format_alert_message( $threat_data, 'MEDIUM' );

        \wp_mail( $to, $subject, $message );
    }

    /**
     * Format alert message.
     *
     * @param array  $threat_data Threat data.
     * @param string $severity    Severity level.
     * @return string Formatted message.
     */
    private function format_alert_message( $threat_data, $severity ) {
        $message = sprintf(
            "SECURITY ALERT - %s SEVERITY\n" .
            "========================================\n\n" .
            "A %s threat has been detected on your WordPress site.\n\n",
            $severity,
            strtolower( $severity )
        );

        $message .= "Site: " . get_bloginfo( 'name' ) . " (" . home_url() . ")\n";
        $message .= "Timestamp: " . \current_time( 'mysql' ) . "\n\n";

        if ( isset( $threat_data['ip_address'] ) ) {
            $message .= "IP Address: " . $threat_data['ip_address'] . "\n";
        }

        if ( isset( $threat_data['threat_score'] ) ) {
            $message .= "Threat Score: " . $threat_data['threat_score'] . "/100\n";
        }

        $message .= "\nThreat Details:\n";
        $message .= \wp_json_encode( $threat_data, JSON_PRETTY_PRINT );

        $message .= "\n\nAutomatic Actions Taken:\n";
        $message .= "- Request blocked\n";
        $message .= "- IP address blocked\n";
        $message .= "- Incident logged\n";

        if ( 'CRITICAL' === $severity ) {
            $message .= "\nIMMEDIATE ACTION REQUIRED:\n";
            $message .= "1. Review security logs\n";
            $message .= "2. Check quarantined files\n";
            $message .= "3. Verify no unauthorized access\n";
            $message .= "4. Update all plugins/themes\n";
        }

        return $message;
    }

    /**
     * Send SMS alert (placeholder - implement with SMS service).
     *
     * @param string $number  Phone number.
     * @param string $message Message text.
     */
    private function send_sms_alert( $number, $message ) {
        // Implement with Twilio, SNS, or other SMS service.
        // Example: Twilio API call.
    }

    /**
     * Get client IP address.
     *
     * @return string IP address.
     */
    private function get_client_ip() {
        $ip = '';

        if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
        } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
        } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
            $ip = sanitize_text_field( \wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
        }

        return $ip;
    }

    /**
     * Log incident response.
     *
     * @param array $response Response data.
     */
    private function log_incident_response( $response ) {
        global $wpdb;

        $table_name = $wpdb->prefix . 'security_incidents';

        $wpdb->insert(
            $table_name,
            array(
                'threat_id'     => $response['threat_id'],
                'timestamp'     => $response['timestamp'],
                'threat_level'  => $response['threat_level'],
                'threat_data'   => \wp_json_encode( $response['threat_data'] ),
                'actions_taken' => \wp_json_encode( $response['actions_taken'] ),
            ),
            array( '%s', '%s', '%d', '%s', '%s' )
        );
    }
}

/**
 * Restore normal monitoring frequency.
 *
 * @since 1.0.0
 */
function restore_normal_monitoring_frequency() {
    \wp_clear_scheduled_hook( 'detect_fs_anomalies' );
    \wp_schedule_event( time(), 'hourly', 'detect_fs_anomalies' );
}
\add_action( 'restore_normal_monitoring', 'restore_normal_monitoring_frequency' );
🚨 AUTOMATED LOCKDOWN CONSIDERATIONS
Automatic maintenance mode can prevent ongoing attacks but also makes your site inaccessible to legitimate users. Enable this feature only if:

  • You have 24/7 monitoring capability
  • False positives have been thoroughly tested
  • You have a documented recovery procedure
  • Business impact of downtime is acceptable

08 0-Day Detection Reporting

Comprehensive reporting transforms raw detection data into actionable intelligence. Your security team needs clear visualizations and prioritized action items.

Security Dashboard Metrics

Real-Time Monitoring Metrics

Active Threats: Number of ongoing suspicious activities being monitored
Blocked Requests: Count of automatically blocked exploitation attempts (last 24 hours)
File System Changes: New/modified files since last baseline
Suspicious Code Files: Files with high heuristic risk scores
API Anomalies: Unusual REST API or AJAX activity patterns
Quarantined Files: Files automatically isolated due to critical threats

Weekly Security Report Template

PHP
<?php
/**
 * Generate weekly security report.
 *
 * @return string HTML report.
 * @since 1.0.0
 */
function generate_weekly_security_report() {
    global $wpdb;

    $report_data = array(
        'period_start' => date( 'Y-m-d', strtotime( '-7 days' ) ),
        'period_end'   => date( 'Y-m-d' ),
        'metrics'      => array(),
    );

    // Count blocked requests.
    $table_requests = $wpdb->prefix . 'security_requests';
    $blocked_count = $wpdb->get_var(
        $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table_requests} 
            WHERE timestamp >= %s AND action_taken = 'blocked'",
            $report_data['period_start']
        )
    );
    $report_data['metrics']['blocked_requests'] = $blocked_count;

    // Count incidents.
    $table_incidents = $wpdb->prefix . 'security_incidents';
    $incident_count = $wpdb->get_var(
        $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table_incidents} 
            WHERE timestamp >= %s",
            $report_data['period_start']
        )
    );
    $report_data['metrics']['total_incidents'] = $incident_count;

    // Critical incidents.
    $critical_count = $wpdb->get_var(
        $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table_incidents} 
            WHERE timestamp >= %s AND threat_level >= 76",
            $report_data['period_start']
        )
    );
    $report_data['metrics']['critical_incidents'] = $critical_count;

    // Generate HTML report.
    ob_start();
    ?>
    <html>
    <head>
        <style>
            body { font-family: Arial, sans-serif; padding: 20px; }
            h1 { color: #333; }
            .metric { background: #f5f5f5; padding: 15px; margin: 10px 0; border-radius: 5px; }
            .metric-value { font-size: 2em; font-weight: bold; color: #e53e3e; }
            .status-good { color: #38a169; }
            .status-warning { color: #ed8936; }
            .status-critical { color: #e53e3e; }
        </style>
    </head>
    <body>
        <h1>WordPress Security Report</h1>
        Period: <?php echo esc_html( $report_data['period_start'] ); ?> to <?php echo esc_html( $report_data['period_end'] ); ?>


        <div class="metric">
            <h3>Blocked Exploitation Attempts</h3>
            <div class="metric-value"><?php echo esc_html( $report_data['metrics']['blocked_requests'] ); ?></div>
        </div>

        <div class="metric">
            <h3>Total Security Incidents</h3>
            <div class="metric-value"><?php echo esc_html( $report_data['metrics']['total_incidents'] ); ?></div>
        </div>

        <div class="metric">
            <h3>Critical Incidents (Threat Level 76+)</h3>
            <div class="metric-value status-critical"><?php echo esc_html( $report_data['metrics']['critical_incidents'] ); ?></div>
        </div>

        <?php if ( 0 === $report_data['metrics']['critical_incidents'] ) : ?>
            <strong>✓ No critical security incidents detected this week.</strong>

        <?php else : ?>
            <strong>⚠ Critical incidents require immediate review!</strong>

        <?php endif; ?>

        <h2>Recommendations</h2>
        <ul>
            <li>Review all blocked requests for attack patterns</li>
            <li>Verify quarantined files are malicious</li>
            <li>Update baseline snapshots if legitimate changes were made</li>
            <li>Run full heuristic scan on plugins directory</li>
        </ul>
    </body>
    </html>
    <?php
    return ob_get_clean();
}

09 Incident Response Plan

When a potential 0-day is detected, every minute counts. Having a documented incident response plan ensures your team acts quickly and effectively.

0-Day Incident Response Checklist

Phase 1: Immediate Response (0-15 minutes)

Verify Alert: Confirm this is not a false positive by reviewing detection data
Assess Scope: Determine which systems/files are affected
Isolate Threat: If confirmed, enable maintenance mode to prevent further exploitation
Block Attack Vectors: Block attacking IP addresses, disable vulnerable endpoints

Phase 2: Investigation (15-60 minutes)

Collect Evidence: Preserve logs, quarantined files, and detection data
Identify Entry Point: Determine how the attack originated
Check for Backdoors: Run full heuristic scan to find any planted backdoors
Assess Damage: Check database for unauthorized changes, review file modifications

Phase 3: Containment & Remediation (1-4 hours)

Remove Malicious Files: Delete or quarantine all identified malicious code
Restore Clean Files: Replace compromised core/plugin files from trusted sources
Clean Database: Remove injected content, rogue admin accounts, malicious options
Change All Credentials: Reset passwords for all users, database, FTP, hosting panel
Apply Security Patches: Update WordPress core, plugins, themes to latest versions

Phase 4: Recovery & Monitoring (4-24 hours)

Verify Clean State: Run full security scan to confirm no malware remains
Create New Baseline: Update file system baseline with clean state
Restore Service: Disable maintenance mode and monitor closely
Enhanced Monitoring: Increase scan frequency for 48 hours post-incident
Document Incident: Record timeline, actions taken, lessons learned

Phase 5: Post-Incident Analysis (1-7 days)

Root Cause Analysis: Determine why the exploit succeeded
Harden Security: Implement additional controls to prevent recurrence
Update Response Plan: Incorporate lessons learned into incident response procedures
Share Intelligence: Report 0-day details to WordPress security team, plugin authors
📋 CRITICAL: PRACTICE YOUR RESPONSE PLAN
Run tabletop exercises quarterly to ensure your team can execute the incident response plan under pressure. Simulated incidents reveal gaps in procedures, communication breakdowns, and missing tools before a real 0-day strikes.

Conclusion: Proactive Defense Against 0-Days

Traditional security scanners cannot protect you from 0-day vulnerabilities. By the time a vulnerability appears in CVE databases, attackers have already exploited it for weeks. This guide provides a multi-layered detection framework that identifies suspicious behavior before the threat becomes known.

✅ IMPLEMENTATION PRIORITIES
Start with these three detection layers first:

  1. Request Pattern Monitoring – Catches exploitation attempts in real-time
  2. File System Anomaly Detection – Detects backdoor deployment within hours
  3. Automated Response System – Blocks threats immediately without human intervention

Then add API monitoring, heuristic scanning, and advanced reporting as resources allow.

Key Takeaways

  • 0-day exploits are used within 24 hours of discovery—signature-based scanners are useless
  • Behavioral analysis, anomaly detection, and heuristic scanning catch unknown threats
  • Automated response systems can block attacks before human administrators are even notified
  • File system baselines must be refreshed weekly to account for legitimate updates
  • REST API and AJAX endpoints are common 0-day targets—monitor them closely
  • A documented incident response plan reduces containment time from hours to minutes
  • Practice your response procedures through tabletop exercises and simulations
🔐 REMEMBER: DEFENSE IN DEPTH
No single detection method catches all 0-days. Layered security ensures that if one detection layer fails, others will catch the threat. Combine request monitoring, file scanning, API tracking, and heuristic analysis for comprehensive coverage.

Stay vigilant. Monitor continuously. Respond immediately. These are your best defenses against the unknown threats that signature-based scanners will never detect.

← WordPress Website Scanner: Complete Security Audit Guide
Share this page
Back to top