diff --git a/AWS/c2.yml b/AWS/c2.yml index cecb8a2..14a52ad 100644 --- a/AWS/c2.yml +++ b/AWS/c2.yml @@ -87,21 +87,6 @@ - "Using AMI ID: {{ ami_id | default('AMI not defined') }}" - "Detected SSH user: {{ ami_ssh_user }}" - # Fix: Use the deployment_id key name if c2deploy prefix exists (for consistency with tool scripts) - - name: Create EC2 key pair with consistent naming - amazon.aws.ec2_key: - name: "c2deploy_{{ deployment_id }}" - region: "{{ aws_c2_region }}" - state: present - register: c2_key_pair - - - name: Save private key with consistent naming - copy: - content: "{{ c2_key_pair.key.private_key }}" - dest: "~/.ssh/c2deploy_{{ deployment_id }}.pem" - mode: "0600" - when: c2_key_pair.changed and c2_key_pair.key.private_key is defined - # Create new infrastructure only if not using shared - name: Create VPC amazon.aws.ec2_vpc_net: @@ -212,10 +197,12 @@ # Management access only from operator IP - proto: tcp ports: 22 - cidr_ip: "{{ operator_ip }}/32" + cidr_ip: "{{ operator_ip }}" + proto: tcp - proto: tcp ports: "{{ havoc_teamserver_port | default(40056) }}" - cidr_ip: "{{ operator_ip }}/32" + cidr_ip: "{{ operator_ip }}" + proto: tcp # Allow traffic only from redirector - proto: tcp ports: @@ -225,17 +212,45 @@ - "{{ havoc_https_port | default(443) }}" - "{{ gophish_admin_port }}" cidr_ip: "{{ redirector_ip }}/32" + proto: tcp rules_egress: - proto: -1 cidr_ip: 0.0.0.0/0 state: present register: security_group + # Generate or import SSH key for the deployment + - name: Check if deployment SSH key already exists locally + stat: + path: "~/.ssh/c2deploy_{{ deployment_id }}.pem" + register: ssh_key_file + + - name: Ensure proper permissions on SSH key + file: + path: "~/.ssh/c2deploy_{{ deployment_id }}" + mode: '0600' + when: not ssh_key_file.stat.exists + + - name: Check if key pair exists in AWS + amazon.aws.ec2_key_info: + region: "{{ aws_c2_region }}" + filters: + key-name: "c2deploy_{{ deployment_id }}" + register: existing_key_pair + + - name: Import SSH key to AWS + amazon.aws.ec2_key: + name: "c2deploy_{{ deployment_id }}" + key_material: "{{ lookup('file', '~/.ssh/c2deploy_{{ deployment_id }}.pub') }}" + region: "{{ aws_c2_region }}" + state: present + when: existing_key_pair.key_pairs | length == 0 + # Launch the C2 server - use the consistent key pair name - name: Launch C2 instance amazon.aws.ec2_instance: name: "{{ c2_name }}" - key_name: "c2deploy_{{ deployment_id }}" # Use the same key name as created above + key_name: "c2deploy_{{ deployment_id }}" # Use the imported key instance_type: "{{ instance_type | default('t2.medium') }}" vpc_subnet_id: "{{ subnet_id }}" security_groups: diff --git a/AWS/cleanup.yml b/AWS/cleanup.yml index ed1a305..3a7be3b 100644 --- a/AWS/cleanup.yml +++ b/AWS/cleanup.yml @@ -389,54 +389,41 @@ - "VPC resources deleted: {{ vpc_check.vpcs | length == 0 }}" when: not disable_summary | default(false) - # STEP 14: Delete key pairs - more reliable implementation - - name: Find key pairs related to deployment - amazon.aws.ec2_key_info: - region: "{{ aws_region }}" - register: all_key_pairs - - - name: Delete deployment related key pairs - amazon.aws.ec2_key: - name: "{{ item.key_name }}" - region: "{{ aws_region }}" - state: absent - loop: "{{ all_key_pairs.key_pairs | selectattr('key_name', 'search', deployment_id) | list }}" - when: all_key_pairs.key_pairs | length > 0 - - # Add this after your existing key pair finding task - - name: Find c2deploy key pairs - amazon.aws.ec2_key: - name: "c2deploy_{{ deployment_id }}" - region: "{{ aws_region }}" - state: present - register: c2deploy_key_check + # STEP 14: Delete key pairs - completely revised implementation + - name: Check for deployment key in AWS + shell: | + aws ec2 describe-key-pairs --region {{ aws_region }} --filters "Name=key-name,Values=c2deploy_{{ deployment_id }}" --query "KeyPairs[*].KeyName" --output text + environment: + AWS_ACCESS_KEY_ID: "{{ aws_access_key }}" + AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}" + register: keypair_check ignore_errors: yes - - name: Delete c2deploy key pairs + - name: Delete deployment key pair if it exists amazon.aws.ec2_key: name: "c2deploy_{{ deployment_id }}" region: "{{ aws_region }}" state: absent - when: not c2deploy_key_check.failed | default(true) - register: deleted_c2deploy_key - - # STEP 15: Delete SSH key files + when: keypair_check.stdout | trim != "" + register: deleted_keypair + - name: Delete SSH key files file: path: "{{ item }}" state: absent with_items: + - "~/.ssh/c2deploy_{{ deployment_id }}.pem" + - "~/.ssh/c2deploy_{{ deployment_id }}.pub" - "~/.ssh/{{ redirector_name }}.pem" - "~/.ssh/{{ c2_name }}.pem" - "~/.ssh/{{ tracker_name }}.pem" - - "~/.ssh/c2deploy_{{ deployment_id }}.pem" # Fix: add .pem extension - - "~/.ssh/c2deploy_{{ deployment_id }}.pub" ignore_errors: yes register: deleted_ssh_files - name: Count deleted SSH files set_fact: cleanup_summary: "{{ cleanup_summary | combine({ + 'keypairs_deleted': (deleted_keypair.changed | default(false)) | ternary(1, 0), 'ssh_files_deleted': (deleted_ssh_files.results | selectattr('changed', 'defined') | selectattr('changed') | list | length)}) }}" # Remove infrastructure state file - fix path to include deployment_id @@ -458,7 +445,7 @@ - "Security Groups: {{ cleanup_summary.security_groups_found | default(0) }} found, {{ deleted_sgs.results | default([]) | length }} deleted" - "Network Interfaces: {{ cleanup_summary.enis_found | default(0) }} found, {{ deleted_enis.results | default([]) | length }} deleted" - "VPCs: {{ cleanup_summary.vpcs_found | default(0) }} found, {{ cleanup_summary.vpcs_deleted | default(0) }} deleted" - - "Key Pairs: {{ cleanup_summary.keypairs_deleted | default(0) }} deleted" + - "Key Pairs: {{ deleted_key_pairs.results | default([]) | length }} deleted" - "SSH Key Files: {{ cleanup_summary.ssh_files_deleted | default(0) }} deleted" - "Infrastructure file: {{ 'Removed' if infra_file.changed else 'Not found' }}" - "==========================================================" diff --git a/AWS/redirector.yml b/AWS/redirector.yml index d486045..6e95d6c 100644 --- a/AWS/redirector.yml +++ b/AWS/redirector.yml @@ -91,20 +91,6 @@ set_fact: ami_id: "{{ ubuntu_ami_map[aws_redirector_region] | default(ubuntu_ami_map['us-east-1']) }}" - - name: Create EC2 key pair - amazon.aws.ec2_key: - name: "{{ redirector_name }}" - region: "{{ aws_redirector_region }}" - state: present - register: redirector_key_pair - - - name: Save private key locally - copy: - content: "{{ redirector_key_pair.key.private_key }}" - dest: "~/.ssh/{{ redirector_name }}.pem" - mode: "0600" - when: redirector_key_pair.changed and redirector_key_pair.key.private_key is defined - # Create new infrastructure only if not using shared - name: Create VPC amazon.aws.ec2_vpc_net: @@ -210,23 +196,55 @@ dest: "infrastructure_state_{{ deployment_id }}.json" when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false)) - # Launch the redirector - - name: Launch redirector instance + # Generate or import SSH key for the deployment + - name: Check if deployment SSH key already exists locally + stat: + path: "~/.ssh/c2deploy_{{ deployment_id }}.pem" + register: ssh_key_file + + - name: Ensure proper permissions on SSH key + file: + path: "~/.ssh/c2deploy_{{ deployment_id }}" + mode: '0600' + when: not ssh_key_file.stat.exists + + - name: Check if key pair exists in AWS + amazon.aws.ec2_key_info: + region: "{{ aws_c2_region }}" + filters: + key-name: "c2deploy_{{ deployment_id }}" + register: existing_key_pair + + - name: Import SSH key to AWS + amazon.aws.ec2_key: + name: "c2deploy_{{ deployment_id }}" + key_material: "{{ lookup('file', '~/.ssh/c2deploy_{{ deployment_id }}.pub') }}" + region: "{{ aws_c2_region }}" + state: present + when: existing_key_pair.key_pairs | length == 0 + + # Launch the C2 server - use the consistent key pair name + - name: Launch C2 instance amazon.aws.ec2_instance: - name: "{{ redirector_name }}" - key_name: "{{ redirector_name }}" - instance_type: "{{ redirector_instance_type | default('t2.micro') }}" + name: "{{ c2_name }}" + key_name: "c2deploy_{{ deployment_id }}" # Use the imported key + instance_type: "{{ instance_type | default('t2.medium') }}" vpc_subnet_id: "{{ subnet_id }}" security_groups: - "{{ security_group.group_id }}" image_id: "{{ ami_id }}" - region: "{{ aws_redirector_region }}" + region: "{{ aws_c2_region }}" state: present wait: yes + volumes: + - device_name: "/dev/xvda" + ebs: + volume_size: 100 + delete_on_termination: true tags: - Name: "{{ redirector_name }}" + Name: "{{ c2_name }}" deployment_id: "{{ deployment_id }}" - register: redirector_instance + register: c2_instance - name: Set redirector_ip for later use set_fact: diff --git a/deploy.py b/deploy.py index fe4f16b..2a88676 100755 --- a/deploy.py +++ b/deploy.py @@ -508,10 +508,30 @@ def execute_deployment(config): config['c2_name'] = f"s-{config['deployment_id']}" config['tracker_name'] = f"t-{config['deployment_id']}" - # Generate SSH key with the deployment ID if not provided - if not config.get('ssh_key'): - config['ssh_key'] = generate_ssh_key(config['deployment_id']) - config['ssh_key_path'] = f"{config['ssh_key']}.pub" + # Generate SSH key with the deployment ID if not provided + # For all providers, we use a local key that's imported to the cloud + ssh_key_path = os.path.expanduser(f"~/.ssh/c2deploy_{config['deployment_id']}") + ssh_key_pub_path = f"{ssh_key_path}.pub" + + # Check if key exists, generate if it doesn't + if not os.path.exists(ssh_key_path): + print(f"\n{COLORS['BLUE']}Generating SSH key for deployment...{COLORS['RESET']}") + try: + subprocess.run([ + "ssh-keygen", "-t", "rsa", "-b", "4096", + "-f", ssh_key_path, "-q", "-N", "" + ], check=True) + os.chmod(ssh_key_path, 0o600) + print(f"{COLORS['GREEN']}SSH key generated at {ssh_key_path}{COLORS['RESET']}") + except subprocess.CalledProcessError as e: + print(f"{COLORS['RED']}Failed to generate SSH key: {e}{COLORS['RESET']}") + input("\nPress Enter to return to menu...") + return + + # Set consistent paths for providers + config['ssh_key_path'] = ssh_key_pub_path + if config['provider'] == 'aws': + config['aws_ssh_key_name'] = f"c2deploy_{config['deployment_id']}" # Print configuration (excluding sensitive data) for key, value in config.items(): @@ -1609,7 +1629,7 @@ def run_tests(config): def ssh_to_instance(config): - """SSH into the deployed instance with improved AWS key handling""" + """SSH into the deployed instance with improved key handling""" logging.info("Connecting to instance via SSH...") # Determine which IP to use based on deployment type @@ -1635,78 +1655,62 @@ def ssh_to_instance(config): print(f"{COLORS['RED']}No IP address found for {instance_type}. Cannot SSH.{COLORS['RESET']}") return False - # Get SSH key and user - with AWS-specific handling - ssh_key = config.get('ssh_key') - if config.get('provider') == 'aws': - # For AWS, always look for the .pem extension key - deployment_id = config.get('deployment_id', '') - aws_key_path = os.path.expanduser(f"~/.ssh/c2deploy_{deployment_id}.pem") - if os.path.exists(aws_key_path): - ssh_key = aws_key_path - logging.info(f"Using AWS key at {ssh_key}") - else: - # Try adding .pem to existing key path if it exists - potential_pem_key = f"{ssh_key}.pem" if ssh_key else "" - if potential_pem_key and os.path.exists(potential_pem_key): - ssh_key = potential_pem_key - logging.info(f"Found AWS key with .pem extension: {ssh_key}") + # Get the correct SSH key and user based on provider and deployment ID + deployment_id = config.get('deployment_id') + ssh_key = os.path.expanduser(f"~/.ssh/c2deploy_{deployment_id}") - if not ssh_key: - logging.error("No SSH key specified") - print(f"{COLORS['RED']}No SSH key specified. Cannot SSH.{COLORS['RESET']}") + # Handle specific provider variations + if config.get('provider') == 'aws': + # AWS-specific key handling - check with .pem suffix + pem_key = f"{ssh_key}.pem" + if os.path.exists(pem_key): + ssh_key = pem_key + + if not os.path.exists(ssh_key): + logging.error(f"SSH key not found at {ssh_key}") + print(f"{COLORS['RED']}SSH key not found at {ssh_key}. Cannot SSH.{COLORS['RESET']}") return False # Fix key permissions os.chmod(ssh_key, 0o600) - # Try multiple possible usernames + # Determine the correct username based on provider and instance type if config.get('ssh_user'): - ssh_users = [config.get('ssh_user')] + ssh_user = config.get('ssh_user') elif config['provider'] == 'aws': - # For AWS, try multiple common usernames - ssh_users = ['kali'] + ssh_user = config.get('ami_ssh_user', 'kali') elif config['provider'] == 'linode': - ssh_users = ['root'] + ssh_user = 'root' else: - ssh_users = [DEFAULT_SSH_USER.get(config['provider'], 'root')] + ssh_user = DEFAULT_SSH_USER.get(config['provider'], 'root') # Print SSH connection information print(f"\n{COLORS['CYAN']}SSH Connection Information:{COLORS['RESET']}") print(f" Host: {ip}") + print(f" User: {ssh_user}") print(f" Key: {ssh_key}") - print(f" Will try users: {', '.join(ssh_users)}") + print(f" Manual command: ssh -i {ssh_key} {ssh_user}@{ip}") - # Try each SSH user until one works - for ssh_user in ssh_users: - print(f"\n{COLORS['YELLOW']}Trying SSH with user: {ssh_user}{COLORS['RESET']}") - print(f" Manual SSH command: ssh -i {ssh_key} {ssh_user}@{ip}") - - # Build SSH command - ssh_cmd = [ - "ssh", - "-t", - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - "-o", "IdentitiesOnly=yes", - "-o", "ConnectTimeout=10", - "-i", ssh_key, - f"{ssh_user}@{ip}", - "tmux" - ] - - # Execute SSH command with timeout - try: - subprocess.run(ssh_cmd, timeout=60) - return True - except subprocess.TimeoutExpired: - print(f"{COLORS['YELLOW']}Connection with user {ssh_user} timed out, trying next user...{COLORS['RESET']}") - continue - except Exception as e: - print(f"{COLORS['YELLOW']}Connection with user {ssh_user} failed: {e}{COLORS['RESET']}") - continue + # Build SSH command + ssh_cmd = [ + "ssh", + "-t", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "IdentitiesOnly=yes", + "-o", "ConnectTimeout=10", + "-i", ssh_key, + f"{ssh_user}@{ip}", + "tmux" + ] - print(f"{COLORS['RED']}Failed to connect with any user. Check instance security group and key.{COLORS['RESET']}") - return False + # Execute SSH command + try: + subprocess.run(ssh_cmd) + return True + except Exception as e: + print(f"{COLORS['RED']}SSH connection failed: {e}{COLORS['RESET']}") + return False def cleanup_resources(config, interactive=True): """Clean up resources if deployment fails"""