working on bugs
This commit is contained in:
@@ -107,7 +107,7 @@ After deployment:
|
|||||||
|
|
||||||
## Using Havoc C2
|
## Using Havoc C2
|
||||||
|
|
||||||
Havoc C2 Framework is installed at `/root/Tools/havoc` on the C2 server.
|
Havoc C2 Framework is installed at `/root/Tools/Havoc` on the C2 server.
|
||||||
|
|
||||||
### Connecting to the Teamserver
|
### Connecting to the Teamserver
|
||||||
|
|
||||||
@@ -126,11 +126,11 @@ From your local machine:
|
|||||||
```bash
|
```bash
|
||||||
./havoc client --address YOUR_C2_IP:40056 --username admin --password [password]
|
./havoc client --address YOUR_C2_IP:40056 --username admin --password [password]
|
||||||
```
|
```
|
||||||
(The password is stored in `/root/Tools/havoc/data/profiles/default.yaotl`)
|
(The password is stored in `/root/Tools/Havoc/data/profiles/default.yaotl`)
|
||||||
|
|
||||||
### Generating Payloads
|
### Generating Payloads
|
||||||
|
|
||||||
Pre-generated payloads are available in `/root/Tools/havoc/payloads/`. You can create new payloads using:
|
Pre-generated payloads are available in `/root/Tools/Havoc/payloads/`. You can create new payloads using:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /root/Tools
|
cd /root/Tools
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ def parse_arguments():
|
|||||||
parser.add_argument('--c2-name', help='Name for the C2 instance (default: random)')
|
parser.add_argument('--c2-name', help='Name for the C2 instance (default: random)')
|
||||||
parser.add_argument('--redirector-subdomain', default='cdn', help='Subdomain for the redirector (default: cdn)')
|
parser.add_argument('--redirector-subdomain', default='cdn', help='Subdomain for the redirector (default: cdn)')
|
||||||
parser.add_argument('--c2-subdomain', default='mail', help='Subdomain for the C2 server (default: mail)')
|
parser.add_argument('--c2-subdomain', default='mail', help='Subdomain for the C2 server (default: mail)')
|
||||||
|
parser.add_argument('--redirector-provider', choices=PROVIDERS, help='Provider to use for redirector (if different from primary provider)')
|
||||||
|
parser.add_argument('--c2-provider', choices=PROVIDERS, help='Provider to use for C2 (if different from primary provider)')
|
||||||
|
parser.add_argument('--redirector-region', help='Region for redirector deployment (can be different from C2)')
|
||||||
|
parser.add_argument('--c2-region', help='Region for C2 deployment (can be different from redirector)')
|
||||||
|
|
||||||
# Common arguments
|
# Common arguments
|
||||||
parser.add_argument('--teardown', action='store_true', help='Tear down existing infrastructure')
|
parser.add_argument('--teardown', action='store_true', help='Tear down existing infrastructure')
|
||||||
@@ -143,21 +147,21 @@ def interactive_setup(deployment_id=None):
|
|||||||
config = {}
|
config = {}
|
||||||
|
|
||||||
print("\n========================================")
|
print("\n========================================")
|
||||||
print("C2itAll - Interactive Setup Wizard")
|
print("C2ingRed - Interactive Setup Wizard")
|
||||||
print("========================================\n")
|
print("========================================\n")
|
||||||
|
|
||||||
# Create a deployment ID for consistent resource naming
|
# Create a deployment ID for consistent resource naming
|
||||||
deployment_id = deployment_id or generate_deployment_id()
|
deployment_id = deployment_id or generate_deployment_id()
|
||||||
config['deployment_id'] = deployment_id
|
config['deployment_id'] = deployment_id
|
||||||
|
|
||||||
# Select provider
|
# Select primary provider
|
||||||
print("Available cloud providers:")
|
print("Available cloud providers:")
|
||||||
for i, provider in enumerate(PROVIDERS, 1):
|
for i, provider in enumerate(PROVIDERS, 1):
|
||||||
print(f" {i}. {provider.capitalize()}")
|
print(f" {i}. {provider.capitalize()}")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
provider_choice = int(input("\nSelect a provider (1-3): "))
|
provider_choice = int(input("\nSelect a primary provider (1-3): "))
|
||||||
if 1 <= provider_choice <= len(PROVIDERS):
|
if 1 <= provider_choice <= len(PROVIDERS):
|
||||||
config['provider'] = PROVIDERS[provider_choice - 1]
|
config['provider'] = PROVIDERS[provider_choice - 1]
|
||||||
break
|
break
|
||||||
@@ -166,118 +170,200 @@ def interactive_setup(deployment_id=None):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
print("Please enter a valid number")
|
print("Please enter a valid number")
|
||||||
|
|
||||||
# Load vars file for the selected provider
|
# Ask if user wants to use cross-provider deployment
|
||||||
provider_dir = config['provider'].capitalize()
|
cross_provider = input("\nDo you want to deploy redirector and C2 on different providers? (y/n) [default: n]: ").lower() == 'y'
|
||||||
if config['provider'] == "aws":
|
|
||||||
provider_dir = "AWS"
|
if cross_provider:
|
||||||
elif config['provider'] == "flokinet":
|
print("\nSelect redirector provider:")
|
||||||
provider_dir = "FlokiNET"
|
for i, provider in enumerate(PROVIDERS, 1):
|
||||||
|
print(f" {i}. {provider.capitalize()}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
redirector_provider_choice = int(input("\nSelect redirector provider (1-3): "))
|
||||||
|
if 1 <= redirector_provider_choice <= len(PROVIDERS):
|
||||||
|
config['redirector_provider'] = PROVIDERS[redirector_provider_choice - 1]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"Please enter a number between 1 and {len(PROVIDERS)}")
|
||||||
|
except ValueError:
|
||||||
|
print("Please enter a valid number")
|
||||||
|
|
||||||
|
print("\nSelect C2 server provider:")
|
||||||
|
for i, provider in enumerate(PROVIDERS, 1):
|
||||||
|
print(f" {i}. {provider.capitalize()}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
c2_provider_choice = int(input("\nSelect C2 provider (1-3): "))
|
||||||
|
if 1 <= c2_provider_choice <= len(PROVIDERS):
|
||||||
|
config['c2_provider'] = PROVIDERS[c2_provider_choice - 1]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"Please enter a number between 1 and {len(PROVIDERS)}")
|
||||||
|
except ValueError:
|
||||||
|
print("Please enter a valid number")
|
||||||
|
|
||||||
|
# Load vars files for all selected providers
|
||||||
|
providers_to_configure = set([config['provider']])
|
||||||
|
if cross_provider:
|
||||||
|
providers_to_configure.add(config['redirector_provider'])
|
||||||
|
providers_to_configure.add(config['c2_provider'])
|
||||||
|
|
||||||
vars_file = f"{provider_dir}/vars.yaml"
|
|
||||||
vars_data = {}
|
vars_data = {}
|
||||||
|
for provider in providers_to_configure:
|
||||||
|
provider_dir = PROVIDER_DIRS.get(provider, provider.capitalize())
|
||||||
|
vars_file = f"{provider_dir}/vars.yaml"
|
||||||
|
|
||||||
if os.path.exists(vars_file):
|
if os.path.exists(vars_file):
|
||||||
try:
|
try:
|
||||||
with open(vars_file, 'r') as f:
|
with open(vars_file, 'r') as f:
|
||||||
vars_data = yaml.safe_load(f) or {}
|
provider_vars = yaml.safe_load(f) or {}
|
||||||
|
vars_data[provider] = provider_vars
|
||||||
print(f"Loaded configuration from {vars_file}")
|
print(f"Loaded configuration from {vars_file}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Failed to load {vars_file}: {e}")
|
print(f"Warning: Failed to load {vars_file}: {e}")
|
||||||
|
vars_data[provider] = {}
|
||||||
|
|
||||||
# Provider-specific configuration
|
# Configure each provider
|
||||||
if config['provider'] == "aws":
|
for provider in providers_to_configure:
|
||||||
# Set defaults from vars file
|
provider_vars = vars_data.get(provider, {})
|
||||||
default_aws_key = vars_data.get('aws_access_key', '')
|
|
||||||
default_aws_secret = vars_data.get('aws_secret_key', '')
|
|
||||||
|
|
||||||
config['aws_access_key'] = input(f"\nAWS Access Key [{'*****' if default_aws_key else 'leave blank to use AWS CLI profile'}]: ") or default_aws_key
|
print(f"\n--- {provider.capitalize()} Configuration ---")
|
||||||
config['aws_secret_key'] = input(f"AWS Secret Key [{'*****' if default_aws_secret else 'leave blank to use AWS CLI profile'}]: ") or default_aws_secret
|
|
||||||
|
|
||||||
# Show available regions
|
if provider == "aws":
|
||||||
aws_regions = vars_data.get('aws_region_choices', [])
|
# AWS credentials
|
||||||
|
default_aws_key = provider_vars.get('aws_access_key', '')
|
||||||
|
default_aws_secret = provider_vars.get('aws_secret_key', '')
|
||||||
|
|
||||||
|
aws_key = input(f"AWS 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
|
||||||
|
|
||||||
|
config['aws_access_key'] = aws_key
|
||||||
|
config['aws_secret_key'] = aws_secret
|
||||||
|
|
||||||
|
# AWS regions
|
||||||
|
aws_regions = provider_vars.get('aws_region_choices', [])
|
||||||
if aws_regions:
|
if aws_regions:
|
||||||
print("\nAvailable AWS regions:")
|
print("\nAvailable AWS regions:")
|
||||||
for i, region in enumerate(aws_regions, 1):
|
for i, region in enumerate(aws_regions, 1):
|
||||||
print(f" {i}. {region}")
|
print(f" {i}. {region}")
|
||||||
|
|
||||||
while True:
|
# Select region for redirector if this is the redirector provider
|
||||||
region_input = input("\nSelect a region (number) or press Enter for random: ")
|
if provider == config.get('redirector_provider', config['provider']):
|
||||||
if not region_input:
|
region_input = input("\nSelect region for redirector (number or leave blank for random): ")
|
||||||
config['aws_region'] = None # Random
|
if region_input:
|
||||||
break
|
try:
|
||||||
|
region_choice = int(region_input)
|
||||||
|
if 1 <= region_choice <= len(aws_regions):
|
||||||
|
config['redirector_region'] = aws_regions[region_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"Invalid choice, using random region")
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region")
|
||||||
|
|
||||||
|
# Select region for C2 if this is the C2 provider
|
||||||
|
if provider == config.get('c2_provider', config['provider']):
|
||||||
|
region_input = input("\nSelect region for C2 (number or leave blank for random): ")
|
||||||
|
if region_input:
|
||||||
|
try:
|
||||||
|
region_choice = int(region_input)
|
||||||
|
if 1 <= region_choice <= len(aws_regions):
|
||||||
|
config['c2_region'] = aws_regions[region_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"Invalid choice, using random region")
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region")
|
||||||
|
|
||||||
|
# If not cross-provider and no specific regions selected, use general region
|
||||||
|
if not cross_provider and not (config.get('redirector_region') or config.get('c2_region')):
|
||||||
|
region_input = input("\nSelect region (number or leave blank for random): ")
|
||||||
|
if region_input:
|
||||||
try:
|
try:
|
||||||
region_choice = int(region_input)
|
region_choice = int(region_input)
|
||||||
if 1 <= region_choice <= len(aws_regions):
|
if 1 <= region_choice <= len(aws_regions):
|
||||||
config['aws_region'] = aws_regions[region_choice - 1]
|
config['aws_region'] = aws_regions[region_choice - 1]
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
print(f"Please enter a number between 1 and {len(aws_regions)}")
|
print(f"Invalid choice, using random region")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("Please enter a valid number or press Enter")
|
print("Invalid input, using random region")
|
||||||
else:
|
|
||||||
default_region = vars_data.get('aws_region', 'us-east-1')
|
|
||||||
config['aws_region'] = input(f"\nAWS Region [default: {default_region}]: ") or default_region
|
|
||||||
|
|
||||||
# Instance size
|
elif provider == "linode":
|
||||||
default_size = vars_data.get('aws_instance_type', 't2.medium')
|
# Linode token
|
||||||
config['aws_instance_type'] = input(f"Instance Size [default: {default_size}]: ") or default_size
|
default_token = provider_vars.get('linode_token', '')
|
||||||
|
token = input(f"\nLinode API Token [{'*****' if default_token else 'required'}]: ") or default_token
|
||||||
|
config['linode_token'] = token
|
||||||
|
|
||||||
elif config['provider'] == "linode":
|
# Linode regions
|
||||||
# Set defaults from vars file
|
linode_regions = provider_vars.get('region_choices', [])
|
||||||
default_token = vars_data.get('linode_token', '')
|
|
||||||
|
|
||||||
config['linode_token'] = input(f"\nLinode API Token [{'*****' if default_token else 'required'}]: ") or default_token
|
|
||||||
|
|
||||||
# Show available regions
|
|
||||||
linode_regions = vars_data.get('region_choices', [])
|
|
||||||
if linode_regions:
|
if linode_regions:
|
||||||
print("\nAvailable Linode regions:")
|
print("\nAvailable Linode regions:")
|
||||||
for i, region in enumerate(linode_regions, 1):
|
for i, region in enumerate(linode_regions, 1):
|
||||||
print(f" {i}. {region}")
|
print(f" {i}. {region}")
|
||||||
|
|
||||||
while True:
|
# Select region for redirector if this is the redirector provider
|
||||||
region_input = input("\nSelect a region (number) or press Enter for random: ")
|
if provider == config.get('redirector_provider', config['provider']):
|
||||||
if not region_input:
|
region_input = input("\nSelect region for redirector (number or leave blank for random): ")
|
||||||
config['linode_region'] = None # Random
|
if region_input:
|
||||||
break
|
try:
|
||||||
|
region_choice = int(region_input)
|
||||||
|
if 1 <= region_choice <= len(linode_regions):
|
||||||
|
config['redirector_region'] = linode_regions[region_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"Invalid choice, using random region")
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region")
|
||||||
|
|
||||||
|
# Select region for C2 if this is the C2 provider
|
||||||
|
if provider == config.get('c2_provider', config['provider']):
|
||||||
|
region_input = input("\nSelect region for C2 (number or leave blank for random): ")
|
||||||
|
if region_input:
|
||||||
|
try:
|
||||||
|
region_choice = int(region_input)
|
||||||
|
if 1 <= region_choice <= len(linode_regions):
|
||||||
|
config['c2_region'] = linode_regions[region_choice - 1]
|
||||||
|
else:
|
||||||
|
print(f"Invalid choice, using random region")
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region")
|
||||||
|
|
||||||
|
# If not cross-provider and no specific regions selected, use general region
|
||||||
|
if not cross_provider and not (config.get('redirector_region') or config.get('c2_region')):
|
||||||
|
region_input = input("\nSelect region (number or leave blank for random): ")
|
||||||
|
if region_input:
|
||||||
try:
|
try:
|
||||||
region_choice = int(region_input)
|
region_choice = int(region_input)
|
||||||
if 1 <= region_choice <= len(linode_regions):
|
if 1 <= region_choice <= len(linode_regions):
|
||||||
config['linode_region'] = linode_regions[region_choice - 1]
|
config['linode_region'] = linode_regions[region_choice - 1]
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
print(f"Please enter a number between 1 and {len(linode_regions)}")
|
print(f"Invalid choice, using random region")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("Please enter a valid number or press Enter")
|
print("Invalid input, using random region")
|
||||||
else:
|
|
||||||
# Use defaults or empty values if no regions are defined
|
|
||||||
default_region = vars_data.get('linode_region', '')
|
|
||||||
config['linode_region'] = input(f"\nLinode Region [default: {default_region or 'random'}]: ") or default_region
|
|
||||||
|
|
||||||
# Instance size/plan
|
# Instance size/plan
|
||||||
default_plan = vars_data.get('plan', 'g6-standard-2')
|
default_plan = provider_vars.get('plan', 'g6-standard-2')
|
||||||
config['plan'] = input(f"Instance Plan [default: {default_plan}]: ") or default_plan
|
plan = input(f"\nInstance Plan [default: {default_plan}]: ") or default_plan
|
||||||
|
config['plan'] = plan
|
||||||
|
|
||||||
elif config['provider'] == "flokinet":
|
elif provider == "flokinet":
|
||||||
print("\nFlokiNET requires pre-provisioned servers.")
|
print("\nFlokiNET requires pre-provisioned servers.")
|
||||||
|
|
||||||
# Set defaults from vars file
|
# Set defaults from vars file
|
||||||
default_redirector_ip = vars_data.get('redirector_ip', '')
|
default_redirector_ip = provider_vars.get('redirector_ip', '')
|
||||||
default_c2_ip = vars_data.get('c2_ip', '')
|
default_c2_ip = provider_vars.get('c2_ip', '')
|
||||||
default_ssh_user = vars_data.get('ssh_user', DEFAULT_SSH_USER['flokinet'])
|
default_ssh_user = provider_vars.get('ssh_user', DEFAULT_SSH_USER['flokinet'])
|
||||||
default_ssh_port = vars_data.get('ssh_port', 22)
|
default_ssh_port = provider_vars.get('ssh_port', 22)
|
||||||
|
|
||||||
|
# Configure FlokiNET servers
|
||||||
|
if provider == config.get('redirector_provider', config['provider']):
|
||||||
config['flokinet_redirector_ip'] = input(f"FlokiNET Redirector IP Address [default: {default_redirector_ip}]: ") or default_redirector_ip
|
config['flokinet_redirector_ip'] = input(f"FlokiNET Redirector IP Address [default: {default_redirector_ip}]: ") or default_redirector_ip
|
||||||
|
|
||||||
|
if provider == config.get('c2_provider', config['provider']):
|
||||||
config['flokinet_c2_ip'] = input(f"FlokiNET C2 Server IP Address [default: {default_c2_ip}]: ") or default_c2_ip
|
config['flokinet_c2_ip'] = input(f"FlokiNET C2 Server IP Address [default: {default_c2_ip}]: ") or default_c2_ip
|
||||||
|
|
||||||
config['ssh_user'] = input(f"SSH User [default: {default_ssh_user}]: ") or default_ssh_user
|
config['ssh_user'] = input(f"SSH User [default: {default_ssh_user}]: ") or default_ssh_user
|
||||||
config['ssh_port'] = input(f"SSH Port [default: {default_ssh_port}]: ") or default_ssh_port
|
config['ssh_port'] = input(f"SSH Port [default: {default_ssh_port}]: ") or default_ssh_port
|
||||||
|
|
||||||
ssh_key = input("Path to SSH Private Key [leave blank to generate new key]: ")
|
|
||||||
if ssh_key:
|
|
||||||
config['ssh_key'] = os.path.expanduser(ssh_key)
|
|
||||||
else:
|
|
||||||
config['ssh_key'] = None # Will generate a new key
|
|
||||||
|
|
||||||
# Deployment type with integrated tracker option
|
# Deployment type with integrated tracker option
|
||||||
print("\nDeployment type:")
|
print("\nDeployment type:")
|
||||||
print(" 1. Full deployment (Redirector + C2) [default]")
|
print(" 1. Full deployment (Redirector + C2) [default]")
|
||||||
@@ -317,28 +403,25 @@ def interactive_setup(deployment_id=None):
|
|||||||
config['deploy_tracker'] = False
|
config['deploy_tracker'] = False
|
||||||
|
|
||||||
# Domain configuration
|
# Domain configuration
|
||||||
default_domain = vars_data.get('domain', 'example.com')
|
default_domain = vars_data.get(config['provider'], {}).get('domain', 'example.com')
|
||||||
config['domain'] = input(f"\nDomain name [default: {default_domain}]: ") or default_domain
|
config['domain'] = input(f"\nDomain name [default: {default_domain}]: ") or default_domain
|
||||||
|
|
||||||
# Subdomain configuration - new feature
|
# Subdomain configuration
|
||||||
default_redirector_subdomain = vars_data.get('redirector_subdomain', 'cdn')
|
default_redirector_subdomain = vars_data.get(config['provider'], {}).get('redirector_subdomain', 'cdn')
|
||||||
config['redirector_subdomain'] = input(f"Redirector subdomain [default: {default_redirector_subdomain}]: ") or default_redirector_subdomain
|
config['redirector_subdomain'] = input(f"Redirector subdomain [default: {default_redirector_subdomain}]: ") or default_redirector_subdomain
|
||||||
|
|
||||||
default_c2_subdomain = vars_data.get('c2_subdomain', 'mail')
|
default_c2_subdomain = vars_data.get(config['provider'], {}).get('c2_subdomain', 'mail')
|
||||||
config['c2_subdomain'] = input(f"C2 server subdomain [default: {default_c2_subdomain}]: ") or default_c2_subdomain
|
config['c2_subdomain'] = input(f"C2 server subdomain [default: {default_c2_subdomain}]: ") or default_c2_subdomain
|
||||||
|
|
||||||
# Always use the most up-to-date domain for the email default
|
# Email for Let's Encrypt
|
||||||
default_email = vars_data.get('letsencrypt_email')
|
|
||||||
if not default_email or "example.com" in default_email:
|
|
||||||
default_email = f"admin@{config['domain']}"
|
default_email = f"admin@{config['domain']}"
|
||||||
|
|
||||||
config['letsencrypt_email'] = input(f"Email for Let's Encrypt [default: {default_email}]: ") or default_email
|
config['letsencrypt_email'] = input(f"Email for Let's Encrypt [default: {default_email}]: ") or default_email
|
||||||
|
|
||||||
# Security options
|
# Security options
|
||||||
print("\nSecurity options:")
|
print("\nSecurity options:")
|
||||||
default_disable_history = vars_data.get('disable_history', True)
|
default_disable_history = vars_data.get(config['provider'], {}).get('disable_history', True)
|
||||||
default_secure_memory = vars_data.get('secure_memory', True)
|
default_secure_memory = vars_data.get(config['provider'], {}).get('secure_memory', True)
|
||||||
default_zero_logs = vars_data.get('zero_logs', True)
|
default_zero_logs = vars_data.get(config['provider'], {}).get('zero_logs', True)
|
||||||
|
|
||||||
disable_history = input(f"Disable command history? (y/n) [default: {'y' if default_disable_history else 'n'}]: ").lower()
|
disable_history = input(f"Disable command history? (y/n) [default: {'y' if default_disable_history else 'n'}]: ").lower()
|
||||||
secure_memory = input(f"Enable secure memory settings? (y/n) [default: {'y' if default_secure_memory else 'n'}]: ").lower()
|
secure_memory = input(f"Enable secure memory settings? (y/n) [default: {'y' if default_secure_memory else 'n'}]: ").lower()
|
||||||
@@ -348,6 +431,48 @@ def interactive_setup(deployment_id=None):
|
|||||||
config['secure_memory'] = True if (secure_memory == 'y' or (default_secure_memory and secure_memory != 'n')) else False
|
config['secure_memory'] = True if (secure_memory == 'y' or (default_secure_memory and secure_memory != 'n')) else False
|
||||||
config['zero_logs'] = True if (zero_logs == 'y' or (default_zero_logs and zero_logs != 'n')) else False
|
config['zero_logs'] = True if (zero_logs == 'y' or (default_zero_logs and zero_logs != 'n')) else False
|
||||||
|
|
||||||
|
# Add multi-region option
|
||||||
|
if not cross_provider and not config.get('c2_only') and not config.get('redirector_only'):
|
||||||
|
multi_region = input("\nDo you want to deploy redirector and C2 in different regions? (y/n) [default: n]: ").lower() == 'y'
|
||||||
|
|
||||||
|
if multi_region:
|
||||||
|
provider_dir = PROVIDER_DIRS.get(config['provider'], config['provider'].capitalize())
|
||||||
|
vars_file = f"{provider_dir}/vars.yaml"
|
||||||
|
|
||||||
|
if os.path.exists(vars_file):
|
||||||
|
with open(vars_file, 'r') as f:
|
||||||
|
vars_data = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
|
if config['provider'] == 'aws':
|
||||||
|
regions = vars_data.get('aws_region_choices', [])
|
||||||
|
elif config['provider'] == 'linode':
|
||||||
|
regions = vars_data.get('region_choices', [])
|
||||||
|
else:
|
||||||
|
regions = []
|
||||||
|
|
||||||
|
if regions:
|
||||||
|
print("\nAvailable regions:")
|
||||||
|
for i, region in enumerate(regions, 1):
|
||||||
|
print(f" {i}. {region}")
|
||||||
|
|
||||||
|
redirector_region_input = input("\nSelect redirector region (number): ")
|
||||||
|
try:
|
||||||
|
redirector_region_choice = int(redirector_region_input)
|
||||||
|
if 1 <= redirector_region_choice <= len(regions):
|
||||||
|
config['redirector_region'] = regions[redirector_region_choice - 1]
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region for redirector")
|
||||||
|
|
||||||
|
c2_region_input = input("Select C2 region (number): ")
|
||||||
|
try:
|
||||||
|
c2_region_choice = int(c2_region_input)
|
||||||
|
if 1 <= c2_region_choice <= len(regions):
|
||||||
|
config['c2_region'] = regions[c2_region_choice - 1]
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input, using random region for C2")
|
||||||
|
else:
|
||||||
|
print("No regions found in vars file, skipping multi-region setup")
|
||||||
|
|
||||||
# Tracker configuration if enabled
|
# Tracker configuration if enabled
|
||||||
if config['deploy_tracker']:
|
if config['deploy_tracker']:
|
||||||
default_tracker_domain = f"track.{config['domain']}"
|
default_tracker_domain = f"track.{config['domain']}"
|
||||||
@@ -382,11 +507,12 @@ def interactive_setup(deployment_id=None):
|
|||||||
if config.get('deploy_tracker'):
|
if config.get('deploy_tracker'):
|
||||||
config['tracker_name'] = f"t-{deployment_id}"
|
config['tracker_name'] = f"t-{deployment_id}"
|
||||||
|
|
||||||
# Additional settings from vars_data
|
# Additional settings
|
||||||
config['gophish_admin_port'] = vars_data.get('gophish_admin_port', str(random.randint(2000, 9000)))
|
default_provider_vars = vars_data.get(config['provider'], {})
|
||||||
config['smtp_auth_user'] = vars_data.get('smtp_auth_user', f"user{random.randint(1000, 9999)}")
|
config['gophish_admin_port'] = default_provider_vars.get('gophish_admin_port', str(random.randint(2000, 9000)))
|
||||||
config['smtp_auth_pass'] = vars_data.get('smtp_auth_pass', ''.join(random.choices(string.ascii_letters + string.digits, k=20)))
|
config['smtp_auth_user'] = default_provider_vars.get('smtp_auth_user', f"user{random.randint(1000, 9999)}")
|
||||||
config['shell_handler_port'] = vars_data.get('shell_handler_port', str(random.randint(4000, 65000)))
|
config['smtp_auth_pass'] = default_provider_vars.get('smtp_auth_pass', ''.join(random.choices(string.ascii_letters + string.digits, k=20)))
|
||||||
|
config['shell_handler_port'] = default_provider_vars.get('shell_handler_port', str(random.randint(4000, 65000)))
|
||||||
|
|
||||||
# Set up integrated tracker flag for deployment
|
# Set up integrated tracker flag for deployment
|
||||||
if config.get('deploy_tracker') and config.get('integrated_tracker'):
|
if config.get('deploy_tracker') and config.get('integrated_tracker'):
|
||||||
@@ -657,137 +783,87 @@ def deploy_infrastructure(config):
|
|||||||
if config.get('linode_token'):
|
if config.get('linode_token'):
|
||||||
os.environ['LINODE_TOKEN'] = config['linode_token']
|
os.environ['LINODE_TOKEN'] = config['linode_token']
|
||||||
|
|
||||||
# IMPORTANT: Ensure ssh_user is explicitly set to avoid template recursion
|
# Set correct ssh_user based on provider
|
||||||
if provider == "linode":
|
if not config.get('ssh_user'):
|
||||||
config['ssh_user'] = "root"
|
config['ssh_user'] = DEFAULT_SSH_USER.get(provider, 'root')
|
||||||
elif provider == "aws":
|
|
||||||
config['ssh_user'] = "kali"
|
|
||||||
|
|
||||||
# Set tracker deployment flag for C2 configuration
|
# Handle cross-provider deployment
|
||||||
if config.get('deploy_tracker') and config.get('integrated_tracker'):
|
redirector_provider = config.get('redirector_provider', provider)
|
||||||
config['setup_integrated_tracker'] = True
|
c2_provider = config.get('c2_provider', provider)
|
||||||
logging.info("Integrated tracker will be deployed on C2 server")
|
|
||||||
|
|
||||||
# Select random region if not specified
|
is_cross_provider = (redirector_provider != c2_provider) or \
|
||||||
if not config.get('region'):
|
(config.get('redirector_region') and config.get('c2_region') and \
|
||||||
if provider == "linode" and config.get('linode_region'):
|
config.get('redirector_region') != config.get('c2_region'))
|
||||||
# Use the specified linode_region
|
|
||||||
config['region'] = config['linode_region']
|
|
||||||
elif provider == "aws" and config.get('aws_region'):
|
|
||||||
# Use the specified aws_region
|
|
||||||
config['region'] = config['aws_region']
|
|
||||||
else:
|
|
||||||
# If no specific region provided, select random
|
|
||||||
config['region'] = select_random_region(config)
|
|
||||||
|
|
||||||
# Set different instance sizes for redirector vs. C2
|
if is_cross_provider and not (config.get('redirector_only') or config.get('c2_only')):
|
||||||
if provider == "linode":
|
return deploy_cross_provider(config, redirector_provider, c2_provider)
|
||||||
# Store the original plan
|
|
||||||
original_plan = config.get('plan', 'g6-standard-2')
|
|
||||||
|
|
||||||
# For redirector, use smaller instance size regardless of what was specified
|
# For FlokiNET, validate required IPs
|
||||||
|
if provider == "flokinet":
|
||||||
|
if not config.get('c2_only') and not config.get('flokinet_redirector_ip') and not config.get('redirector_ip'):
|
||||||
|
logging.error("FlokiNET redirector IP is required")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not config.get('redirector_only') and not config.get('flokinet_c2_ip') and not config.get('c2_ip'):
|
||||||
|
logging.error("FlokiNET C2 IP is required")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get correct provider directory
|
||||||
|
provider_dir = PROVIDER_DIRS.get(provider, provider.capitalize())
|
||||||
|
|
||||||
|
# Deploy redirector if needed
|
||||||
if not config.get('c2_only'):
|
if not config.get('c2_only'):
|
||||||
redirector_config = config.copy()
|
redirector_config = config.copy()
|
||||||
redirector_config['plan'] = 'g6-nanode-1' # Smallest viable Linode plan
|
if config.get('redirector_region'):
|
||||||
|
redirector_config['region'] = config['redirector_region']
|
||||||
|
|
||||||
# Use the correct case for provider directory
|
|
||||||
provider_dir = PROVIDER_DIRS[provider]
|
|
||||||
playbook = f"{provider_dir}/redirector.yml"
|
playbook = f"{provider_dir}/redirector.yml"
|
||||||
inventory_path = create_inventory_file(redirector_config, "local")
|
inventory_path = create_inventory_file(redirector_config, "local")
|
||||||
|
|
||||||
logging.info(f"Deploying redirector using {playbook} with plan: g6-nanode-1")
|
logging.info(f"Deploying {provider} redirector using {playbook} in region {redirector_config.get('region', 'default')}")
|
||||||
redirector_success, stdout, stderr = run_ansible_playbook(
|
redirector_success, stdout, stderr = run_ansible_playbook(
|
||||||
playbook, inventory_path, redirector_config, redirector_config.get('debug', False)
|
playbook, inventory_path, redirector_config, redirector_config.get('debug', False)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log Ansible output
|
|
||||||
if redirector_config.get('debug', False):
|
|
||||||
logging.debug(f"Ansible stdout: {stdout}")
|
|
||||||
if stderr:
|
|
||||||
logging.debug(f"Ansible stderr: {stderr}")
|
|
||||||
|
|
||||||
if os.path.exists(inventory_path):
|
if os.path.exists(inventory_path):
|
||||||
os.unlink(inventory_path)
|
os.unlink(inventory_path)
|
||||||
|
|
||||||
if not redirector_success:
|
if not redirector_success:
|
||||||
logging.error("Redirector deployment failed")
|
logging.error(f"{provider} redirector deployment failed")
|
||||||
|
if redirector_config.get('debug'):
|
||||||
|
logging.error(f"Ansible stderr: {stderr}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# For C2, use the original plan
|
# Extract and save redirector IP for C2 configuration
|
||||||
|
if 'redirector_ip' in redirector_config:
|
||||||
|
config['redirector_ip'] = redirector_config['redirector_ip']
|
||||||
|
|
||||||
|
# Deploy C2 if needed
|
||||||
if not config.get('redirector_only'):
|
if not config.get('redirector_only'):
|
||||||
# Restore original plan for C2
|
c2_config = config.copy()
|
||||||
config['plan'] = original_plan
|
if config.get('c2_region'):
|
||||||
|
c2_config['region'] = config['c2_region']
|
||||||
|
|
||||||
provider_dir = PROVIDER_DIRS[provider]
|
|
||||||
playbook = f"{provider_dir}/c2.yml"
|
playbook = f"{provider_dir}/c2.yml"
|
||||||
inventory_path = create_inventory_file(config, "local")
|
inventory_path = create_inventory_file(c2_config, "local")
|
||||||
|
|
||||||
logging.info(f"Deploying C2 server using {playbook} with plan: {config['plan']}")
|
logging.info(f"Deploying {provider} C2 server using {playbook} in region {c2_config.get('region', 'default')}")
|
||||||
c2_success, stdout, stderr = run_ansible_playbook(
|
c2_success, stdout, stderr = run_ansible_playbook(
|
||||||
playbook, inventory_path, config, config.get('debug', False)
|
playbook, inventory_path, c2_config, c2_config.get('debug', False)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log Ansible output
|
|
||||||
if config.get('debug', False):
|
|
||||||
logging.debug(f"Ansible stdout: {stdout}")
|
|
||||||
if stderr:
|
|
||||||
logging.debug(f"Ansible stderr: {stderr}")
|
|
||||||
|
|
||||||
if os.path.exists(inventory_path):
|
if os.path.exists(inventory_path):
|
||||||
os.unlink(inventory_path)
|
os.unlink(inventory_path)
|
||||||
|
|
||||||
if not c2_success:
|
if not c2_success:
|
||||||
logging.error("C2 server deployment failed")
|
logging.error(f"{provider} C2 server deployment failed")
|
||||||
return False
|
if c2_config.get('debug'):
|
||||||
else:
|
logging.error(f"Ansible stderr: {stderr}")
|
||||||
# For AWS and other providers, follow the same pattern but with their directory names
|
|
||||||
provider_dir = PROVIDER_DIRS[provider]
|
|
||||||
|
|
||||||
# Redirector deployment
|
|
||||||
if not config.get('c2_only'):
|
|
||||||
playbook = f"{provider_dir}/redirector.yml"
|
|
||||||
inventory_path = create_inventory_file(config, "local")
|
|
||||||
|
|
||||||
logging.info(f"Deploying redirector using {playbook}")
|
|
||||||
redirector_success, stdout, stderr = run_ansible_playbook(
|
|
||||||
playbook, inventory_path, config, config.get('debug', False)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Log Ansible output
|
|
||||||
if config.get('debug', False):
|
|
||||||
logging.debug(f"Ansible stdout: {stdout}")
|
|
||||||
if stderr:
|
|
||||||
logging.debug(f"Ansible stderr: {stderr}")
|
|
||||||
|
|
||||||
if os.path.exists(inventory_path):
|
|
||||||
os.unlink(inventory_path)
|
|
||||||
|
|
||||||
if not redirector_success:
|
|
||||||
logging.error("Redirector deployment failed")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# C2 deployment
|
# Extract and save C2 IP for reference
|
||||||
if not config.get('redirector_only'):
|
if 'c2_ip' in c2_config:
|
||||||
playbook = f"{provider_dir}/c2.yml"
|
config['c2_ip'] = c2_config['c2_ip']
|
||||||
inventory_path = create_inventory_file(config, "local")
|
|
||||||
|
|
||||||
logging.info(f"Deploying C2 server using {playbook}")
|
|
||||||
c2_success, stdout, stderr = run_ansible_playbook(
|
|
||||||
playbook, inventory_path, config, config.get('debug', False)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Log Ansible output
|
|
||||||
if config.get('debug', False):
|
|
||||||
logging.debug(f"Ansible stdout: {stdout}")
|
|
||||||
if stderr:
|
|
||||||
logging.debug(f"Ansible stderr: {stderr}")
|
|
||||||
|
|
||||||
if os.path.exists(inventory_path):
|
|
||||||
os.unlink(inventory_path)
|
|
||||||
|
|
||||||
if not c2_success:
|
|
||||||
logging.error("C2 server deployment failed")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -951,11 +1027,13 @@ def ssh_to_instance(config):
|
|||||||
# Build SSH command
|
# Build SSH command
|
||||||
ssh_cmd = [
|
ssh_cmd = [
|
||||||
"ssh",
|
"ssh",
|
||||||
|
"-t",
|
||||||
"-o", "StrictHostKeyChecking=no",
|
"-o", "StrictHostKeyChecking=no",
|
||||||
"-o", "UserKnownHostsFile=/dev/null",
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
"-o", "IdentitiesOnly=yes",
|
"-o", "IdentitiesOnly=yes",
|
||||||
"-i", ssh_key,
|
"-i", ssh_key,
|
||||||
f"{ssh_user}@{ip}"
|
f"{ssh_user}@{ip}",
|
||||||
|
"tmux new"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add port if specified
|
# Add port if specified
|
||||||
@@ -1325,7 +1403,7 @@ def generate_deployment_info(config, success=True):
|
|||||||
if havoc_admin_password:
|
if havoc_admin_password:
|
||||||
info.append(f"Admin Password: {havoc_admin_password}")
|
info.append(f"Admin Password: {havoc_admin_password}")
|
||||||
else:
|
else:
|
||||||
info.append("Admin Password: Check /root/Tools/havoc/data/profiles/default.yaotl on C2 server")
|
info.append("Admin Password: Check /root/Tools/Havoc/data/profiles/default.yaotl on C2 server")
|
||||||
info.append("")
|
info.append("")
|
||||||
|
|
||||||
# DNS Configuration
|
# DNS Configuration
|
||||||
@@ -1396,6 +1474,54 @@ def generate_deployment_info(config, success=True):
|
|||||||
logging.info(f"Deployment information saved to {log_file}")
|
logging.info(f"Deployment information saved to {log_file}")
|
||||||
return log_file
|
return log_file
|
||||||
|
|
||||||
|
def deploy_cross_provider(config, redirector_provider, c2_provider):
|
||||||
|
"""Deploy infrastructure across multiple providers"""
|
||||||
|
# Create copies of config for each provider
|
||||||
|
redirector_config = config.copy()
|
||||||
|
redirector_config['provider'] = redirector_provider
|
||||||
|
redirector_config['c2_only'] = False
|
||||||
|
redirector_config['redirector_only'] = True
|
||||||
|
|
||||||
|
c2_config = config.copy()
|
||||||
|
c2_config['provider'] = c2_provider
|
||||||
|
c2_config['c2_only'] = True
|
||||||
|
c2_config['redirector_only'] = False
|
||||||
|
|
||||||
|
# Set specific regions if provided
|
||||||
|
if config.get('redirector_region'):
|
||||||
|
redirector_config['region'] = config['redirector_region']
|
||||||
|
if config.get('c2_region'):
|
||||||
|
c2_config['region'] = config['c2_region']
|
||||||
|
|
||||||
|
logging.info(f"Cross-provider deployment: Redirector using {redirector_provider} in {redirector_config.get('region', 'default region')}")
|
||||||
|
|
||||||
|
# Deploy redirector first
|
||||||
|
redirector_success = deploy_infrastructure(redirector_config)
|
||||||
|
|
||||||
|
if not redirector_success:
|
||||||
|
logging.error("Redirector deployment failed!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Pass redirector IP to C2 config
|
||||||
|
if 'redirector_ip' in redirector_config:
|
||||||
|
c2_config['redirector_ip'] = redirector_config['redirector_ip']
|
||||||
|
config['redirector_ip'] = redirector_config['redirector_ip']
|
||||||
|
|
||||||
|
logging.info(f"Cross-provider deployment: C2 server using {c2_provider} in {c2_config.get('region', 'default region')}")
|
||||||
|
|
||||||
|
# Deploy C2 server
|
||||||
|
c2_success = deploy_infrastructure(c2_config)
|
||||||
|
|
||||||
|
if not c2_success:
|
||||||
|
logging.error("C2 server deployment failed!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Update the original config with IPs from both deployments
|
||||||
|
if 'c2_ip' in c2_config:
|
||||||
|
config['c2_ip'] = c2_config['c2_ip']
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to run the deployment"""
|
"""Main function to run the deployment"""
|
||||||
# Display banner
|
# Display banner
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
set -e
|
set -e
|
||||||
TEMP_DIR=$(mktemp -d)
|
TEMP_DIR=$(mktemp -d)
|
||||||
LOG_FILE="/root/Tools/havoc_installer.log"
|
LOG_FILE="/root/Tools/havoc_installer.log"
|
||||||
HAVOC_DIR="/root/Tools/havoc"
|
HAVOC_DIR="/root/Tools/Havoc"
|
||||||
HAVOC_DATA_DIR="/root/Tools/havoc/data"
|
HAVOC_DATA_DIR="/root/Tools/Havoc/data"
|
||||||
COMPILER_URL="http://musl.cc/x86_64-w64-mingw32-cross.tgz"
|
COMPILER_URL="http://musl.cc/x86_64-w64-mingw32-cross.tgz"
|
||||||
COMPILER_DIR="/usr/bin/x86_64-w64-mingw32-cross"
|
COMPILER_DIR="/usr/bin/x86_64-w64-mingw32-cross"
|
||||||
COMPILER_PATH="$COMPILER_DIR/bin/x86_64-w64-mingw32-gcc"
|
COMPILER_PATH="$COMPILER_DIR/bin/x86_64-w64-mingw32-gcc"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ LISTEN_PORT=4444
|
|||||||
C2_HOST="127.0.0.1" # This will be replaced by Ansible with actual C2 IP
|
C2_HOST="127.0.0.1" # This will be replaced by Ansible with actual C2 IP
|
||||||
HAVOC_PORT=40056 # Havoc default Teamserver port
|
HAVOC_PORT=40056 # Havoc default Teamserver port
|
||||||
HAVOC_USER="admin"
|
HAVOC_USER="admin"
|
||||||
HAVOC_DIR="/root/Tools/havoc"
|
HAVOC_DIR="/root/Tools/Havoc"
|
||||||
WINDOWS_PAYLOAD="windows/agent_win.exe"
|
WINDOWS_PAYLOAD="windows/agent_win.exe"
|
||||||
LINUX_PAYLOAD="linux/agent_linux"
|
LINUX_PAYLOAD="linux/agent_linux"
|
||||||
|
|
||||||
@@ -65,21 +65,21 @@ deploy_agent() {
|
|||||||
log "Deploying Havoc agent for detected OS: $os_type"
|
log "Deploying Havoc agent for detected OS: $os_type"
|
||||||
|
|
||||||
# Get the latest payload paths from manifest
|
# Get the latest payload paths from manifest
|
||||||
local manifest="/root/Tools/havoc/payloads/manifest.json"
|
local manifest="/root/Tools/Havoc/payloads/manifest.json"
|
||||||
if [ -f "$manifest" ]; then
|
if [ -f "$manifest" ]; then
|
||||||
if [ "$os_type" == "windows" ]; then
|
if [ "$os_type" == "windows" ]; then
|
||||||
WIN_EXE=$(jq -r '.windows_exe' "$manifest")
|
WIN_EXE=$(jq -r '.windows_exe' "$manifest")
|
||||||
PAYLOAD_PATH="/root/Tools/havoc/payloads/windows/$WIN_EXE"
|
PAYLOAD_PATH="/root/Tools/Havoc/payloads/windows/$WIN_EXE"
|
||||||
elif [ "$os_type" == "linux" ]; then
|
elif [ "$os_type" == "linux" ]; then
|
||||||
LINUX_BIN=$(jq -r '.linux_binary' "$manifest")
|
LINUX_BIN=$(jq -r '.linux_binary' "$manifest")
|
||||||
PAYLOAD_PATH="/root/Tools/havoc/payloads/linux/$LINUX_BIN"
|
PAYLOAD_PATH="/root/Tools/Havoc/payloads/linux/$LINUX_BIN"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Use default paths if manifest doesn't exist
|
# Use default paths if manifest doesn't exist
|
||||||
if [ "$os_type" == "windows" ]; then
|
if [ "$os_type" == "windows" ]; then
|
||||||
PAYLOAD_PATH="/root/Tools/havoc/payloads/windows/agent_win.exe"
|
PAYLOAD_PATH="/root/Tools/Havoc/payloads/windows/agent_win.exe"
|
||||||
elif [ "$os_type" == "linux" ]; then
|
elif [ "$os_type" == "linux" ]; then
|
||||||
PAYLOAD_PATH="/root/Tools/havoc/payloads/linux/agent_linux"
|
PAYLOAD_PATH="/root/Tools/Havoc/payloads/linux/agent_linux"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
- name: Install Havoc C2 Framework
|
- name: Install Havoc C2 Framework
|
||||||
shell: "/root/Tools/havoc_installer.sh"
|
shell: "/root/Tools/havoc_installer.sh"
|
||||||
args:
|
args:
|
||||||
creates: "/root/Tools/havoc"
|
creates: "/root/Tools/Havoc"
|
||||||
async: 1800 # Allow 30 minutes for completion
|
async: 1800 # Allow 30 minutes for completion
|
||||||
poll: 0 # Don't wait for completion
|
poll: 0 # Don't wait for completion
|
||||||
register: havoc_installation_job
|
register: havoc_installation_job
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
- name: Create Havoc C2 configuration from template
|
- name: Create Havoc C2 configuration from template
|
||||||
template:
|
template:
|
||||||
src: "../templates/havoc-config.yaotl.j2"
|
src: "../templates/havoc-config.yaotl.j2"
|
||||||
dest: "/root/Tools/havoc/data/havoc.yaotl"
|
dest: "/root/Tools/Havoc/data/havoc.yaotl"
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
- name: Generate Havoc payloads
|
- name: Generate Havoc payloads
|
||||||
shell: "/root/Tools/generate_havoc_payloads.sh"
|
shell: "/root/Tools/generate_havoc_payloads.sh"
|
||||||
args:
|
args:
|
||||||
creates: "/root/Tools/havoc/payloads/manifest.json"
|
creates: "/root/Tools/Havoc/payloads/manifest.json"
|
||||||
register: payload_generation_result
|
register: payload_generation_result
|
||||||
environment:
|
environment:
|
||||||
PATH: "{{ ansible_env.PATH }}:/usr/local/bin"
|
PATH: "{{ ansible_env.PATH }}:/usr/local/bin"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Every deployment produces completely unique payloads
|
# Every deployment produces completely unique payloads
|
||||||
|
|
||||||
# Configuration (automatically populated by Ansible)
|
# Configuration (automatically populated by Ansible)
|
||||||
HAVOC_DIR="/root/Tools/havoc"
|
HAVOC_DIR="/root/Tools/Havoc"
|
||||||
BEACONS_DIR="/root/Tools/beacons"
|
BEACONS_DIR="/root/Tools/beacons"
|
||||||
C2_HOST="{{ ansible_host }}"
|
C2_HOST="{{ ansible_host }}"
|
||||||
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}"
|
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}"
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
# EDR-evasive payload generator for Havoc C2
|
# EDR-evasive payload generator for Havoc C2
|
||||||
|
|
||||||
# Configuration (automatically populated by Ansible)
|
# Configuration (automatically populated by Ansible)
|
||||||
PAYLOADS_DIR="/root/Tools/havoc/payloads"
|
PAYLOADS_DIR="/root/Tools/Havoc/payloads"
|
||||||
C2_HOST="{{ ansible_host }}"
|
C2_HOST="{{ ansible_host }}"
|
||||||
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}"
|
REDIRECTOR_HOST="{{ redirector_subdomain }}.{{ domain }}"
|
||||||
REDIRECTOR_PORT="{{ redirector_port | default('443') }}"
|
REDIRECTOR_PORT="{{ redirector_port | default('443') }}"
|
||||||
TEAMSERVER_HOST="127.0.0.1"
|
TEAMSERVER_HOST="127.0.0.1"
|
||||||
TEAMSERVER_PORT="40056"
|
TEAMSERVER_PORT="40056"
|
||||||
HAVOC_DIR="/root/Tools/havoc"
|
HAVOC_DIR="/root/Tools/Havoc"
|
||||||
HAVOC_CLIENT="$HAVOC_DIR/Client/havoc"
|
HAVOC_CLIENT="$HAVOC_DIR/Client/havoc"
|
||||||
|
|
||||||
# Ensure required directories exist
|
# Ensure required directories exist
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ Operators {
|
|||||||
{% for operator in havoc_operators %}
|
{% for operator in havoc_operators %}
|
||||||
user "{{ operator.name }}" {
|
user "{{ operator.name }}" {
|
||||||
Password = "{{ operator.password }}"
|
Password = "{{ operator.password }}"
|
||||||
Secret = "{{ operator.secret | default(lookup('password', '/dev/null chars=hex_lower length=32')) }}"
|
# Secret is not in documentation, removed or comment it out
|
||||||
|
# Secret = "{{ operator.secret | default(lookup('password', '/dev/null chars=hex_lower length=32')) }}"
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -42,6 +43,9 @@ Listeners {
|
|||||||
HostBind = "0.0.0.0"
|
HostBind = "0.0.0.0"
|
||||||
HostRotation = "{{ havoc_host_rotation | default('round-robin') }}"
|
HostRotation = "{{ havoc_host_rotation | default('round-robin') }}"
|
||||||
PortBind = {{ havoc_http_port | default(8080) }}
|
PortBind = {{ havoc_http_port | default(8080) }}
|
||||||
|
{% if havoc_portconn is defined %}
|
||||||
|
PortConn = {{ havoc_portconn }}
|
||||||
|
{% endif %}
|
||||||
{% if havoc_user_agent is defined %}
|
{% if havoc_user_agent is defined %}
|
||||||
UserAgent = "{{ havoc_user_agent }}"
|
UserAgent = "{{ havoc_user_agent }}"
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -128,8 +132,8 @@ Listeners {
|
|||||||
Secure = true
|
Secure = true
|
||||||
|
|
||||||
Cert {
|
Cert {
|
||||||
Cert = "{{ havoc_cert_path | default('/root/Tools/havoc/data/certs/havoc.crt') }}"
|
Cert = "{{ havoc_cert_path | default('/root/Tools/Havoc/data/certs/havoc.crt') }}"
|
||||||
Key = "{{ havoc_key_path | default('/root/Tools/havoc/data/certs/havoc.key') }}"
|
Key = "{{ havoc_key_path | default('/root/Tools/Havoc/data/certs/havoc.key') }}"
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if havoc_proxy_enabled | default(false) %}
|
{% if havoc_proxy_enabled | default(false) %}
|
||||||
@@ -153,6 +157,9 @@ Listeners {
|
|||||||
Demon {
|
Demon {
|
||||||
Sleep = {{ havoc_sleep | default(random_hex[0:2] | int % 5 + 2) }}
|
Sleep = {{ havoc_sleep | default(random_hex[0:2] | int % 5 + 2) }}
|
||||||
Jitter = {{ havoc_jitter | default(random_hex[2:4] | int % 30 + 20) }}
|
Jitter = {{ havoc_jitter | default(random_hex[2:4] | int % 30 + 20) }}
|
||||||
|
{% if havoc_trust_x_forwarded_for is defined %}
|
||||||
|
TrustXForwardedFor = {{ havoc_trust_x_forwarded_for | lower }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
Injection {
|
Injection {
|
||||||
{% if havoc_spawn64 is defined %}
|
{% if havoc_spawn64 is defined %}
|
||||||
@@ -168,3 +175,24 @@ Demon {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{% if havoc_service_enabled | default(false) %}
|
||||||
|
Service {
|
||||||
|
Endpoint = "{{ havoc_service_endpoint | default('/api/service') }}"
|
||||||
|
Password = "{{ havoc_service_password | default(lookup('password', '/dev/null chars=ascii_letters,digits length=32')) }}"
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if havoc_webhook_discord_enabled | default(false) %}
|
||||||
|
Webhook {
|
||||||
|
Discord {
|
||||||
|
Url = "{{ havoc_webhook_discord_url }}"
|
||||||
|
{% if havoc_webhook_discord_avatar_url is defined %}
|
||||||
|
AvatarUrl = "{{ havoc_webhook_discord_avatar_url }}"
|
||||||
|
{% endif %}
|
||||||
|
{% if havoc_webhook_discord_user is defined %}
|
||||||
|
User = "{{ havoc_webhook_discord_user }}"
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
@@ -12,7 +12,7 @@ Teamserver Port: {{ havoc_teamserver_port | default(40056) }}
|
|||||||
HTTP Listener Port: {{ havoc_http_port | default(8080) }}
|
HTTP Listener Port: {{ havoc_http_port | default(8080) }}
|
||||||
HTTPS Listener Port: {{ havoc_https_port | default(443) }}
|
HTTPS Listener Port: {{ havoc_https_port | default(443) }}
|
||||||
Admin User: {{ havoc_admin_user | default('admin') }}
|
Admin User: {{ havoc_admin_user | default('admin') }}
|
||||||
Admin Password: Stored in /root/Tools/havoc/data/profiles/default.yaotl
|
Admin Password: Stored in /root/Tools/Havoc/data/profiles/default.yaotl
|
||||||
|
|
||||||
CONNECTING TO THE TEAMSERVER
|
CONNECTING TO THE TEAMSERVER
|
||||||
---------------------------
|
---------------------------
|
||||||
@@ -29,7 +29,7 @@ From your local machine:
|
|||||||
- Host: {{ c2_ip }}
|
- Host: {{ c2_ip }}
|
||||||
- Port: {{ havoc_teamserver_port | default(40056) }}
|
- Port: {{ havoc_teamserver_port | default(40056) }}
|
||||||
- User: {{ havoc_admin_user | default('admin') }}
|
- User: {{ havoc_admin_user | default('admin') }}
|
||||||
- Password: See /root/Tools/havoc/data/profiles/default.yaotl
|
- Password: See /root/Tools/Havoc/data/profiles/default.yaotl
|
||||||
|
|
||||||
3. CLI Connection:
|
3. CLI Connection:
|
||||||
$ ./havoc client --address {{ c2_ip }}:{{ havoc_teamserver_port | default(40056) }} --username {{ havoc_admin_user | default('admin') }} --password [password]
|
$ ./havoc client --address {{ c2_ip }}:{{ havoc_teamserver_port | default(40056) }} --username {{ havoc_admin_user | default('admin') }} --password [password]
|
||||||
@@ -44,7 +44,7 @@ To view and manage listeners: Attack → Listeners in the Havoc client.
|
|||||||
|
|
||||||
GENERATING PAYLOADS
|
GENERATING PAYLOADS
|
||||||
-----------------
|
-----------------
|
||||||
Pre-generated payloads are available in /root/Tools/havoc/payloads/
|
Pre-generated payloads are available in /root/Tools/Havoc/payloads/
|
||||||
|
|
||||||
To generate new payloads:
|
To generate new payloads:
|
||||||
1. Connect to the Teamserver
|
1. Connect to the Teamserver
|
||||||
@@ -94,7 +94,7 @@ SERVER MANAGEMENT
|
|||||||
---------------
|
---------------
|
||||||
- Havoc Teamserver service: systemctl status havoc
|
- Havoc Teamserver service: systemctl status havoc
|
||||||
- Service configuration: /etc/systemd/system/havoc.service
|
- Service configuration: /etc/systemd/system/havoc.service
|
||||||
- Configuration profiles: /root/Tools/havoc/data/profiles/
|
- Configuration profiles: /root/Tools/Havoc/data/profiles/
|
||||||
|
|
||||||
TROUBLESHOOTING
|
TROUBLESHOOTING
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Custom Tools Installed in ~/Tools:
|
|||||||
Other Installed C2 Frameworks:
|
Other Installed C2 Frameworks:
|
||||||
------------------------------
|
------------------------------
|
||||||
- Metasploit Framework: system installed (run 'msfconsole')
|
- Metasploit Framework: system installed (run 'msfconsole')
|
||||||
- Havoc C2: installed in /root/Tools/havoc
|
- Havoc C2: installed in /root/Tools/Havoc
|
||||||
|
|
||||||
Security Scripts in /root/Tools/:
|
Security Scripts in /root/Tools/:
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Custom Tools Installed in ~/Tools:
|
|||||||
Other Installed C2 Frameworks:
|
Other Installed C2 Frameworks:
|
||||||
------------------------------
|
------------------------------
|
||||||
- Metasploit Framework: system installed (run 'msfconsole')
|
- Metasploit Framework: system installed (run 'msfconsole')
|
||||||
- Havoc C2: installed in /root/Tools/havoc
|
- Havoc C2: installed in /root/Tools/Havoc
|
||||||
|
|
||||||
Also, remember that many reconnaissance and attack tools are now available system-wide due to the apt and pipx installations.
|
Also, remember that many reconnaissance and attack tools are now available system-wide due to the apt and pipx installations.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ Custom Tools Installed in ~/Tools:
|
|||||||
Other Installed C2 Frameworks:
|
Other Installed C2 Frameworks:
|
||||||
------------------------------
|
------------------------------
|
||||||
- Metasploit Framework: system installed (run 'msfconsole')
|
- Metasploit Framework: system installed (run 'msfconsole')
|
||||||
- Havoc C2: installed in /root/Tools/havoc
|
- Havoc C2: installed in /root/Tools/Havoc
|
||||||
|
|
||||||
Also, remember that many reconnaissance and attack tools are now available system-wide due to the apt and pipx installations.
|
Also, remember that many reconnaissance and attack tools are now available system-wide due to the apt and pipx installations.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Script to serve Havoc C2 payloads generated by generate_havoc_payloads.sh
|
# Script to serve Havoc C2 payloads generated by generate_havoc_payloads.sh
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
PAYLOADS_DIR="/root/Tools/havoc/payloads"
|
PAYLOADS_DIR="/root/Tools/Havoc/payloads"
|
||||||
C2_HOST="{{ ansible_host }}"
|
C2_HOST="{{ ansible_host }}"
|
||||||
LISTEN_PORT=8443
|
LISTEN_PORT=8443
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user