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
+318
View File
@@ -0,0 +1,318 @@
#!/usr/bin/env python3
"""
Cleanup and teardown engine for C2ingRed deployments
"""
import os
import sys
import subprocess
import logging
import json
import glob
# Add project root to path
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from utils.common import COLORS, PROVIDER_DIRS, confirm_action
def teardown_by_deployment_id(deployment_id):
"""Teardown infrastructure by deployment ID"""
print(f"\n{COLORS['BLUE']}Teardown Infrastructure by ID: {deployment_id}{COLORS['RESET']}")
# Look for deployment logs to determine provider and configuration
log_files = glob.glob(f"logs/deployment_{deployment_id}*.log")
if not log_files:
print(f"{COLORS['RED']}No deployment logs found for ID: {deployment_id}{COLORS['RESET']}")
return False
# Try to find deployment info files
info_files = glob.glob(f"logs/deployment_info_{deployment_id}*.txt")
if info_files:
config = parse_deployment_info(info_files[0])
if config:
return execute_teardown(config)
print(f"{COLORS['YELLOW']}Could not determine deployment configuration from logs{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Manual cleanup may be required{COLORS['RESET']}")
return False
def teardown_all_infrastructure():
"""Teardown all deployed infrastructure"""
print(f"\n{COLORS['RED']}⚠️ WARNING: This will attempt to destroy ALL infrastructure!{COLORS['RESET']}")
if not confirm_action("Are you absolutely sure?", default=False):
return False
# Find all deployment info files
info_files = glob.glob("logs/deployment_info_*.txt")
if not info_files:
print(f"{COLORS['GREEN']}No deployment info files found - nothing to teardown{COLORS['RESET']}")
return True
print(f"Found {len(info_files)} deployments to teardown:")
success_count = 0
for info_file in info_files:
config = parse_deployment_info(info_file)
if config:
deployment_id = config.get('deployment_id', 'unknown')
print(f"\nTearing down deployment: {deployment_id}")
if execute_teardown(config):
success_count += 1
print(f"\n{COLORS['GREEN']}Successfully tore down {success_count}/{len(info_files)} deployments{COLORS['RESET']}")
return success_count == len(info_files)
def parse_deployment_info(info_file):
"""Parse deployment info from file to extract configuration"""
try:
config = {}
with open(info_file, 'r') as f:
lines = f.readlines()
in_config_section = False
for line in lines:
line = line.strip()
if line == "Configuration:":
in_config_section = True
continue
elif line.startswith("-" * 30):
continue
elif line.startswith("Access Information:"):
break
if in_config_section and ": " in line:
key, value = line.split(": ", 1)
# Convert string values back to appropriate types
if value.lower() == 'true':
value = True
elif value.lower() == 'false':
value = False
elif value.lower() == 'none':
value = None
config[key] = value
return config
except Exception as e:
logging.error(f"Failed to parse deployment info from {info_file}: {e}")
return None
def execute_teardown(config):
"""Execute teardown based on configuration"""
provider = config.get('provider')
deployment_id = config.get('deployment_id')
if not provider or not deployment_id:
print(f"{COLORS['RED']}Missing provider or deployment ID{COLORS['RESET']}")
return False
print(f"{COLORS['BLUE']}Executing teardown for {provider} deployment {deployment_id}...{COLORS['RESET']}")
# Set up logging for teardown
from utils.common import setup_logging
setup_logging(deployment_id, "teardown")
try:
# Set provider-specific environment variables
set_provider_environment_for_teardown(config)
# Get correct provider directory
provider_dir = PROVIDER_DIRS.get(provider, provider.capitalize())
# Execute teardown playbook
playbook = f"providers/{provider_dir}/cleanup.yml"
if not os.path.exists(playbook):
print(f"{COLORS['YELLOW']}Teardown playbook not found: {playbook}{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}Manual cleanup required{COLORS['RESET']}")
return manual_cleanup_guidance(config)
success = run_teardown_playbook(playbook, config)
if success:
# Clean up local SSH keys
cleanup_ssh_keys_for_deployment(deployment_id)
# Move deployment logs to cleanup folder
archive_deployment_logs(deployment_id)
print(f"{COLORS['GREEN']}Teardown completed successfully{COLORS['RESET']}")
else:
print(f"{COLORS['RED']}Teardown failed - manual cleanup may be required{COLORS['RESET']}")
return success
except Exception as e:
logging.error(f"Teardown execution failed: {e}")
print(f"{COLORS['RED']}Teardown execution failed: {e}{COLORS['RESET']}")
return False
def set_provider_environment_for_teardown(config):
"""Set provider-specific environment variables for teardown"""
provider = config.get('provider')
if provider == "aws":
if config.get('aws_access_key'):
os.environ['AWS_ACCESS_KEY_ID'] = config['aws_access_key']
if config.get('aws_secret_key'):
os.environ['AWS_SECRET_ACCESS_KEY'] = config['aws_secret_key']
elif provider == "linode":
# Try to get token from config first, then from vars file
if config.get('linode_token'):
os.environ['LINODE_TOKEN'] = config['linode_token']
else:
# Load token from provider vars file
try:
from utils.common import load_vars_file, PROVIDER_DIRS
vars_data = load_vars_file(provider)
if vars_data and vars_data.get('linode_token'):
os.environ['LINODE_TOKEN'] = vars_data['linode_token']
config['linode_token'] = vars_data['linode_token'] # Add to config for later use
else:
logging.warning("Linode token not found in vars file - cleanup may fail")
except Exception as e:
logging.warning(f"Failed to load Linode token from vars file: {e}")
def run_teardown_playbook(playbook, config):
"""Run the teardown Ansible playbook"""
try:
cmd = [
'ansible-playbook',
playbook,
'--extra-vars', create_teardown_vars(config)
]
if config.get('debug'):
cmd.append('-vvv')
logging.info(f"Executing teardown: {' '.join(cmd)}")
result = subprocess.run(
cmd,
capture_output=True,
text=True,
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
if result.returncode == 0:
logging.info("Teardown playbook executed successfully")
return True
else:
logging.error(f"Teardown playbook failed with return code {result.returncode}")
logging.error(f"STDOUT: {result.stdout}")
logging.error(f"STDERR: {result.stderr}")
return False
except Exception as e:
logging.error(f"Error executing teardown playbook: {e}")
return False
def create_teardown_vars(config):
"""Create Ansible extra vars for teardown"""
teardown_vars = {
'deployment_id': config.get('deployment_id'),
'provider': config.get('provider'),
'operation': 'teardown'
}
# Add instance names for proper cleanup
if config.get('redirector_name'):
teardown_vars['redirector_name'] = config['redirector_name']
if config.get('c2_name'):
teardown_vars['c2_name'] = config['c2_name']
if config.get('tracker_name'):
teardown_vars['tracker_name'] = config['tracker_name']
if config.get('attack_box_name'):
teardown_vars['attack_box_name'] = config['attack_box_name']
# Add provider-specific vars if present
if config.get('aws_access_key'):
teardown_vars['aws_access_key'] = config['aws_access_key']
if config.get('aws_secret_key'):
teardown_vars['aws_secret_key'] = config['aws_secret_key']
if config.get('aws_region'):
teardown_vars['aws_region'] = config['aws_region']
if config.get('linode_token'):
teardown_vars['linode_token'] = config['linode_token']
if config.get('linode_region'):
teardown_vars['linode_region'] = config['linode_region']
return json.dumps(teardown_vars)
def cleanup_ssh_keys_for_deployment(deployment_id):
"""Clean up SSH keys for a specific deployment"""
ssh_dir = os.path.expanduser("~/.ssh")
key_pattern = f"c2deploy_{deployment_id}*"
for key_file in glob.glob(os.path.join(ssh_dir, key_pattern)):
try:
os.remove(key_file)
logging.info(f"Removed SSH key: {key_file}")
except Exception as e:
logging.warning(f"Failed to remove SSH key {key_file}: {e}")
def archive_deployment_logs(deployment_id):
"""Archive deployment logs after successful teardown"""
try:
# Create archive directory
archive_dir = "logs/archive"
os.makedirs(archive_dir, exist_ok=True)
# Move all files related to this deployment
log_patterns = [
f"logs/deployment_{deployment_id}*",
f"logs/deployment_info_{deployment_id}*",
f"logs/teardown_{deployment_id}*"
]
for pattern in log_patterns:
for file_path in glob.glob(pattern):
archive_path = os.path.join(archive_dir, os.path.basename(file_path))
os.rename(file_path, archive_path)
logging.info(f"Archived: {file_path} -> {archive_path}")
except Exception as e:
logging.warning(f"Failed to archive logs for {deployment_id}: {e}")
def manual_cleanup_guidance(config):
"""Provide manual cleanup guidance when automated teardown isn't available"""
provider = config.get('provider')
deployment_id = config.get('deployment_id')
print(f"\n{COLORS['YELLOW']}Manual Cleanup Required{COLORS['RESET']}")
print(f"{COLORS['YELLOW']}========================{COLORS['RESET']}")
print(f"Deployment ID: {deployment_id}")
print(f"Provider: {provider}")
if provider == "aws":
print(f"\nAWS Resources to check:")
print(f"- EC2 instances with tags containing: {deployment_id}")
print(f"- Security groups with names containing: {deployment_id}")
print(f"- Key pairs with names containing: {deployment_id}")
print(f"- EIPs associated with the deployment")
elif provider == "linode":
print(f"\nLinode Resources to check:")
print(f"- Linodes with labels containing: {deployment_id}")
print(f"- NodeBalancers with labels containing: {deployment_id}")
print(f"- Firewalls with labels containing: {deployment_id}")
elif provider == "flokinet":
print(f"\nFlokiNET Resources to check:")
print(f"- Check your FlokiNET control panel for resources created")
print(f"- Look for servers with the deployment ID: {deployment_id}")
print(f"\nLocal cleanup:")
print(f"- SSH keys: ~/.ssh/c2deploy_{deployment_id}*")
print(f"- Log files: logs/*{deployment_id}*")
return False # Manual cleanup required
if __name__ == "__main__":
print("Cleanup engine loaded")