#!/usr/bin/env python3 """ SIEM مبسط - نظام جمع وتحليل السجلات يجمع ويحلل logs من 3 مصادر ويكتشف الأنماط المشبوهة المصادر: 1. Nginx Access Logs 2. Auth/SSH Logs 3. Application Logs """ import re import json import os from datetime import datetime, timedelta from collections import defaultdict from typing import Dict, List, Tuple import hashlib # ═══════════════════════════════════════════════════════════════ # إعدادات المصادر # ═══════════════════════════════════════════════════════════════ LOG_SOURCES = { 'nginx': '/var/log/nginx/access.log', 'auth': '/var/log/auth.log', 'app': '/var/log/app/application.log' } # للتجربة - ملفات محلية SAMPLE_LOG_SOURCES = { 'nginx': 'sample_logs/nginx_access.log', 'auth': 'sample_logs/auth.log', 'app': 'sample_logs/app.log' } # ═══════════════════════════════════════════════════════════════ # أنماط الكشف عن التهديدات # ═══════════════════════════════════════════════════════════════ THREAT_PATTERNS = { 'brute_force': { 'description': 'محاولات تسجيل دخول فاشلة متكررة', 'threshold': 5, 'window_minutes': 5, 'severity': 'critical' }, 'sql_injection': { 'description': 'محاولة SQL Injection', 'patterns': [ r"(\%27)|(\')|(\-\-)|(\%23)|(#)", r"((\%3D)|(=))[^\n]*((\%27)|(\')|(\-\-)|(\%3B)|(;))", r"\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))", r"union.*select", r"select.*from", r"insert.*into", r"drop.*table" ], 'severity': 'critical' }, 'xss_attempt': { 'description': 'محاولة XSS', 'patterns': [ r"]*>", r"javascript:", r"on\w+\s*=", r" Dict: """تحليل سطر من Nginx access log""" pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[([^\]]+)\] "(\w+) ([^"]+)" (\d+) (\d+) "([^"]*)" "([^"]*)"' match = re.match(pattern, line) if match: return { 'ip': match.group(1), 'timestamp': match.group(2), 'method': match.group(3), 'path': match.group(4), 'status': int(match.group(5)), 'size': int(match.group(6)), 'referer': match.group(7), 'user_agent': match.group(8), 'source': 'nginx' } return None def parse_auth_log(self, line: str) -> Dict: """تحليل سطر من Auth log""" # Failed password failed_pattern = r'(\w+\s+\d+\s+[\d:]+).*Failed password for (?:invalid user )?(\w+) from (\d+\.\d+\.\d+\.\d+)' match = re.search(failed_pattern, line) if match: return { 'timestamp': match.group(1), 'username': match.group(2), 'ip': match.group(3), 'event': 'failed_login', 'source': 'auth' } # Accepted password success_pattern = r'(\w+\s+\d+\s+[\d:]+).*Accepted password for (\w+) from (\d+\.\d+\.\d+\.\d+)' match = re.search(success_pattern, line) if match: return { 'timestamp': match.group(1), 'username': match.group(2), 'ip': match.group(3), 'event': 'successful_login', 'source': 'auth' } return None def parse_app_log(self, line: str) -> Dict: """تحليل سطر من Application log""" pattern = r'\[(\d{4}-\d{2}-\d{2} [\d:]+)\] \[(\w+)\] \[(\d+\.\d+\.\d+\.\d+)\] (.+)' match = re.match(pattern, line) if match: return { 'timestamp': match.group(1), 'level': match.group(2), 'ip': match.group(3), 'message': match.group(4), 'source': 'app' } return None def detect_pattern_threat(self, log_entry: Dict, threat_type: str) -> bool: """كشف التهديدات بناءً على الأنماط""" threat = THREAT_PATTERNS.get(threat_type) if not threat or 'patterns' not in threat: return False # البحث في الحقول المناسبة search_fields = ['path', 'message', 'user_agent', 'referer'] for field in search_fields: if field in log_entry: for pattern in threat['patterns']: if re.search(pattern, str(log_entry[field]), re.IGNORECASE): return True return False def analyze_log_entry(self, entry: Dict): """تحليل سجل واحد للكشف عن التهديدات""" if not entry: return ip = entry.get('ip', 'unknown') # تحديث معلومات IP self.suspicious_ips[ip]['count'] += 1 if not self.suspicious_ips[ip]['first_seen']: self.suspicious_ips[ip]['first_seen'] = entry.get('timestamp') self.suspicious_ips[ip]['last_seen'] = entry.get('timestamp') # كشف SQL Injection if self.detect_pattern_threat(entry, 'sql_injection'): self.add_alert('sql_injection', entry) # كشف XSS if self.detect_pattern_threat(entry, 'xss_attempt'): self.add_alert('xss_attempt', entry) # كشف Path Traversal if self.detect_pattern_threat(entry, 'path_traversal'): self.add_alert('path_traversal', entry) # كشف User-Agent مشبوه if self.detect_pattern_threat(entry, 'suspicious_user_agent'): self.add_alert('suspicious_user_agent', entry) # كشف محاولات تسجيل دخول فاشلة if entry.get('event') == 'failed_login': self.suspicious_ips[ip]['threats'].append('failed_login') if len([t for t in self.suspicious_ips[ip]['threats'] if t == 'failed_login']) >= 5: self.add_alert('brute_force', entry) # كشف أكواد HTTP مشبوهة status = entry.get('status') if status: if status == 401 or status == 403: self.suspicious_ips[ip]['threats'].append('auth_failure') elif status >= 500: self.suspicious_ips[ip]['threats'].append('server_error') def add_alert(self, threat_type: str, entry: Dict): """إضافة تنبيه جديد""" threat_info = THREAT_PATTERNS.get(threat_type, {}) alert = { 'id': hashlib.md5(f"{threat_type}{entry.get('ip')}{datetime.now()}".encode()).hexdigest()[:8], 'timestamp': datetime.now().isoformat(), 'threat_type': threat_type, 'description': threat_info.get('description', threat_type), 'severity': threat_info.get('severity', 'medium'), 'source_ip': entry.get('ip', 'unknown'), 'details': entry, 'status': 'new' } self.alerts.append(alert) self.stats['threats_detected'] += 1 def analyze_file(self, source_name: str, filepath: str): """تحليل ملف سجلات""" if not os.path.exists(filepath): print(f"⚠️ الملف غير موجود: {filepath}") return parser = { 'nginx': self.parse_nginx_log, 'auth': self.parse_auth_log, 'app': self.parse_app_log }.get(source_name) if not parser: print(f"⚠️ لا يوجد محلل لـ: {source_name}") return print(f"📂 تحليل {source_name}: {filepath}") with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: for line in f: self.stats['total_lines'] += 1 entry = parser(line.strip()) if entry: self.analyze_log_entry(entry) self.stats['sources_analyzed'] += 1 def analyze_all(self): """تحليل جميع المصادر""" print("=" * 60) print("🔍 بدء تحليل السجلات - SIEM مبسط") print("=" * 60) for source_name, filepath in self.sources.items(): self.analyze_file(source_name, filepath) print("\n" + "=" * 60) print("📊 نتائج التحليل") print("=" * 60) def get_suspicious_ips(self, min_threats: int = 3) -> List[Dict]: """الحصول على قائمة IPs المشبوهة""" suspicious = [] for ip, data in self.suspicious_ips.items(): threat_count = len(data['threats']) if threat_count >= min_threats: suspicious.append({ 'ip': ip, 'request_count': data['count'], 'threat_count': threat_count, 'threats': list(set(data['threats'])), 'first_seen': data['first_seen'], 'last_seen': data['last_seen'] }) return sorted(suspicious, key=lambda x: x['threat_count'], reverse=True) def get_alerts_by_severity(self) -> Dict[str, List]: """تجميع التنبيهات حسب الخطورة""" by_severity = defaultdict(list) for alert in self.alerts: by_severity[alert['severity']].append(alert) return dict(by_severity) def generate_report(self) -> Dict: """إنشاء تقرير شامل""" return { 'generated_at': datetime.now().isoformat(), 'statistics': self.stats, 'alerts': self.alerts, 'alerts_by_severity': self.get_alerts_by_severity(), 'suspicious_ips': self.get_suspicious_ips(), 'summary': { 'total_alerts': len(self.alerts), 'critical': len([a for a in self.alerts if a['severity'] == 'critical']), 'high': len([a for a in self.alerts if a['severity'] == 'high']), 'medium': len([a for a in self.alerts if a['severity'] == 'medium']), 'suspicious_ips_count': len(self.get_suspicious_ips()) } } def print_summary(self): """طباعة ملخص النتائج""" report = self.generate_report() print(f"\n📈 الإحصائيات:") print(f" - إجمالي الأسطر المحللة: {report['statistics']['total_lines']}") print(f" - المصادر المحللة: {report['statistics']['sources_analyzed']}") print(f" - التهديدات المكتشفة: {report['statistics']['threats_detected']}") print(f"\n🚨 التنبيهات حسب الخطورة:") print(f" - حرجة (Critical): {report['summary']['critical']}") print(f" - عالية (High): {report['summary']['high']}") print(f" - متوسطة (Medium): {report['summary']['medium']}") print(f"\n🔴 عناوين IP المشبوهة ({report['summary']['suspicious_ips_count']}):") for ip_data in report['suspicious_ips'][:10]: print(f" - {ip_data['ip']}: {ip_data['threat_count']} تهديدات, {ip_data['request_count']} طلبات") if self.alerts: print(f"\n⚠️ آخر 5 تنبيهات:") for alert in self.alerts[-5:]: print(f" [{alert['severity'].upper()}] {alert['description']} من {alert['source_ip']}") def export_json(self, filepath: str): """تصدير التقرير كـ JSON""" report = self.generate_report() with open(filepath, 'w', encoding='utf-8') as f: json.dump(report, f, ensure_ascii=False, indent=2) print(f"\n💾 تم حفظ التقرير: {filepath}") # ═══════════════════════════════════════════════════════════════ # إنشاء ملفات سجلات تجريبية # ═══════════════════════════════════════════════════════════════ def create_sample_logs(): """إنشاء ملفات سجلات تجريبية للاختبار""" os.makedirs('sample_logs', exist_ok=True) # Nginx sample logs nginx_logs = """192.168.1.100 - - [26/Jul/2026:14:05:01 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0" 192.168.1.100 - - [26/Jul/2026:14:05:02 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0" 192.168.1.100 - - [26/Jul/2026:14:05:03 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0" 192.168.1.100 - - [26/Jul/2026:14:05:04 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0" 192.168.1.100 - - [26/Jul/2026:14:05:05 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0" 192.168.1.100 - - [26/Jul/2026:14:05:06 +0000] "POST /api/login HTTP/1.1" 200 512 "-" "Mozilla/5.0" 10.0.0.50 - - [26/Jul/2026:14:10:01 +0000] "GET /api/users?id=1' OR '1'='1 HTTP/1.1" 400 120 "-" "sqlmap/1.5" 10.0.0.50 - - [26/Jul/2026:14:10:02 +0000] "GET /api/users?id=1 UNION SELECT * FROM users HTTP/1.1" 400 120 "-" "sqlmap/1.5" 172.16.0.25 - - [26/Jul/2026:14:15:01 +0000] "GET /page?q= HTTP/1.1" 400 80 "-" "Mozilla/5.0" 172.16.0.25 - - [26/Jul/2026:14:15:02 +0000] "GET /../../etc/passwd HTTP/1.1" 403 50 "-" "Mozilla/5.0" 8.8.8.8 - - [26/Jul/2026:14:20:01 +0000] "GET / HTTP/1.1" 200 1024 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" 8.8.8.8 - - [26/Jul/2026:14:20:02 +0000] "GET /about HTTP/1.1" 200 2048 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" """ # Auth sample logs auth_logs = """Jul 26 14:00:01 server sshd[1234]: Failed password for invalid user admin from 192.168.1.200 port 22 ssh2 Jul 26 14:00:02 server sshd[1234]: Failed password for invalid user admin from 192.168.1.200 port 22 ssh2 Jul 26 14:00:03 server sshd[1234]: Failed password for invalid user root from 192.168.1.200 port 22 ssh2 Jul 26 14:00:04 server sshd[1234]: Failed password for invalid user test from 192.168.1.200 port 22 ssh2 Jul 26 14:00:05 server sshd[1234]: Failed password for invalid user user from 192.168.1.200 port 22 ssh2 Jul 26 14:05:01 server sshd[1235]: Accepted password for developer from 10.0.0.10 port 22 ssh2 """ # App sample logs app_logs = """[2026-07-26 14:00:01] [ERROR] [192.168.1.100] Login failed for user: admin [2026-07-26 14:00:02] [ERROR] [192.168.1.100] Login failed for user: admin [2026-07-26 14:00:03] [WARN] [10.0.0.50] Suspicious query detected: SELECT * FROM users [2026-07-26 14:00:04] [INFO] [8.8.8.8] User logged in successfully [2026-07-26 14:00:05] [ERROR] [172.16.0.25] Invalid input: """ with open('sample_logs/nginx_access.log', 'w') as f: f.write(nginx_logs) with open('sample_logs/auth.log', 'w') as f: f.write(auth_logs) with open('sample_logs/app.log', 'w') as f: f.write(app_logs) print("✅ تم إنشاء ملفات السجلات التجريبية في sample_logs/") # ═══════════════════════════════════════════════════════════════ # التشغيل الرئيسي # ═══════════════════════════════════════════════════════════════ if __name__ == '__main__': import sys # إنشاء ملفات تجريبية إذا لم تكن موجودة if not os.path.exists('sample_logs'): create_sample_logs() # تحليل السجلات analyzer = LogAnalyzer(use_sample=True) analyzer.analyze_all() analyzer.print_summary() # تصدير التقرير analyzer.export_json('siem_report.json') print("\n" + "=" * 60) print("✅ اكتمل التحليل - راجع siem_report.json للتفاصيل") print("=" * 60)