- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
474 lines
18 KiB
PHP
474 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Waf\Rules;
|
|
|
|
use App\Framework\Waf\DetectionCategory;
|
|
use App\Framework\Waf\DetectionSeverity;
|
|
use App\Framework\Waf\Rules\ValueObjects\RuleCondition;
|
|
use App\Framework\Waf\Rules\ValueObjects\RulePattern;
|
|
use App\Framework\Waf\ValueObjects\RuleId;
|
|
|
|
/**
|
|
* OWASP ModSecurity Core Rule Set (CRS) implementation
|
|
* Based on OWASP CRS v3.3+ patterns and rules
|
|
*/
|
|
final class OWASPCoreRuleSet
|
|
{
|
|
/**
|
|
* Get all OWASP CRS rules
|
|
*/
|
|
public static function getAllRules(): array
|
|
{
|
|
return array_merge(
|
|
self::getSqlInjectionRules(),
|
|
self::getXssRules(),
|
|
self::getPathTraversalRules(),
|
|
self::getCommandInjectionRules(),
|
|
self::getFileUploadRules(),
|
|
self::getUserAgentRules(),
|
|
self::getHttpProtocolRules(),
|
|
self::getApplicationAttackRules(),
|
|
self::getGenericAttackRules()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* SQL Injection Detection Rules (OWASP Top 10 #3)
|
|
*/
|
|
public static function getSqlInjectionRules(): array
|
|
{
|
|
return [
|
|
// Basic SQL Injection patterns
|
|
new Rule(
|
|
id: RuleId::sql('920100'),
|
|
name: 'SQL Injection - Union Attack',
|
|
description: 'Detects SQL UNION attack patterns',
|
|
category: DetectionCategory::SQL_INJECTION,
|
|
severity: DetectionSeverity::CRITICAL,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:union[\s\/\*]+(?:all[\s\/\*]+)?select)', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 95,
|
|
tags: ['sql', 'injection', 'union', 'owasp-top10']
|
|
),
|
|
|
|
// SQL meta-characters
|
|
new Rule(
|
|
id: RuleId::sql('920110'),
|
|
name: 'SQL Injection - Meta Characters',
|
|
description: 'Detects SQL meta-characters in parameters',
|
|
category: DetectionCategory::SQL_INJECTION,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:[\'\"`][\s]*(?:or|and)[\s]*[\'\"`]*[\s]*(?:[\'\"`]*[\w]+[\'\"`]*[\s]*=[\s]*[\'\"`]*[\w]+|[\d]+[\s]*=[\s]*[\d]+))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 90,
|
|
tags: ['sql', 'injection', 'meta-characters', 'owasp-top10']
|
|
),
|
|
|
|
// SQL comment attacks
|
|
new Rule(
|
|
id: RuleId::sql('920120'),
|
|
name: 'SQL Injection - Comment Attack',
|
|
description: 'Detects SQL comment-based injection',
|
|
category: DetectionCategory::SQL_INJECTION,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:--|#|\/\*|\*\/))', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 80,
|
|
tags: ['sql', 'injection', 'comments']
|
|
),
|
|
|
|
// SQL functions
|
|
new Rule(
|
|
id: RuleId::sql('920130'),
|
|
name: 'SQL Injection - Function Detection',
|
|
description: 'Detects SQL functions in input',
|
|
category: DetectionCategory::SQL_INJECTION,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:concat|substring|ascii|char|count|group_concat|version|database|user|current_user|system_user|schema|table_name|column_name)[\s]*\()', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 85,
|
|
tags: ['sql', 'injection', 'functions', 'owasp-top10']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Cross-Site Scripting (XSS) Rules (OWASP Top 10 #7)
|
|
*/
|
|
public static function getXssRules(): array
|
|
{
|
|
return [
|
|
// Script tag injection
|
|
new Rule(
|
|
id: RuleId::xss('941100'),
|
|
name: 'XSS - Script Tag Attack',
|
|
description: 'Detects script tag injection attempts',
|
|
category: DetectionCategory::XSS,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:<script[^>]*>.*?<\/script>)', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 90,
|
|
tags: ['xss', 'script', 'owasp-top10']
|
|
),
|
|
|
|
// Event handler injection
|
|
new Rule(
|
|
id: RuleId::xss('941110'),
|
|
name: 'XSS - Event Handler Attack',
|
|
description: 'Detects JavaScript event handler injection',
|
|
category: DetectionCategory::XSS,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:on(?:load|error|click|focus|blur|change|submit|reset|select|keydown|keyup|keypress|mouseover|mouseout|mousedown|mouseup|mousemove)[\s]*=)', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 85,
|
|
tags: ['xss', 'events', 'owasp-top10']
|
|
),
|
|
|
|
// JavaScript pseudo protocol
|
|
new Rule(
|
|
id: RuleId::xss('941120'),
|
|
name: 'XSS - JavaScript Pseudo Protocol',
|
|
description: 'Detects javascript: pseudo protocol usage',
|
|
category: DetectionCategory::XSS,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:javascript[\s]*:)', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 80,
|
|
tags: ['xss', 'javascript', 'protocol']
|
|
),
|
|
|
|
// HTML injection
|
|
new Rule(
|
|
id: RuleId::xss('941130'),
|
|
name: 'XSS - HTML Tag Injection',
|
|
description: 'Detects potentially dangerous HTML tags',
|
|
category: DetectionCategory::XSS,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:<(?:iframe|object|embed|applet|meta|link|style|img|svg|math|details|template)[^>]*>)', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 70,
|
|
tags: ['xss', 'html', 'tags']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Path Traversal Rules
|
|
*/
|
|
public static function getPathTraversalRules(): array
|
|
{
|
|
return [
|
|
// Directory traversal
|
|
new Rule(
|
|
id: RuleId::pathTraversal('930100'),
|
|
name: 'Path Traversal - Directory Traversal',
|
|
description: 'Detects directory traversal attempts',
|
|
category: DetectionCategory::PATH_TRAVERSAL,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::urlPath(
|
|
RulePattern::regex('(?i:(?:\.\.[\\/])|(?:[\\/]\.\.)|(?:\.\.\\\\)|(?:\\\\\.\.)|(?:%2e%2e%2f)|(?:%2e%2e\\\\)|(?:\.\.%2f)|(?:\.\.%5c)|(?:%2e%2e%5c)|(?:%c0%ae%c0%ae%c0%af)|(?:%c1%9c%c1%9c%c1%af))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 85,
|
|
tags: ['path-traversal', 'directory', 'file-access']
|
|
),
|
|
|
|
// Absolute path access
|
|
new Rule(
|
|
id: RuleId::pathTraversal('930110'),
|
|
name: 'Path Traversal - Absolute Path Access',
|
|
description: 'Detects absolute path access attempts',
|
|
category: DetectionCategory::PATH_TRAVERSAL,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:\/etc\/passwd|\/etc\/shadow|\/etc\/hosts|\/proc\/|\/sys\/|c:[\\\\\\/]|\\\\\\\\))', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 75,
|
|
tags: ['path-traversal', 'absolute-path', 'system-files']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Command Injection Rules
|
|
*/
|
|
public static function getCommandInjectionRules(): array
|
|
{
|
|
return [
|
|
// OS Command injection
|
|
new Rule(
|
|
id: RuleId::commandInjection('932100'),
|
|
name: 'Command Injection - OS Commands',
|
|
description: 'Detects OS command injection attempts',
|
|
category: DetectionCategory::COMMAND_INJECTION,
|
|
severity: DetectionSeverity::CRITICAL,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:;|\||\|\||&&|&|`|\$\(|\${)[\s]*(?:cat|ls|pwd|id|uname|whoami|ps|kill|rm|mv|cp|chmod|chown|find|grep|awk|sed|sort|head|tail|wc|netstat|ifconfig|ping|wget|curl|nc|telnet|ssh|su|sudo|passwd|shadow|etc\/passwd|etc\/shadow|proc\/))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 95,
|
|
tags: ['command-injection', 'os-injection', 'rce', 'owasp-top10']
|
|
),
|
|
|
|
// Shell metacharacters
|
|
new Rule(
|
|
id: RuleId::commandInjection('932110'),
|
|
name: 'Command Injection - Shell Metacharacters',
|
|
description: 'Detects shell metacharacters',
|
|
category: DetectionCategory::COMMAND_INJECTION,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:[;&|`$(){}[\]<>])', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 80,
|
|
tags: ['command-injection', 'shell', 'metacharacters']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* File Upload Rules
|
|
*/
|
|
public static function getFileUploadRules(): array
|
|
{
|
|
return [
|
|
// Malicious file extensions
|
|
new Rule(
|
|
id: RuleId::generic('933100'),
|
|
name: 'File Upload - Malicious Extensions',
|
|
description: 'Detects potentially malicious file extensions',
|
|
category: DetectionCategory::FILE_UPLOAD_ABUSE,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::header(
|
|
'Content-Type',
|
|
RulePattern::regex('(?i:application\/(?:x-)?(?:php|jsp|asp|exe|bat|cmd|sh|python|perl|ruby|javascript)|text\/(?:x-)?(?:php|jsp|asp|python|perl|ruby))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 85,
|
|
tags: ['file-upload', 'malware', 'webshell']
|
|
),
|
|
|
|
// Double extensions
|
|
new Rule(
|
|
id: RuleId::generic('933110'),
|
|
name: 'File Upload - Double Extensions',
|
|
description: 'Detects double file extensions',
|
|
category: DetectionCategory::FILE_UPLOAD_ABUSE,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:\.(?:jpg|jpeg|png|gif|bmp|doc|docx|pdf|txt)\.(?:php|jsp|asp|exe|bat|cmd|sh|py|pl|rb|js))', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 70,
|
|
tags: ['file-upload', 'double-extension', 'bypass']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* User Agent Rules
|
|
*/
|
|
public static function getUserAgentRules(): array
|
|
{
|
|
return [
|
|
// Security scanners
|
|
new Rule(
|
|
id: RuleId::generic('913100'),
|
|
name: 'User-Agent - Security Scanner Detection',
|
|
description: 'Detects known security scanning tools',
|
|
category: DetectionCategory::BOT_DETECTION,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::userAgent(
|
|
RulePattern::regex('(?i:(?:sqlmap|nmap|nikto|w3af|acunetix|nessus|openvas|burp|havij|hydra|metasploit|python-requests|curl|wget))', 'i')
|
|
),
|
|
action: RuleAction::CHALLENGE,
|
|
priority: 70,
|
|
tags: ['user-agent', 'scanner', 'tool-detection']
|
|
),
|
|
|
|
// Empty or missing User-Agent
|
|
new Rule(
|
|
id: RuleId::generic('913110'),
|
|
name: 'User-Agent - Missing or Empty',
|
|
description: 'Detects missing or empty user agent strings',
|
|
category: DetectionCategory::BOT_DETECTION,
|
|
severity: DetectionSeverity::LOW,
|
|
condition: RuleCondition::userAgent(
|
|
RulePattern::regex('^$', '')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 40,
|
|
tags: ['user-agent', 'empty', 'suspicious']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* HTTP Protocol Rules
|
|
*/
|
|
public static function getHttpProtocolRules(): array
|
|
{
|
|
return [
|
|
// Invalid HTTP methods
|
|
new Rule(
|
|
id: RuleId::generic('911100'),
|
|
name: 'HTTP Protocol - Invalid Method',
|
|
description: 'Detects invalid HTTP methods',
|
|
category: DetectionCategory::PROTOCOL_ATTACK,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::httpMethod(
|
|
RulePattern::regex('^(?!GET|POST|HEAD|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT).*', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 60,
|
|
tags: ['http', 'protocol', 'method', 'invalid']
|
|
),
|
|
|
|
// Oversized request headers
|
|
new Rule(
|
|
id: RuleId::generic('911110'),
|
|
name: 'HTTP Protocol - Oversized Headers',
|
|
description: 'Detects oversized HTTP headers',
|
|
category: DetectionCategory::PROTOCOL_ATTACK,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::header(
|
|
'*',
|
|
RulePattern::regex('.{8192,}', '') // Headers over 8KB
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 65,
|
|
tags: ['http', 'protocol', 'headers', 'size']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Application Attack Rules
|
|
*/
|
|
public static function getApplicationAttackRules(): array
|
|
{
|
|
return [
|
|
// LDAP injection
|
|
new Rule(
|
|
id: RuleId::generic('950100'),
|
|
name: 'Application Attack - LDAP Injection',
|
|
description: 'Detects LDAP injection attempts',
|
|
category: DetectionCategory::INJECTION_ATTACK,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:\()(?:&|\|)(?:\(|\)))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 80,
|
|
tags: ['ldap', 'injection', 'application']
|
|
),
|
|
|
|
// XPath injection
|
|
new Rule(
|
|
id: RuleId::generic('950110'),
|
|
name: 'Application Attack - XPath Injection',
|
|
description: 'Detects XPath injection attempts',
|
|
category: DetectionCategory::INJECTION_ATTACK,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:(?:\/\/|\.\.|\[@|position\(\)|text\(\)|node\(\)|ancestor|descendant|following|preceding|self))', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 75,
|
|
tags: ['xpath', 'injection', 'xml']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Generic Attack Rules
|
|
*/
|
|
public static function getGenericAttackRules(): array
|
|
{
|
|
return [
|
|
// Null byte injection
|
|
new Rule(
|
|
id: RuleId::generic('960100'),
|
|
name: 'Generic Attack - Null Byte Injection',
|
|
description: 'Detects null byte injection attempts',
|
|
category: DetectionCategory::PROTOCOL_ATTACK,
|
|
severity: DetectionSeverity::HIGH,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:%00|\\x00|\0)', 'i')
|
|
),
|
|
action: RuleAction::BLOCK,
|
|
priority: 80,
|
|
tags: ['null-byte', 'injection', 'bypass']
|
|
),
|
|
|
|
// Unicode evasion
|
|
new Rule(
|
|
id: RuleId::generic('960110'),
|
|
name: 'Generic Attack - Unicode Evasion',
|
|
description: 'Detects Unicode-based evasion attempts',
|
|
category: DetectionCategory::EVASION,
|
|
severity: DetectionSeverity::MEDIUM,
|
|
condition: RuleCondition::requestBody(
|
|
RulePattern::regex('(?i:%u[0-9a-f]{4}|\\\\u[0-9a-f]{4})', 'i')
|
|
),
|
|
action: RuleAction::SCORE,
|
|
priority: 60,
|
|
tags: ['unicode', 'evasion', 'encoding']
|
|
),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get rules by OWASP Top 10 category
|
|
*/
|
|
public static function getOwaspTop10Rules(): array
|
|
{
|
|
return array_filter(
|
|
self::getAllRules(),
|
|
fn (Rule $rule) => $rule->hasTag('owasp-top10')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get critical severity rules only
|
|
*/
|
|
public static function getCriticalRules(): array
|
|
{
|
|
return array_filter(
|
|
self::getAllRules(),
|
|
fn (Rule $rule) => $rule->severity === DetectionSeverity::CRITICAL
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get high-priority rules (priority >= 80)
|
|
*/
|
|
public static function getHighPriorityRules(): array
|
|
{
|
|
return array_filter(
|
|
self::getAllRules(),
|
|
fn (Rule $rule) => $rule->priority >= 80
|
|
);
|
|
}
|
|
}
|