diff --git a/AWS/c2.yml b/AWS/c2.yml index 537aabc..8eb4f51 100644 --- a/AWS/c2.yml +++ b/AWS/c2.yml @@ -41,48 +41,51 @@ when: c2_key_pair.changed and c2_key_pair.key.private_key is defined - name: Create a security group for the C2 server - amazon.aws.ec2_group: - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" + amazon.aws.ec2_security_group: name: "{{ c2_name }}-sg" description: "Security group for C2 server {{ c2_name }}" + vpc_id: "{{ vpc_id }}" region: "{{ aws_region }}" rules: - proto: tcp - ports: + ports: - 22 - 80 - 443 - - 8888 # Sliver HTTP listener - - 50051 # Sliver gRPC port - - 31337 # Additional C2 port + - "{{ havoc_teamserver_port | default(40056) }}" + - "{{ havoc_http_port | default(8080) }}" + - "{{ havoc_https_port | default(443) }}" + - "{{ gophish_admin_port | default(3333) }}" cidr_ip: 0.0.0.0/0 rules_egress: - proto: -1 cidr_ip: 0.0.0.0/0 - register: c2_sg_result + state: present + register: security_group - - name: Launch C2 EC2 instance + - name: Set region for AWS C2 server + set_fact: + aws_c2_region: "{{ aws_region | default(region, true) | default('us-east-1', true) }}" + when: aws_region_choices is not defined or aws_region_choices|length == 0 + + - name: Set random region for AWS C2 server + set_fact: + aws_c2_region: "{{ aws_region_choices | random }}" + when: aws_region_choices is defined and aws_region_choices|length > 0 and aws_c2_region is not defined + + - name: Launch EC2 instance for C2 server amazon.aws.ec2_instance: - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" - region: "{{ aws_region }}" name: "{{ c2_name }}" - image_id: "{{ ami_map[aws_region] }}" - instance_type: "{{ instance_type }}" - key_name: "{{ c2_name }}" - security_groups: - - "{{ c2_name }}-sg" + key_name: "{{ aws_keypair_name }}" + instance_type: "{{ instance_type | default('t2.medium') }}" + security_group: "{{ c2_name }}-sg" + image_id: "{{ ami_id }}" + region: "{{ aws_c2_region }}" + state: present wait: yes tags: Name: "{{ c2_name }}" - Role: "c2" - volumes: - - device_name: "/dev/xvda" - ebs: - volume_size: 100 - delete_on_termination: true - register: c2_instance + register: ec2 - name: Set c2_ip for later use set_fact: diff --git a/AWS/cleanup.yml b/AWS/cleanup.yml index 399311f..e111f73 100644 --- a/AWS/cleanup.yml +++ b/AWS/cleanup.yml @@ -72,6 +72,105 @@ c2_instance_id: "{{ c2_id_result.stdout | trim }}" when: cleanup_c2 and c2_id_result.stdout is defined and c2_id_result.stdout | trim != "" + - name: Get information about the redirector security group + amazon.aws.ec2_security_group_info: + filters: + tag:redirector_name: "{{ redirector_name }}" + region: "{{ aws_region }}" + register: sg_info + ignore_errors: yes + when: cleanup_redirector | bool + + - name: Get information about the VPC + amazon.aws.ec2_vpc_net_info: + filters: + tag:redirector_name: "{{ redirector_name }}" + region: "{{ aws_region }}" + register: vpc_info + ignore_errors: yes + when: cleanup_redirector | bool + + - name: Get subnet information + amazon.aws.ec2_vpc_subnet_info: + filters: + tag:redirector_name: "{{ redirector_name }}" + region: "{{ aws_region }}" + register: subnet_info + ignore_errors: yes + when: vpc_info.vpcs is defined and vpc_info.vpcs | length > 0 + + - name: Get internet gateway information + amazon.aws.ec2_vpc_igw_info: + filters: + tag:redirector_name: "{{ redirector_name }}" + region: "{{ aws_region }}" + register: igw_info + ignore_errors: yes + when: vpc_info.vpcs is defined and vpc_info.vpcs | length > 0 + + - name: Get route table information + amazon.aws.ec2_vpc_route_table_info: + filters: + tag:redirector_name: "{{ redirector_name }}" + region: "{{ aws_region }}" + register: rtb_info + ignore_errors: yes + when: vpc_info.vpcs is defined and vpc_info.vpcs | length > 0 + + # Delete in proper order (dependencies first) + - name: Delete route tables + amazon.aws.ec2_vpc_route_table: + route_table_id: "{{ item.id }}" + region: "{{ aws_region }}" + state: absent + loop: "{{ rtb_info.route_tables | default([]) }}" + ignore_errors: yes + when: rtb_info.route_tables is defined and rtb_info.route_tables | length > 0 + + - name: Detach internet gateway from VPC + amazon.aws.ec2_vpc_igw: + vpc_id: "{{ vpc_info.vpcs[0].id }}" + region: "{{ aws_region }}" + state: absent + ignore_errors: yes + when: vpc_info.vpcs is defined and vpc_info.vpcs | length > 0 and igw_info.internet_gateways is defined and igw_info.internet_gateways | length > 0 + + - name: Delete internet gateway + amazon.aws.ec2_vpc_igw: + igw_id: "{{ item.id }}" + region: "{{ aws_region }}" + state: absent + loop: "{{ igw_info.internet_gateways | default([]) }}" + ignore_errors: yes + when: igw_info.internet_gateways is defined and igw_info.internet_gateways | length > 0 + + - name: Delete subnets + amazon.aws.ec2_vpc_subnet: + vpc_id: "{{ vpc_info.vpcs[0].id }}" + cidr: "{{ item.cidr_block }}" + region: "{{ aws_region }}" + state: absent + loop: "{{ subnet_info.subnets | default([]) }}" + ignore_errors: yes + when: subnet_info.subnets is defined and subnet_info.subnets | length > 0 + + - name: Delete security groups + amazon.aws.ec2_security_group: + group_id: "{{ item.group_id }}" + region: "{{ aws_region }}" + state: absent + loop: "{{ sg_info.security_groups | default([]) }}" + ignore_errors: yes + when: sg_info.security_groups is defined and sg_info.security_groups | length > 0 + + - name: Delete VPC + amazon.aws.ec2_vpc_net: + vpc_id: "{{ vpc_info.vpcs[0].id }}" + region: "{{ aws_region }}" + state: absent + ignore_errors: yes + when: vpc_info.vpcs is defined and vpc_info.vpcs | length > 0 + - name: Terminate redirector instance amazon.aws.ec2_instance: state: absent diff --git a/AWS/redirector.yml b/AWS/redirector.yml index 7fe0e87..a1c9bca 100644 --- a/AWS/redirector.yml +++ b/AWS/redirector.yml @@ -43,42 +43,107 @@ mode: "0600" when: redirector_key_pair.changed and redirector_key_pair.key.private_key is defined - - name: Create a security group for the redirector - amazon.aws.ec2_group: - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" - name: "{{ redirector_name }}-sg" - description: "Security group for redirector {{ redirector_name }}" + - name: Check if VPC exists with specified ID (if provided) + amazon.aws.ec2_vpc_net_info: + vpc_ids: "{{ vpc_id | default(omit) }}" region: "{{ aws_region }}" - rules: - - proto: tcp - ports: - - 22 - - 80 - - 443 - - "{{ shell_handler_port }}" - cidr_ip: 0.0.0.0/0 - rules_egress: - - proto: -1 - cidr_ip: 0.0.0.0/0 - register: redirector_sg_result + register: vpc_info + when: vpc_id is defined and vpc_id | length > 0 - - name: Launch redirector EC2 instance - amazon.aws.ec2_instance: - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" + - name: Create VPC if not provided or not found + amazon.aws.ec2_vpc_net: + name: "{{ redirector_name }}-vpc" + cidr_block: "10.0.0.0/16" region: "{{ aws_region }}" + tags: + Name: "{{ redirector_name }}-vpc" + redirector_name: "{{ redirector_name }}" + register: vpc_result + when: vpc_id is not defined or vpc_id | length == 0 or (vpc_info.vpcs | default([]) | length == 0) + + - name: Set VPC ID from existing or newly created VPC + set_fact: + vpc_id: "{{ vpc_id if (vpc_id is defined and vpc_id | length > 0 and vpc_info.vpcs | length > 0) else vpc_result.vpc.id }}" + + - name: Create internet gateway for VPC + amazon.aws.ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + region: "{{ aws_region }}" + state: present + tags: + Name: "{{ redirector_name }}-igw" + redirector_name: "{{ redirector_name }}" + register: igw_result + when: vpc_result.changed | default(false) + + - name: Create subnet in VPC + amazon.aws.ec2_vpc_subnet: + vpc_id: "{{ vpc_id }}" + cidr: "10.0.1.0/24" + region: "{{ aws_region }}" + az: "{{ aws_region }}a" + map_public: yes + tags: + Name: "{{ redirector_name }}-subnet" + redirector_name: "{{ redirector_name }}" + register: subnet_result + when: vpc_result.changed | default(false) + + - name: Create routing table for internet access + amazon.aws.ec2_vpc_route_table: + vpc_id: "{{ vpc_id }}" + region: "{{ aws_region }}" + tags: + Name: "{{ redirector_name }}-rtb" + redirector_name: "{{ redirector_name }}" + routes: + - dest: "0.0.0.0/0" + gateway_id: "{{ igw_result.gateway_id }}" + subnets: + - "{{ subnet_result.subnet.id }}" + register: route_table_result + when: vpc_result.changed | default(false) and igw_result.changed | default(false) and subnet_result.changed | default(false) + + - name: Set region for AWS redirector + set_fact: + aws_redirector_region: "{{ aws_region | default(region, true) | default('us-east-1', true) }}" + when: aws_region_choices is not defined or aws_region_choices|length == 0 + + - name: Set random region for AWS redirector + set_fact: + aws_redirector_region: "{{ aws_region_choices | random }}" + when: aws_region_choices is defined and aws_region_choices|length > 0 and aws_redirector_region is not defined + + - name: Launch EC2 instance for redirector + amazon.aws.ec2_instance: name: "{{ redirector_name }}" - image_id: "{{ ami_map[aws_region] }}" - instance_type: "{{ instance_type }}" - key_name: "{{ redirector_name }}" - security_groups: - - "{{ redirector_name }}-sg" + key_name: "{{ aws_keypair_name }}" + instance_type: "{{ instance_type | default('t2.micro') }}" + security_group: "{{ redirector_name }}-sg" + image_id: "{{ ami_id }}" + region: "{{ aws_redirector_region }}" + state: present wait: yes tags: Name: "{{ redirector_name }}" - Role: "redirector" - register: redirector_instance + register: ec2 + + - name: Launch EC2 instance for redirector + amazon.aws.ec2_instance: + name: "{{ redirector_name }}" + key_name: "{{ aws_keypair_name }}" + vpc_subnet_id: "{{ subnet_result.subnet.id | default(omit) }}" + instance_type: "{{ instance_type | default('t2.micro') }}" + security_group: "{{ redirector_name }}-sg" + network: + assign_public_ip: true + image_id: "{{ ami_id }}" + region: "{{ aws_region }}" + state: present + wait: yes + tags: + Name: "{{ redirector_name }}" + register: ec2 - name: Set redirector_ip for later use set_fact: diff --git a/Linode/c2.yml b/Linode/c2.yml index 31a382b..4bcd70e 100644 --- a/Linode/c2.yml +++ b/Linode/c2.yml @@ -24,14 +24,23 @@ - linode_token is defined and linode_token != "" fail_msg: "Linode API token is required. Set linode_token in vars.yaml or via --linode-token." + - name: Set region for C2 server + set_fact: + c2_region_value: "{{ selected_region | default(region, true) | default(linode_region, true) | default('us-east', true) }}" + when: region_choices is not defined or region_choices|length == 0 + + - name: Set random region for C2 server + set_fact: + c2_region_value: "{{ region_choices | random }}" + when: region_choices is defined and region_choices|length > 0 and c2_region_value is not defined + - name: Create C2 Linode instance community.general.linode_v4: access_token: "{{ linode_token }}" label: "{{ c2_name }}" type: "{{ plan }}" - region: "{{ selected_region }}" - # Use static value to avoid recursive templating - image: "{{ c2_image_static }}" + region: "{{ c2_region_value }}" + image: "linode/kali" root_pass: "{{ lookup('password', '/dev/null length=16') }}" authorized_keys: - "{{ lookup('file', ssh_key_path) }}" diff --git a/Linode/redirector.yml b/Linode/redirector.yml index c36acb1..3790e4d 100644 --- a/Linode/redirector.yml +++ b/Linode/redirector.yml @@ -20,13 +20,23 @@ - linode_token is defined and linode_token != "" fail_msg: "Linode API token is required. Set linode_token in vars.yaml or via --linode-token." + - name: Set region for deployment + set_fact: + deployment_region: "{{ selected_region | default(region, true) | default(linode_region, true) | default('us-east', true) }}" + when: region_choices is not defined or region_choices|length == 0 + + - name: Set random region for deployment + set_fact: + deployment_region: "{{ region_choices | random }}" + when: region_choices is defined and region_choices|length > 0 and deployment_region is not defined + - name: Create redirector Linode instance community.general.linode_v4: access_token: "{{ linode_token }}" label: "{{ redirector_name }}" type: "{{ plan }}" - region: "{{ selected_region }}" - image: "{{ redirector_image_static }}" + region: "{{ deployment_region }}" + image: "linode/debian12" root_pass: "{{ lookup('password', '/dev/null length=16') }}" authorized_keys: - "{{ lookup('file', ssh_key_path) }}" diff --git a/Linode/tracker.yml b/Linode/tracker.yml index 9740afc..9735d39 100644 --- a/Linode/tracker.yml +++ b/Linode/tracker.yml @@ -23,13 +23,23 @@ - linode_token is defined and linode_token != "" fail_msg: "Linode API token is required. Set linode_token in vars.yaml." + - name: Set region for tracker + set_fact: + tracker_region_value: "{{ selected_region | default(region, true) | default(linode_region, true) | default('us-east', true) }}" + when: region_choices is not defined or region_choices|length == 0 + + - name: Set random region for tracker + set_fact: + tracker_region_value: "{{ region_choices | random }}" + when: region_choices is defined and region_choices|length > 0 and tracker_region_value is not defined + - name: Create tracker Linode instance community.general.linode_v4: access_token: "{{ linode_token }}" label: "{{ tracker_name }}" type: "{{ plan }}" - region: "{{ linode_region }}" - image: "{{ tracker_image }}" + region: "{{ tracker_region_value }}" + image: "{{ image | default('linode/debian12') }}" root_pass: "{{ lookup('password', '/dev/null length=16') }}" authorized_keys: - "{{ lookup('file', ssh_key_path) }}" diff --git a/deploy.py b/deploy.py index 53ef789..0128689 100644 --- a/deploy.py +++ b/deploy.py @@ -142,6 +142,7 @@ def parse_arguments(): return parser.parse_args() + def interactive_setup(deployment_id=None): """Interactive setup wizard for deployment""" config = {} @@ -187,36 +188,7 @@ def interactive_setup(deployment_id=None): 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, using random regions") + select_regions(config, vars_data, config['provider'], use_multi_region=True) if cross_provider: print("\nSelect redirector provider:") @@ -287,51 +259,9 @@ def interactive_setup(deployment_id=None): 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: - print("\nAvailable AWS regions:") - for i, region in enumerate(aws_regions, 1): - print(f" {i}. {region}") - - # Handle region selection based on deployment type - if cross_provider or use_multi_region: - # Select specific regions for redirector/C2 if this provider is used for them - if provider == config.get('redirector_provider', config['provider']): - region_input = input("\nSelect region for redirector (number or leave blank for random): ") - if region_input: - 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") - - 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") - else: - # Single region for both if not using multi-region - region_input = input("\nSelect region (number or leave blank for random): ") - if region_input: - try: - region_choice = int(region_input) - if 1 <= region_choice <= len(aws_regions): - config['aws_region'] = aws_regions[region_choice - 1] - else: - print(f"Invalid choice, using random region") - except ValueError: - print("Invalid input, using random region") + # AWS regions - skip if already configured in multi-region setup + if not use_multi_region: + select_regions(config, provider_vars, provider, use_multi_region, cross_provider) elif provider == "linode": # Linode token @@ -339,51 +269,9 @@ def interactive_setup(deployment_id=None): token = input(f"\nLinode API Token [{'*****' if default_token else 'required'}]: ") or default_token config['linode_token'] = token - # Linode regions - linode_regions = provider_vars.get('region_choices', []) - if linode_regions: - print("\nAvailable Linode regions:") - for i, region in enumerate(linode_regions, 1): - print(f" {i}. {region}") - - # Handle region selection based on deployment type - if cross_provider or use_multi_region: - # Select specific regions for redirector/C2 if this provider is used for them - if provider == config.get('redirector_provider', config['provider']): - region_input = input("\nSelect region for redirector (number or leave blank for random): ") - if region_input: - 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") - - 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") - else: - # Single region for both if not using multi-region - region_input = input("\nSelect region (number or leave blank for random): ") - if region_input: - try: - region_choice = int(region_input) - if 1 <= region_choice <= len(linode_regions): - config['linode_region'] = linode_regions[region_choice - 1] - else: - print(f"Invalid choice, using random region") - except ValueError: - print("Invalid input, using random region") + # Skip region selection if already done in multi-region setup + if not use_multi_region: + select_regions(config, provider_vars, provider, use_multi_region, cross_provider) # Instance size/plan default_plan = provider_vars.get('plan', 'g6-standard-2') @@ -602,6 +490,97 @@ def generate_ssh_key(deployment_id=None): logging.error(f"Failed to generate SSH key: {e}") return None +def select_regions(config, provider_vars, provider, use_multi_region=False, cross_provider=False): + """Provider-agnostic region selection function""" + # Determine which region key to use based on provider + if provider == 'aws': + region_key = 'aws_region_choices' + elif provider == 'linode': + region_key = 'region_choices' + else: + region_key = 'region_choices' + + # Get regions list + regions = provider_vars.get(region_key, []) + if not regions: + print(f"No regions found for {provider}, using random selection") + return + + # Show available regions + print(f"\nAvailable {provider.capitalize()} regions:") + for i, region in enumerate(regions, 1): + print(f" {i}. {region}") + + # Multi-region deployment + if use_multi_region and not cross_provider: + if not config.get('redirector_region'): + redirector_region_input = input("\nSelect redirector region (number or leave blank for random): ") + if redirector_region_input: + try: + redirector_region_choice = int(redirector_region_input) + if 1 <= redirector_region_choice <= len(regions): + config['redirector_region'] = regions[redirector_region_choice - 1] + else: + print("Invalid choice, using random region for redirector") + except ValueError: + print("Invalid input, using random region for redirector") + else: + print("Selecting random region for redirector") + + if not config.get('c2_region'): + c2_region_input = input("Select C2 region (number or leave blank for random): ") + if c2_region_input: + try: + c2_region_choice = int(c2_region_input) + if 1 <= c2_region_choice <= len(regions): + config['c2_region'] = regions[c2_region_choice - 1] + else: + print("Invalid choice, using random region for C2") + except ValueError: + print("Invalid input, using random region for C2") + else: + print("Selecting random region for C2") + + # Cross-provider deployment + elif cross_provider: + if provider == config.get('redirector_provider', config['provider']) and not config.get('redirector_region'): + region_input = input("\nSelect region for redirector (number or leave blank for random): ") + if region_input: + try: + region_choice = int(region_input) + if 1 <= region_choice <= len(regions): + config['redirector_region'] = regions[region_choice - 1] + else: + print(f"Invalid choice, using random region") + except ValueError: + print("Invalid input, using random region") + + if provider == config.get('c2_provider', config['provider']) and not config.get('c2_region'): + 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(regions): + config['c2_region'] = regions[region_choice - 1] + else: + print(f"Invalid choice, using random region") + except ValueError: + print("Invalid input, using random region") + + # Single region deployment + else: + region_var = f"{provider}_region" if provider == 'aws' else 'linode_region' if provider == 'linode' else 'region' + region_input = input("\nSelect region (number or leave blank for random): ") + if region_input: + try: + region_choice = int(region_input) + if 1 <= region_choice <= len(regions): + config[region_var] = regions[region_choice - 1] + else: + print(f"Invalid choice, using random region") + except ValueError: + print("Invalid input, using random region") + def select_random_region(config): """Select a random region from the available regions for the provider""" provider = config['provider']