Upload 10 files
Browse files- .gitignore +43 -0
- LICENSE +21 -0
- README.md +39 -3
- ai/anomaly_model.py +47 -0
- ai/risk_scoring.py +23 -0
- config.py +40 -0
- main.py +135 -0
- requirements.txt +9 -0
- test_framework.py +49 -0
- test_simulator.py +39 -0
.gitignore
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@"
|
| 2 |
+
# Python
|
| 3 |
+
__pycache__/
|
| 4 |
+
*.py[cod]
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
env/
|
| 8 |
+
venv/
|
| 9 |
+
*.egg-info/
|
| 10 |
+
dist/
|
| 11 |
+
build/
|
| 12 |
+
|
| 13 |
+
# Database files
|
| 14 |
+
*.db
|
| 15 |
+
*.sqlite
|
| 16 |
+
*.sqlite3
|
| 17 |
+
|
| 18 |
+
# Logs
|
| 19 |
+
logs/*.log
|
| 20 |
+
*.log
|
| 21 |
+
|
| 22 |
+
# Models
|
| 23 |
+
models/
|
| 24 |
+
*.pkl
|
| 25 |
+
|
| 26 |
+
# GeoIP database
|
| 27 |
+
geoip/*.mmdb
|
| 28 |
+
|
| 29 |
+
# Sensitive config
|
| 30 |
+
config_local.py
|
| 31 |
+
|
| 32 |
+
# IDE
|
| 33 |
+
.vscode/
|
| 34 |
+
.idea/
|
| 35 |
+
*.swp
|
| 36 |
+
|
| 37 |
+
# OS junk
|
| 38 |
+
.DS_Store
|
| 39 |
+
Thumbs.db
|
| 40 |
+
|
| 41 |
+
# Temporary PCAP exports
|
| 42 |
+
*.pcap
|
| 43 |
+
"@ | Out-File -FilePath .gitignore -Encoding utf8
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 GhostCmd
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MayOne Security Framework
|
| 2 |
+
|
| 3 |
+
**AI‑powered Intrusion Detection & Response for Windows**
|
| 4 |
+
|
| 5 |
+

|
| 6 |
+
|
| 7 |
+
## Features
|
| 8 |
+
|
| 9 |
+
- Real‑time packet capture (Scapy)
|
| 10 |
+
- Rule‑based threat detection (port scan, brute force, DDoS, bursts)
|
| 11 |
+
- AI anomaly detection (Isolation Forest) – learns normal traffic
|
| 12 |
+
- Risk scoring (0–100) with threat levels: LOW, MEDIUM, HIGH, CRITICAL
|
| 13 |
+
- Automatic IP blocking via Windows Firewall (inbound+outbound)
|
| 14 |
+
- SQLite database for events, threats, blocked IPs, reports
|
| 15 |
+
- Live Flask dashboard with:
|
| 16 |
+
- Traffic statistics
|
| 17 |
+
- Protocol distribution & top ports charts
|
| 18 |
+
- Recent threats table
|
| 19 |
+
- Manual IP block/unblock
|
| 20 |
+
- Geo‑IP blocking (optional, MaxMind GeoLite2)
|
| 21 |
+
- Scheduled & emergency PDF reports (with logo watermark)
|
| 22 |
+
- PCAP export (full buffer)
|
| 23 |
+
- Multithreaded, thread‑safe, low CPU usage
|
| 24 |
+
|
| 25 |
+
## Requirements
|
| 26 |
+
|
| 27 |
+
- Windows 10/11 (or Windows Server)
|
| 28 |
+
- Python 3.10 or higher
|
| 29 |
+
- Npcap (with WinPcap API compatibility) – [Download](https://npcap.com)
|
| 30 |
+
- Administrator privileges (for sniffing and firewall changes)
|
| 31 |
+
|
| 32 |
+
## Installation
|
| 33 |
+
|
| 34 |
+
1. Clone the repository:
|
| 35 |
+
```bash
|
| 36 |
+
git clone https://github.com/yourusername/MayOne-Security-Framework.git
|
| 37 |
+
cd MayOne-Security-Framework
|
| 38 |
+
pip install -r requirements.txt
|
| 39 |
+
python main.py
|
ai/anomaly_model.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from sklearn.ensemble import IsolationForest
|
| 3 |
+
import pickle
|
| 4 |
+
import os
|
| 5 |
+
import threading
|
| 6 |
+
|
| 7 |
+
class AnomalyModel:
|
| 8 |
+
def __init__(self, buffer_size=500):
|
| 9 |
+
self.model = None
|
| 10 |
+
self.feature_buffer = []
|
| 11 |
+
self.buffer_size = buffer_size
|
| 12 |
+
self.is_trained = False
|
| 13 |
+
self.lock = threading.Lock()
|
| 14 |
+
|
| 15 |
+
def _extract_features(self, packet_info):
|
| 16 |
+
size = packet_info['size']
|
| 17 |
+
proto = packet_info['protocol']
|
| 18 |
+
proto_map = {'TCP': 0, 'UDP': 1, 'ICMP': 2, 'OTHER': 3}
|
| 19 |
+
proto_code = proto_map.get(proto, 3)
|
| 20 |
+
return [size, proto_code]
|
| 21 |
+
|
| 22 |
+
def add_packet(self, packet_info):
|
| 23 |
+
feats = self._extract_features(packet_info)
|
| 24 |
+
with self.lock:
|
| 25 |
+
self.feature_buffer.append(feats)
|
| 26 |
+
if len(self.feature_buffer) >= self.buffer_size and not self.is_trained:
|
| 27 |
+
self._train()
|
| 28 |
+
return feats
|
| 29 |
+
|
| 30 |
+
def _train(self):
|
| 31 |
+
X = np.array(self.feature_buffer)
|
| 32 |
+
self.model = IsolationForest(contamination=0.1, random_state=42)
|
| 33 |
+
self.model.fit(X)
|
| 34 |
+
self.is_trained = True
|
| 35 |
+
print(f"[AI] Anomaly model trained on {len(X)} samples.")
|
| 36 |
+
|
| 37 |
+
def predict_anomaly_score(self, packet_info):
|
| 38 |
+
if not self.is_trained:
|
| 39 |
+
return 0.0
|
| 40 |
+
feats = self._extract_features(packet_info)
|
| 41 |
+
pred = self.model.predict([feats])[0]
|
| 42 |
+
if pred == -1:
|
| 43 |
+
score = -self.model.decision_function([feats])[0]
|
| 44 |
+
score = np.clip(score, 0, 1)
|
| 45 |
+
else:
|
| 46 |
+
score = 0.0
|
| 47 |
+
return score
|
ai/risk_scoring.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class RiskScorer:
|
| 2 |
+
def __init__(self, rule_weight=0.6, ai_weight=0.4):
|
| 3 |
+
self.rule_weight = rule_weight
|
| 4 |
+
self.ai_weight = ai_weight
|
| 5 |
+
|
| 6 |
+
def compute_risk(self, rule_threats, ai_anomaly_score):
|
| 7 |
+
if rule_threats:
|
| 8 |
+
max_rule_risk = max(score for _, score in rule_threats) / 100.0
|
| 9 |
+
else:
|
| 10 |
+
max_rule_risk = 0.0
|
| 11 |
+
|
| 12 |
+
combined = self.rule_weight * max_rule_risk + self.ai_weight * ai_anomaly_score
|
| 13 |
+
return min(100, int(combined * 100))
|
| 14 |
+
|
| 15 |
+
def threat_level(self, risk_score):
|
| 16 |
+
if risk_score >= 80:
|
| 17 |
+
return "CRITICAL"
|
| 18 |
+
elif risk_score >= 60:
|
| 19 |
+
return "HIGH"
|
| 20 |
+
elif risk_score >= 30:
|
| 21 |
+
return "MEDIUM"
|
| 22 |
+
else:
|
| 23 |
+
return "LOW"
|
config.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Configuration file for MayOne Security Framework
|
| 2 |
+
|
| 3 |
+
# Detection thresholds
|
| 4 |
+
THRESHOLD = 0.6 # Risk threshold (0-1) above which action is taken
|
| 5 |
+
TIME_WINDOW = 10 # Time window in seconds for burst/scan detection
|
| 6 |
+
PORT_SCAN_THRESHOLD = 20 # Number of unique ports from one src in TIME_WINDOW
|
| 7 |
+
BRUTE_FORCE_THRESHOLD = 10 # Number of failed-like packets (e.g., TCP to port 22/3389)
|
| 8 |
+
DDoS_THRESHOLD = 100 # Packets per second from one src to trigger DDoS suspicion
|
| 9 |
+
BURST_THRESHOLD = 50 # Packets in 1 second from one src
|
| 10 |
+
|
| 11 |
+
# Response
|
| 12 |
+
AUTO_BLOCK = True # Automatically block IP via Windows Firewall
|
| 13 |
+
LOG_LEVEL = "INFO" # DEBUG, INFO, WARNING, ERROR
|
| 14 |
+
REPORT_INTERVAL = 600 # Seconds between automatic reports (10 minutes)
|
| 15 |
+
|
| 16 |
+
# Network interface to sniff (use None for default)
|
| 17 |
+
NETWORK_INTERFACE = None # e.g., "Ethernet" or "Wi-Fi"
|
| 18 |
+
|
| 19 |
+
# Dashboard
|
| 20 |
+
DASHBOARD_HOST = "127.0.0.1"
|
| 21 |
+
DASHBOARD_PORT = 5000
|
| 22 |
+
|
| 23 |
+
# Database
|
| 24 |
+
DB_PATH = "database/security_events.db"
|
| 25 |
+
|
| 26 |
+
# Whitelisted private IP ranges (CIDR)
|
| 27 |
+
PRIVATE_RANGES = [
|
| 28 |
+
"127.0.0.0/8",
|
| 29 |
+
"10.0.0.0/8",
|
| 30 |
+
"172.16.0.0/12",
|
| 31 |
+
"192.168.0.0/16",
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
# GeoIP blocking
|
| 35 |
+
ENABLE_GEOIP_BLOCK = True # Default state (can be toggled from dashboard)
|
| 36 |
+
GEOIP_DB_PATH = "geoip/GeoLite2-Country.mmdb"
|
| 37 |
+
HIGH_RISK_COUNTRIES = ["RU", "CN", "KP", "IR", "SY", "UA", "AF", "IQ", "LY", "SO"]
|
| 38 |
+
|
| 39 |
+
# PCAP export
|
| 40 |
+
PCAP_BUFFER_SIZE = 10000 # Max number of raw packets to keep
|
main.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import threading
|
| 3 |
+
import queue
|
| 4 |
+
import time
|
| 5 |
+
import logging
|
| 6 |
+
import signal
|
| 7 |
+
import sys
|
| 8 |
+
import ipaddress
|
| 9 |
+
from config import *
|
| 10 |
+
from database.db import Database
|
| 11 |
+
from monitor.packet_sniffer import PacketSniffer
|
| 12 |
+
from detection.threat_detector import ThreatDetector
|
| 13 |
+
from ai.anomaly_model import AnomalyModel
|
| 14 |
+
from ai.risk_scoring import RiskScorer
|
| 15 |
+
from response.block_ip import block_ip_windows
|
| 16 |
+
from reports.report_generator import ReportGenerator
|
| 17 |
+
from dashboard.app import run_dashboard
|
| 18 |
+
from geoip.blocker import GeoIPBlocker
|
| 19 |
+
|
| 20 |
+
logging.basicConfig(
|
| 21 |
+
filename='logs/system.log',
|
| 22 |
+
level=getattr(logging, LOG_LEVEL),
|
| 23 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 24 |
+
)
|
| 25 |
+
console = logging.StreamHandler()
|
| 26 |
+
console.setLevel(logging.INFO)
|
| 27 |
+
logging.getLogger('').addHandler(console)
|
| 28 |
+
|
| 29 |
+
# Global variable that can be toggled from dashboard
|
| 30 |
+
geoip_enabled = ENABLE_GEOIP_BLOCK
|
| 31 |
+
|
| 32 |
+
def is_private_ip(ip):
|
| 33 |
+
try:
|
| 34 |
+
addr = ipaddress.ip_address(ip)
|
| 35 |
+
for cidr in PRIVATE_RANGES:
|
| 36 |
+
if addr in ipaddress.ip_network(cidr):
|
| 37 |
+
return True
|
| 38 |
+
return False
|
| 39 |
+
except:
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
class MayOneEngine:
|
| 43 |
+
def __init__(self):
|
| 44 |
+
self.packet_queue = queue.Queue(maxsize=10000)
|
| 45 |
+
self.db = Database()
|
| 46 |
+
self.sniffer = PacketSniffer(self.packet_queue, NETWORK_INTERFACE, PCAP_BUFFER_SIZE)
|
| 47 |
+
self.detector = ThreatDetector(TIME_WINDOW, PORT_SCAN_THRESHOLD,
|
| 48 |
+
BRUTE_FORCE_THRESHOLD, DDoS_THRESHOLD, BURST_THRESHOLD)
|
| 49 |
+
self.anomaly_model = AnomalyModel()
|
| 50 |
+
self.scorer = RiskScorer()
|
| 51 |
+
self.reporter = ReportGenerator(self.db)
|
| 52 |
+
self.geoip = GeoIPBlocker() if ENABLE_GEOIP_BLOCK else None
|
| 53 |
+
self.running = True
|
| 54 |
+
self.last_report_time = time.time()
|
| 55 |
+
self.recently_blocked = set()
|
| 56 |
+
|
| 57 |
+
def start(self):
|
| 58 |
+
logging.info("Starting MayOne Security Framework")
|
| 59 |
+
self.sniffer.start()
|
| 60 |
+
dash_thread = threading.Thread(target=run_dashboard, args=(DASHBOARD_HOST, DASHBOARD_PORT, self.sniffer), daemon=True)
|
| 61 |
+
dash_thread.start()
|
| 62 |
+
while self.running:
|
| 63 |
+
try:
|
| 64 |
+
packet = self.packet_queue.get(timeout=1)
|
| 65 |
+
self.process_packet(packet)
|
| 66 |
+
except queue.Empty:
|
| 67 |
+
self.check_auto_report()
|
| 68 |
+
continue
|
| 69 |
+
except Exception as e:
|
| 70 |
+
logging.error(f"Main loop error: {e}", exc_info=True)
|
| 71 |
+
|
| 72 |
+
def process_packet(self, packet):
|
| 73 |
+
global geoip_enabled
|
| 74 |
+
src_ip = packet['src_ip']
|
| 75 |
+
|
| 76 |
+
# GeoIP blocking check (if enabled)
|
| 77 |
+
if geoip_enabled and self.geoip and not is_private_ip(src_ip):
|
| 78 |
+
if self.geoip.is_high_risk(src_ip):
|
| 79 |
+
logging.info(f"GeoIP high-risk country detected: {src_ip}")
|
| 80 |
+
if AUTO_BLOCK and src_ip not in self.recently_blocked:
|
| 81 |
+
if block_ip_windows(src_ip, "GeoIP high-risk country"):
|
| 82 |
+
self.db.insert_blocked_ip(src_ip, "GeoIP auto-block")
|
| 83 |
+
self.recently_blocked.add(src_ip)
|
| 84 |
+
action = "GEO_BLOCKED"
|
| 85 |
+
self.db.insert_event(src_ip, packet['dst_ip'], packet['protocol'],
|
| 86 |
+
packet['port'], packet['size'], "GEOIP_RISK", 90, action)
|
| 87 |
+
self.db.insert_threat(src_ip, "GEOIP_RISK", 90, f"Country: high-risk")
|
| 88 |
+
return # skip further processing
|
| 89 |
+
|
| 90 |
+
# Normal AI + rule detection
|
| 91 |
+
anomaly_score = self.anomaly_model.predict_anomaly_score(packet)
|
| 92 |
+
rule_threats = self.detector.detect(packet)
|
| 93 |
+
risk = self.scorer.compute_risk(rule_threats, anomaly_score)
|
| 94 |
+
threat_level = self.scorer.threat_level(risk)
|
| 95 |
+
action = None
|
| 96 |
+
|
| 97 |
+
if risk >= THRESHOLD * 100 and AUTO_BLOCK and not is_private_ip(src_ip):
|
| 98 |
+
if src_ip not in self.recently_blocked:
|
| 99 |
+
if block_ip_windows(src_ip, f"{threat_level} risk {risk}"):
|
| 100 |
+
self.db.insert_blocked_ip(src_ip, f"{threat_level} risk")
|
| 101 |
+
self.recently_blocked.add(src_ip)
|
| 102 |
+
action = "BLOCKED"
|
| 103 |
+
|
| 104 |
+
threat_type = rule_threats[0][0] if rule_threats else None
|
| 105 |
+
self.db.insert_event(
|
| 106 |
+
src_ip, packet['dst_ip'], packet['protocol'],
|
| 107 |
+
packet['port'], packet['size'], threat_type, risk, action
|
| 108 |
+
)
|
| 109 |
+
if threat_type or anomaly_score > 0.3:
|
| 110 |
+
self.db.insert_threat(src_ip, threat_type or "ANOMALY", risk,
|
| 111 |
+
f"Anomaly={anomaly_score:.2f}")
|
| 112 |
+
|
| 113 |
+
if threat_level == "CRITICAL":
|
| 114 |
+
logging.warning(f"CRITICAL threat from {src_ip} - generating emergency report")
|
| 115 |
+
threading.Thread(target=self.reporter.generate_report, args=("critical_threat",)).start()
|
| 116 |
+
|
| 117 |
+
self.anomaly_model.add_packet(packet)
|
| 118 |
+
|
| 119 |
+
def check_auto_report(self):
|
| 120 |
+
if time.time() - self.last_report_time >= REPORT_INTERVAL:
|
| 121 |
+
logging.info("Generating scheduled report")
|
| 122 |
+
self.reporter.generate_report("scheduled")
|
| 123 |
+
self.last_report_time = time.time()
|
| 124 |
+
|
| 125 |
+
def shutdown(self):
|
| 126 |
+
logging.info("Shutting down MayOne Security Framework")
|
| 127 |
+
self.running = False
|
| 128 |
+
self.sniffer.stop()
|
| 129 |
+
self.db.close()
|
| 130 |
+
sys.exit(0)
|
| 131 |
+
|
| 132 |
+
if __name__ == "__main__":
|
| 133 |
+
engine = MayOneEngine()
|
| 134 |
+
signal.signal(signal.SIGINT, lambda sig, frame: engine.shutdown())
|
| 135 |
+
engine.start()
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scapy==2.5.0
|
| 2 |
+
pandas==2.0.3
|
| 3 |
+
numpy==1.24.3
|
| 4 |
+
scikit-learn==1.3.0
|
| 5 |
+
flask==2.3.3
|
| 6 |
+
reportlab==4.0.4
|
| 7 |
+
waitress==2.1.2
|
| 8 |
+
geoip2==4.8.1
|
| 9 |
+
maxminddb==2.6.2
|
test_framework.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
sys.path.append('.')
|
| 5 |
+
from database.db import Database
|
| 6 |
+
from detection.threat_detector import ThreatDetector
|
| 7 |
+
from ai.risk_scoring import RiskScorer
|
| 8 |
+
from reports.report_generator import ReportGenerator
|
| 9 |
+
|
| 10 |
+
def test_db():
|
| 11 |
+
db = Database()
|
| 12 |
+
db.insert_event("192.168.1.100", "8.8.8.8", "TCP", 443, 1500, "TEST", 50, None)
|
| 13 |
+
events = db.get_recent_events()
|
| 14 |
+
assert len(events) >= 1
|
| 15 |
+
print("[OK] Database works")
|
| 16 |
+
|
| 17 |
+
def test_detector():
|
| 18 |
+
det = ThreatDetector(time_window=10, port_scan_th=3)
|
| 19 |
+
packets = [
|
| 20 |
+
{'src_ip': '1.2.3.4', 'port': 80, 'timestamp': 100},
|
| 21 |
+
{'src_ip': '1.2.3.4', 'port': 81, 'timestamp': 101},
|
| 22 |
+
{'src_ip': '1.2.3.4', 'port': 82, 'timestamp': 102},
|
| 23 |
+
{'src_ip': '1.2.3.4', 'port': 83, 'timestamp': 103},
|
| 24 |
+
]
|
| 25 |
+
for p in packets:
|
| 26 |
+
threats = det.detect(p)
|
| 27 |
+
assert any(t[0]=='PORT_SCAN' for t in threats)
|
| 28 |
+
print("[OK] Threat detector works")
|
| 29 |
+
|
| 30 |
+
def test_risk_scorer():
|
| 31 |
+
scorer = RiskScorer()
|
| 32 |
+
risk = scorer.compute_risk([('PORT_SCAN', 80)], 0.5)
|
| 33 |
+
assert 0 <= risk <= 100
|
| 34 |
+
print("[OK] Risk scoring works")
|
| 35 |
+
|
| 36 |
+
def test_report(db):
|
| 37 |
+
gen = ReportGenerator(db)
|
| 38 |
+
paths = gen.generate_report("test")
|
| 39 |
+
for p in paths:
|
| 40 |
+
assert os.path.exists(p)
|
| 41 |
+
print("[OK] Report generation works")
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
test_db()
|
| 45 |
+
test_detector()
|
| 46 |
+
test_risk_scorer()
|
| 47 |
+
db = Database()
|
| 48 |
+
test_report(db)
|
| 49 |
+
print("All tests passed.")
|
test_simulator.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import time
|
| 3 |
+
import random
|
| 4 |
+
from scapy.all import IP, TCP, UDP, send
|
| 5 |
+
|
| 6 |
+
def port_scan(target_ip="127.0.0.1", ports=range(1, 100)):
|
| 7 |
+
print(f"[SIM] Port scanning {target_ip}...")
|
| 8 |
+
for port in ports:
|
| 9 |
+
pkt = IP(dst=target_ip)/TCP(dport=port, flags="S")
|
| 10 |
+
send(pkt, verbose=False)
|
| 11 |
+
time.sleep(0.01)
|
| 12 |
+
|
| 13 |
+
def brute_force(target_ip="127.0.0.1", port=22, attempts=50):
|
| 14 |
+
print(f"[SIM] Brute force simulation on {target_ip}:{port}")
|
| 15 |
+
for i in range(attempts):
|
| 16 |
+
pkt = IP(dst=target_ip)/TCP(dport=port, flags="S")
|
| 17 |
+
send(pkt, verbose=False)
|
| 18 |
+
time.sleep(0.05)
|
| 19 |
+
|
| 20 |
+
def ddos_flood(target_ip="127.0.0.1", duration=5, rate=200):
|
| 21 |
+
print(f"[SIM] DDoS flood on {target_ip} for {duration}s at {rate} pps")
|
| 22 |
+
end = time.time() + duration
|
| 23 |
+
while time.time() < end:
|
| 24 |
+
for _ in range(rate):
|
| 25 |
+
pkt = IP(dst=target_ip)/UDP(dport=random.randint(1, 65535))
|
| 26 |
+
send(pkt, verbose=False)
|
| 27 |
+
time.sleep(1)
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
print("Choose attack: 1=Port Scan, 2=Brute Force, 3=DDoS Flood")
|
| 31 |
+
choice = input("> ")
|
| 32 |
+
if choice == "1":
|
| 33 |
+
port_scan()
|
| 34 |
+
elif choice == "2":
|
| 35 |
+
brute_force()
|
| 36 |
+
elif choice == "3":
|
| 37 |
+
ddos_flood()
|
| 38 |
+
else:
|
| 39 |
+
print("Invalid")
|