How to Scan Your WordPress Site for 0-Day Vulnerabilities (Before They Go Public)
Table of Contents
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
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
/**
* 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 );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
/**
* 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 );
}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
/**
* 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' );
}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
/**
* 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' );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
/**
* 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' );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
Weekly Security Report Template
<?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)
Phase 2: Investigation (15-60 minutes)
Phase 3: Containment & Remediation (1-4 hours)
Phase 4: Recovery & Monitoring (4-24 hours)
Phase 5: Post-Incident Analysis (1-7 days)
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.
Start with these three detection layers first:
- Request Pattern Monitoring – Catches exploitation attempts in real-time
- File System Anomaly Detection – Detects backdoor deployment within hours
- 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
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.