293 lines
9.5 KiB
YAML
293 lines
9.5 KiB
YAML
---
|
|
# Security hardening tasks for C2 server
|
|
|
|
- name: Check if system is updated
|
|
apt:
|
|
update_cache: yes
|
|
register: apt_update_result
|
|
until: apt_update_result is success
|
|
retries: 5
|
|
delay: 5
|
|
|
|
- name: Install security packages (non-UFW)
|
|
apt:
|
|
name:
|
|
- fail2ban
|
|
- unattended-upgrades
|
|
- debsums
|
|
- aide
|
|
- rkhunter
|
|
- logrotate
|
|
state: present
|
|
register: package_install
|
|
until: package_install is success
|
|
retries: 3
|
|
delay: 5
|
|
|
|
# Skip UFW for AWS, use it for other providers
|
|
- name: Determine if using AWS provider
|
|
set_fact:
|
|
is_aws_provider: "{{ provider | default('unknown') == 'aws' }}"
|
|
|
|
# Only install and configure UFW for non-AWS providers
|
|
- name: Check if UFW is installed
|
|
command: which ufw
|
|
register: ufw_check
|
|
failed_when: false
|
|
changed_when: false
|
|
when: not is_aws_provider
|
|
|
|
- name: Install UFW if not present (non-AWS)
|
|
apt:
|
|
name: ufw
|
|
state: present
|
|
update_cache: yes
|
|
register: ufw_install
|
|
until: ufw_install is success
|
|
retries: 3
|
|
delay: 5
|
|
when: not is_aws_provider and ufw_check.rc != 0
|
|
|
|
- name: Configure UFW for non-AWS deployments
|
|
block:
|
|
- name: Configure UFW default policies
|
|
ufw:
|
|
state: enabled
|
|
policy: deny
|
|
direction: incoming
|
|
ignore_errors: yes
|
|
|
|
- name: Configure UFW for C2 server
|
|
block:
|
|
- name: Reset UFW to default deny
|
|
ufw:
|
|
state: enabled
|
|
policy: deny
|
|
direction: incoming
|
|
|
|
- name: Allow SSH only from operator IP
|
|
ufw:
|
|
rule: allow
|
|
port: "22"
|
|
src: "{{ operator_ip }}"
|
|
proto: tcp
|
|
|
|
- name: Allow Havoc Teamserver only from operator IP
|
|
ufw:
|
|
rule: allow
|
|
port: "{{ havoc_teamserver_port | default('40056') }}"
|
|
src: "{{ operator_ip }}"
|
|
proto: tcp
|
|
|
|
- name: Allow traffic only from redirector
|
|
ufw:
|
|
rule: allow
|
|
port: "{{ item }}"
|
|
src: "{{ redirector_ip }}"
|
|
proto: tcp
|
|
loop:
|
|
- "80"
|
|
- "443"
|
|
- "{{ havoc_http_port | default('8080') }}"
|
|
- "{{ havoc_https_port | default('9443') }}" # Updated from 443
|
|
- "{{ havoc_payload_port | default('8443') }}"
|
|
- "{{ gophish_admin_port }}"
|
|
- "{{ gophish_phish_port | default('8081') }}"
|
|
- "{{ tracker_port | default('5000') }}"
|
|
when: "'c2servers' in group_names"
|
|
|
|
- name: Configure UFW for redirector
|
|
block:
|
|
- name: Reset UFW to default deny
|
|
ufw:
|
|
state: enabled
|
|
policy: deny
|
|
direction: incoming
|
|
|
|
- name: Allow SSH only from operator IP
|
|
ufw:
|
|
rule: allow
|
|
port: "22"
|
|
src: "{{ operator_ip }}"
|
|
proto: tcp
|
|
|
|
- name: Allow public services from anywhere
|
|
ufw:
|
|
rule: allow
|
|
port: "{{ item }}"
|
|
proto: tcp
|
|
loop:
|
|
- "80"
|
|
- "443"
|
|
- "{{ shell_handler_port | default('4488') }}"
|
|
when: "'redirectors' in group_names"
|
|
# Only run UFW block if not AWS and UFW is available/installed
|
|
when: not is_aws_provider and (ufw_check.rc == 0 or ufw_install is success)
|
|
|
|
# Configure iptables fallback if UFW isn't available (non-AWS only)
|
|
- name: Configure basic iptables rules if UFW unavailable
|
|
block:
|
|
- name: Set up basic iptables rules for C2 server
|
|
shell: |
|
|
iptables -F
|
|
iptables -P INPUT DROP
|
|
iptables -P FORWARD DROP
|
|
iptables -P OUTPUT ACCEPT
|
|
# Allow established connections
|
|
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
# Allow SSH from operator
|
|
iptables -A INPUT -p tcp --dport 22 -s {{ operator_ip }} -j ACCEPT
|
|
# Allow Havoc teamserver from operator
|
|
iptables -A INPUT -p tcp --dport {{ havoc_teamserver_port | default(40056) }} -s {{ operator_ip }} -j ACCEPT
|
|
# Allow traffic from redirector
|
|
iptables -A INPUT -p tcp --dport 80 -s {{ redirector_ip }} -j ACCEPT
|
|
iptables -A INPUT -p tcp --dport 443 -s {{ redirector_ip }} -j ACCEPT
|
|
iptables -A INPUT -p tcp --dport {{ havoc_http_port | default(8080) }} -s {{ redirector_ip }} -j ACCEPT
|
|
iptables -A INPUT -p tcp --dport {{ havoc_https_port | default(443) }} -s {{ redirector_ip }} -j ACCEPT
|
|
iptables -A INPUT -p tcp --dport {{ gophish_admin_port }} -s {{ redirector_ip }} -j ACCEPT
|
|
when: "'c2servers' in group_names"
|
|
when: not is_aws_provider and (ufw_check.rc != 0 and (ufw_install is undefined or ufw_install is failed))
|
|
|
|
# SSH Hardening - applies to all providers
|
|
- name: Harden SSH configuration
|
|
lineinfile:
|
|
path: /etc/ssh/sshd_config
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
state: present
|
|
loop:
|
|
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
|
|
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
|
|
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
|
|
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
|
|
- { regexp: '^#?AllowTcpForwarding', line: 'AllowTcpForwarding yes' }
|
|
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
|
|
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' }
|
|
register: ssh_config_updated
|
|
|
|
- name: Configure system resource limits
|
|
pam_limits:
|
|
domain: "*"
|
|
limit_type: "{{ item.limit_type }}"
|
|
limit_item: "{{ item.limit_item }}"
|
|
value: "{{ item.value }}"
|
|
loop:
|
|
- { limit_type: soft, limit_item: nofile, value: 65535 }
|
|
- { limit_type: hard, limit_item: nofile, value: 65535 }
|
|
- { limit_type: soft, limit_item: nproc, value: 4096 }
|
|
- { limit_type: hard, limit_item: nproc, value: 4096 }
|
|
|
|
# Set up fail2ban - applies to all providers
|
|
- name: Set up fail2ban SSH jail
|
|
copy:
|
|
dest: /etc/fail2ban/jail.d/sshd.conf
|
|
content: |
|
|
[sshd]
|
|
enabled = true
|
|
port = ssh
|
|
filter = sshd
|
|
logpath = /var/log/auth.log
|
|
maxretry = 5
|
|
bantime = 3600
|
|
mode: '0644'
|
|
register: fail2ban_config_updated
|
|
|
|
- name: Enable automatic security updates
|
|
copy:
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
content: |
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
APT::Periodic::AutocleanInterval "7";
|
|
mode: '0644'
|
|
|
|
- name: Create log cleaning script if zero-logs is enabled
|
|
copy:
|
|
dest: /root/Tools/clean-logs.sh
|
|
content: |
|
|
#!/bin/bash
|
|
# Log cleaning script
|
|
echo "Cleaning system logs at $(date)"
|
|
|
|
# Clear auth logs
|
|
echo "" > /var/log/auth.log
|
|
echo "" > /var/log/auth.log.1
|
|
|
|
# Clear syslog
|
|
echo "" > /var/log/syslog
|
|
echo "" > /var/log/syslog.1
|
|
|
|
# Clear application specific logs
|
|
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
|
|
|
# Clear bash history
|
|
cat /dev/null > ~/.bash_history
|
|
history -c
|
|
|
|
echo "Log cleaning completed at $(date)"
|
|
mode: '0700'
|
|
when: zero_logs | default(false) | bool
|
|
|
|
# Service restart handlers
|
|
- name: Restart SSH service if configuration changed
|
|
service:
|
|
name: ssh
|
|
state: restarted
|
|
when: ssh_config_updated.changed
|
|
|
|
- name: Check if fail2ban service exists
|
|
stat:
|
|
path: "/etc/init.d/fail2ban"
|
|
register: fail2ban_service_stat
|
|
|
|
- name: Restart fail2ban service if installed and configuration changed
|
|
service:
|
|
name: fail2ban
|
|
state: restarted
|
|
when: fail2ban_service_stat.stat.exists and fail2ban_config_updated.changed
|
|
|
|
# AWS-specific security updates - Only execute when provider is AWS
|
|
- name: Check if we're running on AWS provider
|
|
set_fact:
|
|
is_aws_provider: "{{ hostvars['localhost']['provider'] | default('') == 'aws' }}"
|
|
|
|
- name: AWS-specific security updates
|
|
block:
|
|
- name: Get redirector security group information
|
|
block:
|
|
- name: Check if infrastructure state file exists
|
|
stat:
|
|
path: "{{ playbook_dir }}/../infrastructure_state_{{ hostvars['localhost']['deployment_id'] }}.json"
|
|
register: redirector_state_file
|
|
|
|
- name: Load redirector state if available
|
|
include_vars:
|
|
file: "{{ playbook_dir }}/../infrastructure_state_{{ hostvars['localhost']['deployment_id'] }}.json"
|
|
name: redirector_state
|
|
when: redirector_state_file.stat.exists
|
|
|
|
- name: Explicitly confirm we're running on AWS
|
|
debug:
|
|
msg: "Updating AWS security group for redirector to allow SSH from C2"
|
|
|
|
- name: Update redirector security group to allow SSH from C2
|
|
delegate_to: localhost
|
|
amazon.aws.ec2_security_group_rule:
|
|
security_group_id: "{{ redirector_state.security_group_id }}"
|
|
region: "{{ redirector_state.region | default(aws_region) }}"
|
|
rule:
|
|
proto: tcp
|
|
ports: 22
|
|
cidr_ip: "{{ ansible_host }}/32"
|
|
state: present
|
|
when: redirector_state is defined and redirector_state.security_group_id is defined
|
|
register: sg_update
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ hostvars['localhost']['aws_access_key'] }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ hostvars['localhost']['aws_secret_key'] }}"
|
|
ignore_errors: yes
|
|
|
|
- name: Display security group update result
|
|
debug:
|
|
msg: "{{ 'Redirector security group updated to allow SSH from C2 (' + ansible_host + ')' if sg_update is success else 'Failed to update security group: ' + (sg_update.msg | default('unknown error')) }}"
|
|
when: is_aws_provider | bool |