Update q4-siem/siem_analyzer.py
هذا الالتزام موجود في:
@@ -1,24 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Ghaymah Lightweight SIEM Engine
|
||||||
|
Author: AbdulRhman Ewais
|
||||||
|
Description: Consolidated Log Analyzer for Auth, Nginx, and API services.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
LOG_FILE = "ghaymah_access.log"
|
# مسارات السجلات الافتراضية المربوطة بالـ Volume
|
||||||
FAILED_THRESHOLD = 5
|
LOG_SOURCES = {
|
||||||
|
"auth": "/var/log/siem_logs/auth.log",
|
||||||
|
"web": "/var/log/siem_logs/nginx_access.log",
|
||||||
|
"api": "/var/log/siem_logs/ghaymah_api.log"
|
||||||
|
}
|
||||||
|
|
||||||
ip_failures = defaultdict(int)
|
# أنماط الكشف (Detection Signatures)
|
||||||
|
BRUTE_FORCE_THRESHOLD = 5
|
||||||
|
XSS_PATTERN = re.compile(r'(<script>|javascript:|onerror=|onload=)', re.IGNORECASE)
|
||||||
|
SQLI_PATTERN = re.compile(r'(UNION\s+SELECT|SELECT\s+.*\s+FROM|OR\s+1\s*=\s*1|--|/\*|\*/)', re.IGNORECASE)
|
||||||
|
|
||||||
log_pattern = re.compile(r'(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[.*?\] "(?P<method>\w+) (?P<path>\S+) HTTP/.*?" (?P<status>\d{3})')
|
def parse_auth_logs(filepath):
|
||||||
|
"""تحليل سجلات المصادقة لكشف هجمات الـ SSH Brute Force"""
|
||||||
|
alerts = []
|
||||||
|
failed_attempts = defaultdict(int)
|
||||||
|
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return alerts
|
||||||
|
|
||||||
try:
|
# النمط: Failed password for invalid user admin from 192.168.1.50 port 54322 ssh2
|
||||||
with open(LOG_FILE, 'r') as f:
|
pattern = re.compile(r'Failed password for .* from (?P<ip>\d+\.\d+\.\d+\.\d+)')
|
||||||
|
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
match = log_pattern.search(line)
|
match = pattern.search(line)
|
||||||
|
if match:
|
||||||
|
ip = match.group('ip')
|
||||||
|
failed_attempts[ip] += 1
|
||||||
|
if failed_attempts[ip] == BRUTE_FORCE_THRESHOLD:
|
||||||
|
alerts.append({
|
||||||
|
"source": "Auth Logs (SSH)",
|
||||||
|
"ip": ip,
|
||||||
|
"type": "SSH Brute Force Attempt",
|
||||||
|
"severity": "High",
|
||||||
|
"details": f"Detected {failed_attempts[ip]} failed SSH logins."
|
||||||
|
})
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
def parse_web_logs(filepath):
|
||||||
|
"""تحليل سجلات Nginx لكشف محاولات الاختراق (SQLi / XSS)"""
|
||||||
|
alerts = []
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
# النمط القياسي لـ Nginx Log
|
||||||
|
pattern = re.compile(r'(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[.*\] "(?P<method>\w+) (?P<path>\S+) HTTP/.*" (?P<status>\d{3})')
|
||||||
|
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
match = pattern.search(line)
|
||||||
if match:
|
if match:
|
||||||
data = match.groupdict()
|
data = match.groupdict()
|
||||||
if data['status'] == '401' and '/login' in data['path']:
|
path_decoded = data['path']
|
||||||
ip_failures[data['ip']] += 1
|
|
||||||
if ip_failures[data['ip']] >= FAILED_THRESHOLD:
|
if SQLI_PATTERN.search(path_decoded):
|
||||||
print(f"[ALERT] High Brute Force Risk: {data['ip']} ({ip_failures[data['ip']]} failed attempts)")
|
alerts.append({
|
||||||
if "select" in data['path'].lower() or "union" in data['path'].lower():
|
"source": "Web Logs (Nginx)",
|
||||||
print(f"[CRITICAL] Potential SQL Injection from {data['ip']}: {data['path']}")
|
"ip": data['ip'],
|
||||||
except FileNotFoundError:
|
"type": "SQL Injection Attempt",
|
||||||
print(f"Log file {LOG_FILE} not found. Ready for deployment.")
|
"severity": "Critical",
|
||||||
|
"details": f"Suspicious pattern in URL: {path_decoded}"
|
||||||
|
})
|
||||||
|
elif XSS_PATTERN.search(path_decoded):
|
||||||
|
alerts.append({
|
||||||
|
"source": "Web Logs (Nginx)",
|
||||||
|
"ip": data['ip'],
|
||||||
|
"type": "Cross-Site Scripting (XSS)",
|
||||||
|
"severity": "High",
|
||||||
|
"details": f"Payload detected in path: {path_decoded}"
|
||||||
|
})
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
def parse_api_logs(filepath):
|
||||||
|
"""تحليل سجلات تطبيق غيمة لكشف إساءة استخدام الـ Token أو الـ Rate Limit"""
|
||||||
|
alerts = []
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
try:
|
||||||
|
log_data = json.loads(line)
|
||||||
|
if log_data.get("status") == 429: # Too Many Requests
|
||||||
|
alerts.append({
|
||||||
|
"source": "Ghaymah API",
|
||||||
|
"ip": log_data.get("client_ip", "Unknown"),
|
||||||
|
"type": "API Rate Limit Exceeded",
|
||||||
|
"severity": "Medium",
|
||||||
|
"details": f"Client hammered Endpoint: {log_data.get('endpoint')}"
|
||||||
|
})
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
def main():
|
||||||
|
all_alerts = []
|
||||||
|
all_alerts.extend(parse_auth_logs(LOG_SOURCES["auth"]))
|
||||||
|
all_alerts.extend(parse_web_logs(LOG_SOURCES["web"]))
|
||||||
|
all_alerts.extend(parse_api_logs(LOG_SOURCES["api"]))
|
||||||
|
|
||||||
|
# تصدير النتائج لملف JSON لتقرأه الـ Dashboard
|
||||||
|
output_path = "/var/log/siem_logs/siem_alerts.json"
|
||||||
|
with open(output_path, 'w', encoding='utf-8') as out_f:
|
||||||
|
json.dump(all_alerts, out_f, indent=4)
|
||||||
|
|
||||||
|
print(f"[+] SIEM Analysis complete. {len(all_alerts)} alerts written to {output_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
المرجع في مشكلة جديدة
حظر مستخدم