mirror of
https://github.com/mblanke/ThreatHunt.git
synced 2026-03-01 05:50:21 -05:00
590 lines
29 KiB
Python
590 lines
29 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate 12 realistic Velociraptor-style CSV test files.
|
|
|
|
Mock network: 75 hosts, 10 users, 3 subnets.
|
|
Sprinkles AUP-triggering keywords across DNS, URL, and process data.
|
|
"""
|
|
|
|
import csv
|
|
import os
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
random.seed(42)
|
|
|
|
OUT = Path(__file__).parent / "test_csvs"
|
|
OUT.mkdir(exist_ok=True)
|
|
|
|
# ── Shared network inventory ──────────────────────────────────────────
|
|
|
|
SUBNETS = ["10.10.1", "10.10.2", "10.10.3"]
|
|
DEPARTMENTS = ["IT", "HR", "Finance", "Sales", "Engineering", "Legal", "Marketing", "Exec"]
|
|
OS_LIST = ["Windows 10 Enterprise", "Windows 11 Enterprise", "Windows Server 2022", "Windows Server 2019"]
|
|
DOMAIN = "acme.local"
|
|
|
|
HOSTS = []
|
|
for i in range(1, 76):
|
|
subnet = SUBNETS[i % 3]
|
|
ip = f"{subnet}.{100 + i}"
|
|
dept = DEPARTMENTS[i % len(DEPARTMENTS)]
|
|
prefix = {"IT": "IT-WS", "HR": "HR-WS", "Finance": "FIN-WS", "Sales": "SLS-WS",
|
|
"Engineering": "ENG-WS", "Legal": "LEG-WS", "Marketing": "MKT-WS", "Exec": "EXEC-WS"}
|
|
hostname = f"{prefix.get(dept, 'WS')}-{i:03d}"
|
|
os_ver = OS_LIST[i % len(OS_LIST)]
|
|
mac = f"00:1A:2B:{i:02X}:{(i*3)%256:02X}:{(i*7)%256:02X}"
|
|
HOSTS.append({"hostname": hostname, "ip": ip, "os": os_ver, "mac": mac, "dept": dept})
|
|
|
|
SERVERS = [
|
|
{"hostname": "DC-01", "ip": "10.10.1.10", "os": "Windows Server 2022", "mac": "00:1A:2B:AA:01:01"},
|
|
{"hostname": "DC-02", "ip": "10.10.2.10", "os": "Windows Server 2022", "mac": "00:1A:2B:AA:02:02"},
|
|
{"hostname": "FILE-01", "ip": "10.10.1.11", "os": "Windows Server 2019", "mac": "00:1A:2B:AA:03:03"},
|
|
{"hostname": "EXCH-01", "ip": "10.10.1.12", "os": "Windows Server 2022", "mac": "00:1A:2B:AA:04:04"},
|
|
{"hostname": "WEB-01", "ip": "10.10.3.10", "os": "Windows Server 2022", "mac": "00:1A:2B:AA:05:05"},
|
|
{"hostname": "SQL-01", "ip": "10.10.2.11", "os": "Windows Server 2019", "mac": "00:1A:2B:AA:06:06"},
|
|
{"hostname": "PROXY-01", "ip": "10.10.1.13", "os": "Windows Server 2022", "mac": "00:1A:2B:AA:07:07"},
|
|
]
|
|
ALL_HOSTS = HOSTS + SERVERS
|
|
|
|
USERS = [
|
|
"jsmith", "agarcia", "bwilson", "cjohnson", "dlee",
|
|
"emartinez", "fthompson", "gwhite", "hbrown", "idavis",
|
|
"admin", "svc_backup", "svc_sql", "svc_web",
|
|
]
|
|
|
|
# Base time range: 2-week window
|
|
BASE_TIME = datetime(2026, 2, 10, 8, 0, 0)
|
|
END_TIME = datetime(2026, 2, 20, 18, 0, 0)
|
|
|
|
def rand_ts():
|
|
delta = (END_TIME - BASE_TIME).total_seconds()
|
|
return BASE_TIME + timedelta(seconds=random.uniform(0, delta))
|
|
|
|
def ts_str(dt=None):
|
|
return (dt or rand_ts()).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
|
|
def rand_host():
|
|
return random.choice(ALL_HOSTS)
|
|
|
|
def rand_user():
|
|
return random.choice(USERS)
|
|
|
|
def rand_ext_ip():
|
|
return f"{random.randint(1,223)}.{random.randint(0,255)}.{random.randint(0,255)}.{random.randint(1,254)}"
|
|
|
|
# AUP trigger domains/URLs (will be sprinkled into DNS and proxy logs)
|
|
AUP_DOMAINS = [
|
|
"www.bet365.com", "pokerstars.com", "draftkings.com", # Gambling
|
|
"store.steampowered.com", "steamcommunity.com", "discord.gg", # Gaming
|
|
"www.netflix.com", "hulu.com", "open.spotify.com", # Streaming
|
|
"thepiratebay.org", "1337x.to", "fitgirl-repacks.site", # Piracy
|
|
"www.pornhub.com", "onlyfans.com", "xvideos.com", # Adult
|
|
"www.facebook.com", "www.tiktok.com", "www.reddit.com", # Social Media
|
|
"www.indeed.com", "www.glassdoor.com", "www.linkedin.com/jobs", # Job Search
|
|
"www.amazon.com", "www.ebay.com", "www.shein.com", # Shopping
|
|
]
|
|
|
|
AUP_PROCESSES = [
|
|
"utorrent.exe", "qbittorrent.exe", "steam.exe", "discord.exe",
|
|
"spotify.exe", "epicgameslauncher.exe",
|
|
]
|
|
|
|
LEGIT_DOMAINS = [
|
|
"login.microsoftonline.com", "outlook.office365.com", "teams.microsoft.com",
|
|
"graph.microsoft.com", "update.microsoft.com", "windowsupdate.com",
|
|
"acme.sharepoint.com", "acme.local", "dc-01.acme.local", "dc-02.acme.local",
|
|
"file-01.acme.local", "exch-01.acme.local", "github.com", "stackoverflow.com",
|
|
"cdn.jsdelivr.net", "pypi.org", "npmjs.com", "google.com", "googleapis.com",
|
|
"cloudflare.com", "aws.amazon.com", "akamai.net", "time.windows.com",
|
|
]
|
|
|
|
LEGIT_PROCESSES = [
|
|
"svchost.exe", "explorer.exe", "chrome.exe", "msedge.exe", "outlook.exe",
|
|
"teams.exe", "code.exe", "powershell.exe", "cmd.exe", "notepad.exe",
|
|
"taskhostw.exe", "RuntimeBroker.exe", "SearchHost.exe", "lsass.exe",
|
|
"csrss.exe", "winlogon.exe", "dwm.exe", "System", "smss.exe",
|
|
"services.exe", "spoolsv.exe", "MsMpEng.exe", "OneDrive.exe",
|
|
]
|
|
|
|
STATES = ["ESTABLISHED", "LISTEN", "TIME_WAIT", "CLOSE_WAIT", "SYN_SENT"]
|
|
PROTOCOLS = ["TCP", "UDP", "TCP", "TCP", "TCP"]
|
|
|
|
def write_csv(filename, headers, rows):
|
|
path = OUT / filename
|
|
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
w = csv.DictWriter(f, fieldnames=headers)
|
|
w.writeheader()
|
|
w.writerows(rows)
|
|
print(f" {filename}: {len(rows)} rows")
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 1. Netstat connections (Velociraptor: Windows.Network.Netstat)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_netstat():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
n_conns = random.randint(8, 40)
|
|
for _ in range(n_conns):
|
|
proc = random.choice(LEGIT_PROCESSES + (AUP_PROCESSES if random.random() < 0.08 else []))
|
|
state = random.choice(STATES)
|
|
proto = random.choice(PROTOCOLS)
|
|
local_port = random.choice([80, 443, 445, 135, 139, 3389, 5985, 8080, 53, 88, 389, 636,
|
|
random.randint(49152, 65535)])
|
|
if state == "LISTEN":
|
|
remote_ip = "0.0.0.0"
|
|
remote_port = 0
|
|
else:
|
|
remote_ip = random.choice([rand_ext_ip(), random.choice(ALL_HOSTS)["ip"]])
|
|
remote_port = random.choice([80, 443, 8080, 3389, 445, 53, 88, 389, 636,
|
|
random.randint(1024, 65535)])
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"Timestamp": ts_str(),
|
|
"Pid": random.randint(100, 65000),
|
|
"Name": proc,
|
|
"Status": state,
|
|
"Protocol": proto,
|
|
"Laddr.IP": host["ip"],
|
|
"Laddr.Port": local_port,
|
|
"Raddr.IP": remote_ip,
|
|
"Raddr.Port": remote_port,
|
|
"Username": rand_user(),
|
|
})
|
|
return rows
|
|
|
|
print("Generating Velociraptor test CSVs...")
|
|
rows = gen_netstat()
|
|
write_csv("01_netstat_connections.csv",
|
|
["Hostname", "Timestamp", "Pid", "Name", "Status", "Protocol",
|
|
"Laddr.IP", "Laddr.Port", "Raddr.IP", "Raddr.Port", "Username"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 2. DNS queries (Velociraptor: Windows.Network.DNS)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_dns():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
n_queries = random.randint(15, 60)
|
|
for _ in range(n_queries):
|
|
if random.random() < 0.12:
|
|
domain = random.choice(AUP_DOMAINS)
|
|
else:
|
|
domain = random.choice(LEGIT_DOMAINS)
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"EventTime": ts_str(),
|
|
"QueryName": domain,
|
|
"QueryType": random.choice(["A", "AAAA", "CNAME", "MX", "TXT", "A", "A"]),
|
|
"ResponseCode": random.choice(["NOERROR", "NOERROR", "NOERROR", "NXDOMAIN", "SERVFAIL"]),
|
|
"SourceIP": host["ip"],
|
|
"AnswerIP": rand_ext_ip() if random.random() > 0.2 else "",
|
|
})
|
|
return rows
|
|
|
|
rows = gen_dns()
|
|
write_csv("02_dns_queries.csv",
|
|
["Hostname", "EventTime", "QueryName", "QueryType", "ResponseCode", "SourceIP", "AnswerIP"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 3. Process listing (Velociraptor: Windows.System.Pslist)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_pslist():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
user = rand_user()
|
|
for proc in LEGIT_PROCESSES:
|
|
pid = random.randint(100, 65000)
|
|
ppid = random.randint(1, 20) if proc in ("svchost.exe", "csrss.exe", "lsass.exe") else random.randint(100, 60000)
|
|
rows.append({
|
|
"ComputerName": host["hostname"],
|
|
"CreateTime": ts_str(),
|
|
"Pid": pid,
|
|
"PPid": ppid,
|
|
"Name": proc,
|
|
"CommandLine": f"C:\\Windows\\System32\\{proc}" if proc != "System" else "System",
|
|
"Username": f"ACME\\{user}" if proc not in ("System", "smss.exe", "csrss.exe") else "NT AUTHORITY\\SYSTEM",
|
|
"MemoryUsage": random.randint(1024, 500000),
|
|
})
|
|
# Sprinkle AUP processes on ~15% of hosts
|
|
if random.random() < 0.15:
|
|
aup_proc = random.choice(AUP_PROCESSES)
|
|
rows.append({
|
|
"ComputerName": host["hostname"],
|
|
"CreateTime": ts_str(),
|
|
"Pid": random.randint(10000, 65000),
|
|
"PPid": random.randint(100, 60000),
|
|
"Name": aup_proc,
|
|
"CommandLine": f"C:\\Users\\{user}\\AppData\\Local\\{aup_proc.replace('.exe', '')}\\{aup_proc}",
|
|
"Username": f"ACME\\{user}",
|
|
"MemoryUsage": random.randint(50000, 400000),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_pslist()
|
|
write_csv("03_process_listing.csv",
|
|
["ComputerName", "CreateTime", "Pid", "PPid", "Name", "CommandLine", "Username", "MemoryUsage"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 4. Network interfaces (Velociraptor: Windows.Network.Interfaces)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_interfaces():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
h = host
|
|
rows.append({
|
|
"Hostname": h["hostname"],
|
|
"Timestamp": ts_str(),
|
|
"Name": "Ethernet0",
|
|
"MacAddress": h.get("mac", f"00:1A:2B:{random.randint(0,255):02X}:{random.randint(0,255):02X}:{random.randint(0,255):02X}"),
|
|
"IP": h["ip"],
|
|
"Netmask": "255.255.255.0",
|
|
"Gateway": h["ip"].rsplit(".", 1)[0] + ".1",
|
|
"DNSServer": "10.10.1.10",
|
|
"DHCPEnabled": random.choice(["True", "False"]),
|
|
"Status": "Up",
|
|
})
|
|
# Some hosts have a secondary NIC
|
|
if random.random() < 0.15:
|
|
rows.append({
|
|
"Hostname": h["hostname"],
|
|
"Timestamp": ts_str(),
|
|
"Name": "WiFi",
|
|
"MacAddress": f"00:1A:2B:{random.randint(0,255):02X}:{random.randint(0,255):02X}:{random.randint(0,255):02X}",
|
|
"IP": f"192.168.1.{random.randint(100,254)}",
|
|
"Netmask": "255.255.255.0",
|
|
"Gateway": "192.168.1.1",
|
|
"DNSServer": "192.168.1.1",
|
|
"DHCPEnabled": "True",
|
|
"Status": "Up",
|
|
})
|
|
return rows
|
|
|
|
rows = gen_interfaces()
|
|
write_csv("04_network_interfaces.csv",
|
|
["Hostname", "Timestamp", "Name", "MacAddress", "IP", "Netmask", "Gateway", "DNSServer", "DHCPEnabled", "Status"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 5. Logged-in users (Velociraptor: Windows.Sys.Users)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_logged_users():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
n_users = random.randint(1, 3)
|
|
used = set()
|
|
for _ in range(n_users):
|
|
u = rand_user()
|
|
while u in used:
|
|
u = rand_user()
|
|
used.add(u)
|
|
logon_ts = rand_ts()
|
|
rows.append({
|
|
"ComputerName": host["hostname"],
|
|
"SourceIP": host["ip"],
|
|
"User": f"ACME\\{u}",
|
|
"LogonType": random.choice([2, 3, 10, 10, 2]), # 2=Interactive, 3=Network, 10=RDP
|
|
"LogonTime": ts_str(logon_ts),
|
|
"LogoffTime": ts_str(logon_ts + timedelta(hours=random.uniform(0.5, 10))) if random.random() > 0.3 else "",
|
|
"OS": host.get("os", "Windows 10 Enterprise"),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_logged_users()
|
|
write_csv("05_logged_in_users.csv",
|
|
["ComputerName", "SourceIP", "User", "LogonType", "LogonTime", "LogoffTime", "OS"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 6. Scheduled tasks (Velociraptor: Windows.System.TaskScheduler)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
SCHED_TASKS = [
|
|
("\\Microsoft\\Windows\\UpdateOrchestrator\\Schedule Scan", "C:\\Windows\\System32\\usoclient.exe StartScan"),
|
|
("\\Microsoft\\Windows\\Defrag\\ScheduledDefrag", "C:\\Windows\\System32\\defrag.exe -c -h -o"),
|
|
("\\Microsoft\\Windows\\WindowsUpdate\\Automatic App Update", "C:\\Windows\\System32\\UsoClient.exe"),
|
|
("\\ACME\\Backup", "C:\\Tools\\backup.ps1"),
|
|
("\\ACME\\Inventory", "C:\\Tools\\inventory.exe --scan"),
|
|
]
|
|
|
|
def gen_sched_tasks():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
for task_name, cmd in SCHED_TASKS:
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"IP": host["ip"],
|
|
"TaskName": task_name,
|
|
"CommandLine": cmd,
|
|
"Enabled": random.choice(["True", "True", "True", "False"]),
|
|
"LastRunTime": ts_str(),
|
|
"NextRunTime": ts_str(),
|
|
"Username": "NT AUTHORITY\\SYSTEM" if "Microsoft" in task_name else f"ACME\\svc_backup",
|
|
})
|
|
return rows
|
|
|
|
rows = gen_sched_tasks()
|
|
write_csv("06_scheduled_tasks.csv",
|
|
["Hostname", "IP", "TaskName", "CommandLine", "Enabled", "LastRunTime", "NextRunTime", "Username"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 7. Browser history (Velociraptor: Windows.Application.Chrome.History)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_browser_history():
|
|
rows = []
|
|
for host in HOSTS: # only workstations
|
|
user = rand_user()
|
|
n_entries = random.randint(10, 35)
|
|
for _ in range(n_entries):
|
|
if random.random() < 0.15:
|
|
domain = random.choice(AUP_DOMAINS)
|
|
url = f"https://{domain}/{random.choice(['', 'home', 'watch', 'play', 'search?q=free+movies', 'category/popular'])}"
|
|
title_map = {
|
|
"bet365": "Bet365 - Sports Betting",
|
|
"pokerstars": "PokerStars - Online Poker",
|
|
"netflix": "Netflix - Watch TV Shows",
|
|
"steam": "Steam Store",
|
|
"piratebay": "The Pirate Bay",
|
|
"pornhub": "Pornhub",
|
|
"facebook": "Facebook - Log In",
|
|
"tiktok": "TikTok - Make Your Day",
|
|
"indeed": "Indeed - Job Search",
|
|
"amazon": "Amazon.com - Shopping",
|
|
}
|
|
title = next((v for k, v in title_map.items() if k in domain), domain)
|
|
else:
|
|
domain = random.choice(LEGIT_DOMAINS)
|
|
url = f"https://{domain}/{random.choice(['', 'docs', 'api', 'search', 'dashboard', 'inbox'])}"
|
|
title = domain
|
|
rows.append({
|
|
"ComputerName": host["hostname"],
|
|
"SourceAddress": host["ip"],
|
|
"User": user,
|
|
"URL": url,
|
|
"Title": title,
|
|
"VisitTime": ts_str(),
|
|
"VisitCount": random.randint(1, 20),
|
|
"Browser": random.choice(["Chrome", "Edge", "Chrome", "Chrome"]),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_browser_history()
|
|
write_csv("07_browser_history.csv",
|
|
["ComputerName", "SourceAddress", "User", "URL", "Title", "VisitTime", "VisitCount", "Browser"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 8. Sysmon network connections (Sysmon Event ID 3)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_sysmon_network():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
n_events = random.randint(10, 50)
|
|
user = rand_user()
|
|
for _ in range(n_events):
|
|
proc = random.choice(LEGIT_PROCESSES + (["chrome.exe", "msedge.exe"] * 3))
|
|
dst_ip = random.choice([rand_ext_ip(), random.choice(ALL_HOSTS)["ip"]])
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"EventTime": ts_str(),
|
|
"EventID": 3,
|
|
"Image": f"C:\\Windows\\System32\\{proc}" if proc not in ("chrome.exe", "msedge.exe") else f"C:\\Program Files\\{proc}",
|
|
"User": f"ACME\\{user}",
|
|
"Protocol": random.choice(["tcp", "udp"]),
|
|
"SourceIp": host["ip"],
|
|
"SourcePort": random.randint(49152, 65535),
|
|
"DestinationIp": dst_ip,
|
|
"DestinationPort": random.choice([80, 443, 53, 445, 389, 3389, 8080]),
|
|
"DestinationHostname": random.choice(LEGIT_DOMAINS + AUP_DOMAINS[:3]) if random.random() < 0.1 else "",
|
|
})
|
|
return rows
|
|
|
|
rows = gen_sysmon_network()
|
|
write_csv("08_sysmon_network.csv",
|
|
["Hostname", "EventTime", "EventID", "Image", "User", "Protocol",
|
|
"SourceIp", "SourcePort", "DestinationIp", "DestinationPort", "DestinationHostname"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 9. Autoruns (Velociraptor: Windows.Sys.AutoRuns)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
AUTORUN_ENTRIES = [
|
|
("MicrosoftEdgeAutoLaunch", "C:\\Program Files\\Microsoft\\Edge\\msedge.exe --no-startup-window"),
|
|
("SecurityHealth", "C:\\Windows\\System32\\SecurityHealthSystray.exe"),
|
|
("OneDrive", "C:\\Users\\{user}\\AppData\\Local\\Microsoft\\OneDrive\\OneDrive.exe /background"),
|
|
("WindowsDefender", "C:\\ProgramData\\Microsoft\\Windows Defender\\MsMpEng.exe"),
|
|
]
|
|
|
|
AUP_AUTORUNS = [
|
|
("Steam", "C:\\Program Files (x86)\\Steam\\steam.exe -silent"),
|
|
("Discord", "C:\\Users\\{user}\\AppData\\Local\\Discord\\Update.exe --processStart Discord.exe"),
|
|
("Spotify", "C:\\Users\\{user}\\AppData\\Roaming\\Spotify\\Spotify.exe /minimized"),
|
|
("uTorrent", "C:\\Users\\{user}\\AppData\\Roaming\\uTorrent\\uTorrent.exe /minimized"),
|
|
]
|
|
|
|
def gen_autoruns():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
user = rand_user()
|
|
for name, cmd in AUTORUN_ENTRIES:
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"IP": host["ip"],
|
|
"EntryName": name,
|
|
"EntryPath": cmd.replace("{user}", user),
|
|
"Category": "Logon",
|
|
"Enabled": "True",
|
|
"Signer": "Microsoft Corporation" if "Microsoft" in cmd or "Windows" in cmd else "(Not Signed)",
|
|
"MD5": f"{random.randint(0, 2**128):032x}",
|
|
"Timestamp": ts_str(),
|
|
})
|
|
# ~20% of workstations have AUP autoruns
|
|
if host in HOSTS and random.random() < 0.20:
|
|
entry = random.choice(AUP_AUTORUNS)
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"IP": host["ip"],
|
|
"EntryName": entry[0],
|
|
"EntryPath": entry[1].replace("{user}", user),
|
|
"Category": "Logon",
|
|
"Enabled": "True",
|
|
"Signer": "(Not Signed)",
|
|
"MD5": f"{random.randint(0, 2**128):032x}",
|
|
"Timestamp": ts_str(),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_autoruns()
|
|
write_csv("09_autoruns.csv",
|
|
["Hostname", "IP", "EntryName", "EntryPath", "Category", "Enabled", "Signer", "MD5", "Timestamp"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 10. Windows Event Logs — Logon events (Event IDs 4624/4625)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_logon_events():
|
|
rows = []
|
|
for host in ALL_HOSTS:
|
|
n_events = random.randint(8, 30)
|
|
for _ in range(n_events):
|
|
event_id = random.choice([4624, 4624, 4624, 4624, 4625])
|
|
logon_type = random.choice([2, 3, 7, 10, 3, 3])
|
|
user = rand_user()
|
|
src_ip = random.choice([host["ip"], random.choice(ALL_HOSTS)["ip"], "127.0.0.1"])
|
|
rows.append({
|
|
"ComputerName": host["hostname"],
|
|
"System.TimeCreated": ts_str(),
|
|
"EventID": event_id,
|
|
"LogonType": logon_type,
|
|
"SubjectUserName": user,
|
|
"SubjectUserSid": f"S-1-5-21-{random.randint(1000000,9999999)}-{random.randint(1000,9999)}",
|
|
"SourceAddress": src_ip,
|
|
"SourcePort": random.randint(1024, 65535),
|
|
"Status": "0x0" if event_id == 4624 else "0xC000006D",
|
|
"FailureReason": "" if event_id == 4624 else "Unknown user name or bad password",
|
|
})
|
|
return rows
|
|
|
|
rows = gen_logon_events()
|
|
write_csv("10_logon_events.csv",
|
|
["ComputerName", "System.TimeCreated", "EventID", "LogonType",
|
|
"SubjectUserName", "SubjectUserSid", "SourceAddress", "SourcePort",
|
|
"Status", "FailureReason"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 11. Proxy / web filter logs
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def gen_proxy_logs():
|
|
rows = []
|
|
for host in HOSTS: # workstations only
|
|
n_entries = random.randint(15, 50)
|
|
user = rand_user()
|
|
for _ in range(n_entries):
|
|
if random.random() < 0.10:
|
|
dom = random.choice(AUP_DOMAINS)
|
|
url = f"https://{dom}/"
|
|
action = random.choice(["BLOCKED", "ALLOWED", "ALLOWED"])
|
|
category = random.choice(["Gambling", "Gaming", "Streaming", "Adult", "Social Media", "Shopping", "Piracy"])
|
|
else:
|
|
dom = random.choice(LEGIT_DOMAINS)
|
|
url = f"https://{dom}/api/v1/resource"
|
|
action = "ALLOWED"
|
|
category = random.choice(["Business", "Technology", "Cloud Services", "Productivity"])
|
|
rows.append({
|
|
"Timestamp": ts_str(),
|
|
"Hostname": host["hostname"],
|
|
"SourceIP": host["ip"],
|
|
"Username": f"ACME\\{user}",
|
|
"URL": url,
|
|
"Domain": dom,
|
|
"Action": action,
|
|
"Category": category,
|
|
"Method": random.choice(["GET", "POST", "GET", "GET"]),
|
|
"ResponseCode": random.choice([200, 200, 200, 301, 403, 404]) if action == "ALLOWED" else 403,
|
|
"BytesSent": random.randint(100, 50000),
|
|
"BytesReceived": random.randint(500, 500000),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_proxy_logs()
|
|
write_csv("11_proxy_logs.csv",
|
|
["Timestamp", "Hostname", "SourceIP", "Username", "URL", "Domain",
|
|
"Action", "Category", "Method", "ResponseCode", "BytesSent", "BytesReceived"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# 12. File listing — suspicious downloads (Velociraptor: Windows.Search.FileFinder)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
DOWNLOAD_FILES = [
|
|
("Q1_Budget_2026.xlsx", 245000, "a3f1b2c4d5e6f7a8b9c0d1e2f3a4b5c6"),
|
|
("meeting_notes.docx", 89000, "b4c2d3e5f6a7b8c9d0e1f2a3b4c5d6e7"),
|
|
("vpn_config.ovpn", 1200, "c5d3e4f6a7b8c9d0e1f2a3b4c5d6e7f8"),
|
|
("project_plan.pptx", 1500000, "d6e4f5a7b8c9d0e1f2a3b4c5d6e7f8a9"),
|
|
("setup.exe", 45000000, "e7f5a6b8c9d0e1f2a3b4c5d6e7f8a9b0"),
|
|
("crack_photoshop.exe", 12000000, "f8a6b7c9d0e1f2a3b4c5d6e7f8a9b0c1"), # AUP
|
|
("keygen_v2.exe", 500000, "a9b7c8d0e1f2a3b4c5d6e7f8a9b0c1d2"), # AUP
|
|
("steam_installer.exe", 3500000, "b0c8d9e1f2a3b4c5d6e7f8a9b0c1d2e3"), # AUP
|
|
("free_movie_2026.torrent", 45000, "c1d9e0f2a3b4c5d6e7f8a9b0c1d2e3f4"), # AUP
|
|
("salary_comparison.pdf", 320000, "d2e0f1a3b4c5d6e7f8a9b0c1d2e3f4a5"),
|
|
]
|
|
|
|
def gen_file_listing():
|
|
rows = []
|
|
for host in HOSTS:
|
|
user = rand_user()
|
|
n_files = random.randint(3, 8)
|
|
selected = random.sample(DOWNLOAD_FILES, min(n_files, len(DOWNLOAD_FILES)))
|
|
for fname, size, md5 in selected:
|
|
rows.append({
|
|
"Hostname": host["hostname"],
|
|
"SourceIP": host["ip"],
|
|
"FullPath": f"C:\\Users\\{user}\\Downloads\\{fname}",
|
|
"FileName": fname,
|
|
"Size": size,
|
|
"MD5": md5,
|
|
"SHA256": f"{random.randint(0, 2**256):064x}",
|
|
"MTime": ts_str(),
|
|
"CTime": ts_str(),
|
|
"Username": f"ACME\\{user}",
|
|
"OS": host.get("os", "Windows 10 Enterprise"),
|
|
})
|
|
return rows
|
|
|
|
rows = gen_file_listing()
|
|
write_csv("12_file_listing.csv",
|
|
["Hostname", "SourceIP", "FullPath", "FileName", "Size", "MD5", "SHA256",
|
|
"MTime", "CTime", "Username", "OS"], rows)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# Summary
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
total = sum(len(list((OUT / f).open())) - 1 for f in os.listdir(OUT) if f.endswith(".csv"))
|
|
print(f"\nDone! 12 CSV files in {OUT}")
|
|
print(f"Network: {len(ALL_HOSTS)} hosts, {len(USERS)} users, {len(SUBNETS)} subnets")
|
|
print(f"AUP triggers in: DNS, browser history, proxy logs, autoruns, file listing, process list, sysmon")
|