Initial public portfolio release

Sanitized version of red team infrastructure automation platform.
Operational content (implant pipelines, lures, credential capture)
replaced with documented stubs. Architecture and infrastructure
automation code intact.
This commit is contained in:
Operator
2026-06-23 16:12:14 -04:00
commit 0799bfbae8
239 changed files with 40012 additions and 0 deletions
+634
View File
@@ -0,0 +1,634 @@
---
# FlokiNET full deployment playbook (C2 + Redirector)
- name: Prepare FlokiNET infrastructure deployment
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
# Generate random shell handler port if not provided
shell_handler_port: "{{ shell_handler_port | default(4000 + 60000 | random) }}"
redirector_subdomain: "{{ redirector_subdomain | default('cdn') }}"
c2_subdomain: "{{ c2_subdomain | default('mail') }}"
tasks:
- name: Validate required FlokiNET configuration
assert:
that:
- redirector_ip is defined and redirector_ip != ""
- c2_ip is defined and c2_ip != ""
fail_msg: "FlokiNET requires both redirector_ip and c2_ip. Set these values in vars.yaml or via command line arguments."
- name: Add redirector to inventory
add_host:
name: "redirector"
groups: "redirectors"
ansible_host: "{{ redirector_ip }}"
ansible_user: "{{ ssh_user | default('root') }}"
ansible_ssh_private_key_file: "{{ ssh_key_path | replace('.pub', '') }}"
ansible_ssh_port: "{{ ssh_port | default(22) }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Add C2 server to inventory
add_host:
name: "c2"
groups: "c2servers"
ansible_host: "{{ c2_ip }}"
ansible_user: "{{ ssh_user | default('root') }}"
ansible_ssh_private_key_file: "{{ ssh_key_path | replace('.pub', '') }}"
ansible_ssh_port: "{{ ssh_port | default(22) }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Verify SSH connection to redirector
wait_for:
host: "{{ redirector_ip }}"
port: "{{ ssh_port | default(22) }}"
delay: 10
timeout: 60
state: started
ignore_errors: true
- name: Verify SSH connection to C2 server
wait_for:
host: "{{ c2_ip }}"
port: "{{ ssh_port | default(22) }}"
delay: 10
timeout: 60
state: started
ignore_errors: true
- name: Configure FlokiNET redirector
hosts: redirectors
become: true
gather_facts: true
vars_files:
- vars.yaml
tasks:
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Set hostname
hostname:
name: "redirector"
- name: Update apt cache
apt:
update_cache: yes
- name: Upgrade all packages
apt:
upgrade: dist
- name: Install base utilities and tools via apt
apt:
name:
- git
- wget
- curl
- unzip
- python3-pip
- python3-virtualenv
- tmux
- pipx
- nmap
- tcpdump
- nginx
- certbot
- python3-certbot-nginx
- socat
- netcat-openbsd
- secure-delete
state: present
- name: Set a custom MOTD
template:
src: motd-flokinet.j2
dest: /etc/motd
owner: root
group: root
mode: '0644'
vars:
letsencrypt_email: "{{ letsencrypt_email }}"
domain: "{{ domain }}"
redirector_subdomain: "{{ redirector_subdomain }}"
- name: Create directories for operational scripts
file:
path: "{{ item }}"
state: directory
mode: '0700'
owner: root
group: root
with_items:
- /opt/c2
- /opt/shell-handler
- name: Copy clean-logs.sh script
copy:
src: "../../common/files/clean-logs.sh"
dest: /opt/c2/clean-logs.sh
mode: '0700'
owner: root
group: root
- name: Copy secure-exit.sh script
copy:
src: "../../common/files/secure-exit.sh"
dest: /opt/c2/secure-exit.sh
mode: '0700'
owner: root
group: root
- name: Copy shell handler script
copy:
src: "../../common/files/persistent-listener.sh"
dest: /opt/shell-handler/persistent-listener.sh
mode: '0700'
owner: root
group: root
- name: Configure shell handler script with C2 IP
replace:
path: /opt/shell-handler/persistent-listener.sh
regexp: 'C2_HOST="127.0.0.1"'
replace: 'C2_HOST="{{ c2_ip }}"'
- name: Configure shell handler script with listening port
replace:
path: /opt/shell-handler/persistent-listener.sh
regexp: 'LISTEN_PORT=4444'
replace: 'LISTEN_PORT={{ shell_handler_port }}'
- name: Create shell handler service
template:
src: shell-handler.service.j2
dest: /etc/systemd/system/shell-handler.service
mode: '0644'
owner: root
group: root
- name: Configure NGINX for zero-logging if enabled
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: '0644'
owner: root
group: root
when: zero_logs | bool
- name: Configure NGINX for C2 redirection
template:
src: redirector-site.conf.j2
dest: /etc/nginx/sites-available/default
mode: '0644'
owner: root
group: root
- name: Create legitimate-looking index.html
template:
src: redirector-index.html.j2
dest: /var/www/html/index.html
mode: '0644'
owner: www-data
group: www-data
- name: Start and enable shell handler service
systemd:
name: shell-handler
state: started
enabled: yes
daemon_reload: yes
- name: Set up cron job for log cleaning if zero-logs enabled
cron:
name: "Clean logs"
minute: "0"
hour: "*/6"
job: "/opt/c2/clean-logs.sh > /dev/null 2>&1"
when: zero_logs | bool
- name: Install Let's Encrypt certificate if domain specified
shell: |
certbot --nginx -d {{ redirector_subdomain }}.{{ domain }} --non-interactive --agree-tos -m {{ letsencrypt_email }}
args:
creates: /etc/letsencrypt/live/{{ redirector_subdomain }}.{{ domain }}/fullchain.pem
when: domain != "example.com"
- name: Restart NGINX
systemd:
name: nginx
state: restarted
- name: FlokiNET-specific security configurations
include_tasks: flokinet-security.yml
when: enable_hardened_security | default(true)
- name: Configure FlokiNET C2 server
hosts: c2servers
become: true
gather_facts: true
vars_files:
- vars.yaml
tasks:
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Set hostname
hostname:
name: "c2"
- name: Update apt cache
apt:
update_cache: yes
- name: Upgrade all packages
apt:
upgrade: dist
- name: Set a custom MOTD
template:
src: motd-flokinet.j2
dest: /etc/motd
owner: root
group: root
mode: '0644'
vars:
letsencrypt_email: "{{ letsencrypt_email }}"
domain: "{{ domain }}"
c2_subdomain: "{{ c2_subdomain }}"
- name: Install base utilities and tools via apt
apt:
name:
- git
- wget
- curl
- unzip
- python3-pip
- python3-virtualenv
- tmux
- pipx
- nmap
- tcpdump
- hydra
- john
- hashcat
- sqlmap
- gobuster
- dirb
- enum4linux
- dnsenum
- seclists
- responder
- golang
- proxychains
- tor
- crackmapexec
- jq
- unzip
- postfix
- certbot
- opendkim
- opendkim-tools
- dovecot-core
- dovecot-imapd
- dovecot-pop3d
- dovecot-sieve
- dovecot-managesieved
- yq
state: present
- name: Create Tools directory
file:
path: /root/Tools
state: directory
owner: root
group: root
mode: '0755'
- name: Ensure pipx path is configured
shell: |
pipx ensurepath
args:
executable: /bin/bash
- name: Install tools via pipx
shell: |
export PATH=$PATH:/root/.local/bin
pipx ensurepath
pipx install git+https://github.com/Pennyw0rth/NetExec
pipx install git+https://github.com/blacklanternsecurity/TREVORspray
pipx install impacket
args:
executable: /bin/bash
- name: Download Kerbrute
shell: |
mkdir -p ~/Tools/Kerbrute
wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_amd64 -O ~/Tools/Kerbrute/kerbrute
chmod +x ~/Tools/Kerbrute/kerbrute
args:
executable: /bin/bash
creates: /root/Tools/Kerbrute/kerbrute
- name: Clone SharpCollection nightly builds
git:
repo: https://github.com/Flangvik/SharpCollection.git
dest: ~/Tools/SharpCollection
version: master
ignore_errors: yes
- name: Clone PEASS-ng
git:
repo: https://github.com/carlospolop/PEASS-ng.git
dest: ~/Tools/PEASS-ng
ignore_errors: yes
- name: Clone MailSniper
git:
repo: https://github.com/dafthack/MailSniper.git
dest: ~/Tools/MailSniper
ignore_errors: yes
- name: Clone Inveigh
git:
repo: https://github.com/Kevin-Robertson/Inveigh.git
dest: ~/Tools/Inveigh
ignore_errors: yes
- name: Install Sliver C2 server
shell: |
curl https://sliver.sh/install | bash
systemctl enable sliver
systemctl start sliver
- name: Install Metasploit Framework (Nightly Build)
shell: |
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > ~/Tools/msfinstall
chmod 755 ~/Tools/msfinstall
~/Tools/msfinstall
args:
executable: /bin/bash
creates: /usr/bin/msfconsole
- name: Grab GoPhish
shell: |
curl -L "$(curl -s https://api.github.com/repos/gophish/gophish/releases/latest | jq -r '.assets[] | select(.browser_download_url | contains("linux-64bit.zip")) | .browser_download_url')" -o ~/Tools/gophish.zip
unzip ~/Tools/gophish.zip -d ~/Tools/gophish
rm -rf ~/Tools/gophish.zip
chmod +x ~/Tools/gophish/gophish
args:
creates: /root/Tools/gophish/gophish
- name: Deploy Gophish config.json with custom admin port
template:
src: gophish-config.j2
dest: ~/Tools/gophish/config.json
owner: root
group: root
mode: '0644'
vars:
gophish_admin_port: "{{ gophish_admin_port }}"
domain: "{{ domain }}"
- name: Configure Postfix main.cf
lineinfile:
path: /etc/postfix/main.cf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^myhostname', line: "myhostname = mail.{{ domain }}" }
- { regexp: '^mydomain', line: "mydomain = {{ domain }}" }
- { regexp: '^myorigin', line: "myorigin = $mydomain" }
- { regexp: '^inet_interfaces', line: "inet_interfaces = all" }
- { regexp: '^inet_protocols', line: "inet_protocols = ipv4" }
- { regexp: '^smtpd_banner', line: "smtpd_banner = $myhostname ESMTP $mail_name" }
- { regexp: '^mynetworks', line: "mynetworks = 127.0.0.0/8 [::1]/128" }
- { regexp: '^relay_domains', line: "relay_domains = $mydestination" }
- { regexp: '^smtpd_tls_cert_file', line: "smtpd_tls_cert_file = /etc/letsencrypt/live/{{ domain }}/fullchain.pem" }
- { regexp: '^smtpd_tls_key_file', line: "smtpd_tls_key_file = /etc/letsencrypt/live/{{ domain }}/privkey.pem" }
- { regexp: '^smtpd_tls_security_level', line: "smtpd_tls_security_level = encrypt" }
- { regexp: '^smtpd_tls_session_cache_database', line: "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache" }
- { regexp: '^smtp_tls_session_cache_database', line: "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache" }
- { regexp: '^smtpd_use_tls', line: "smtpd_use_tls = yes" }
- { regexp: '^smtpd_tls_auth_only', line: "smtpd_tls_auth_only = yes" }
- { regexp: '^milter_default_action', line: "milter_default_action = accept" }
- { regexp: '^milter_protocol', line: "milter_protocol = 6" }
- { regexp: '^smtpd_milters', line: "smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock" }
- { regexp: '^non_smtpd_milters', line: "non_smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock" }
- name: Configure OpenDKIM
lineinfile:
path: /etc/opendkim.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^Domain', line: "Domain {{ domain }}" }
- { regexp: '^KeyFile', line: "KeyFile /etc/opendkim/keys/{{ domain }}/mail.private" }
- { regexp: '^Selector', line: "Selector mail" }
- { regexp: '^Socket', line: "Socket local:/var/spool/postfix/opendkim/opendkim.sock" }
- { regexp: '^Syslog', line: "Syslog yes" }
- { regexp: '^UMask', line: "UMask 002" }
- { regexp: '^Mode', line: "Mode sv" }
- name: Create DKIM directory
file:
path: /etc/opendkim/keys/{{ domain }}
state: directory
owner: opendkim
group: opendkim
mode: 0700
- name: Generate DKIM keys
command: >
opendkim-genkey -D /etc/opendkim/keys/{{ domain }} -d {{ domain }} -s mail
args:
creates: /etc/opendkim/keys/{{ domain }}/mail.private
- name: Set permissions for DKIM keys
file:
path: /etc/opendkim/keys/{{ domain }}/mail.private
owner: opendkim
group: opendkim
mode: 0600
- name: Configure OpenDKIM TrustedHosts
copy:
content: |
127.0.0.1
::1
localhost
{{ domain }}
dest: /etc/opendkim/TrustedHosts
owner: opendkim
group: opendkim
mode: 0644
- name: Enable submission port (587) in master.cf
blockinfile:
path: /etc/postfix/master.cf
insertafter: '^#submission'
block: |
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
- name: Configure Dovecot for Postfix SASL
blockinfile:
path: /etc/dovecot/conf.d/10-master.conf
insertafter: '^service auth {'
block: |
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
- name: Set Dovecot auth_mechanisms
lineinfile:
path: /etc/dovecot/conf.d/10-auth.conf
regexp: '^auth_mechanisms'
line: 'auth_mechanisms = plain login'
- name: Create Dovecot password file for SASL authentication
file:
path: /etc/dovecot/passwd
state: touch
mode: '0600'
owner: dovecot
group: dovecot
- name: Add SMTP auth user to Dovecot
lineinfile:
path: /etc/dovecot/passwd
line: "{{ smtp_auth_user }}:{{ smtp_auth_pass | password_hash('sha512_crypt') }}"
- name: Disable system auth and use passwd-file
lineinfile:
path: /etc/dovecot/conf.d/10-auth.conf
regexp: '^!include auth-system.conf.ext'
line: '#!include auth-system.conf.ext'
- name: Add auth-passwdfile configuration
blockinfile:
path: /etc/dovecot/conf.d/10-auth.conf
insertafter: '^auth_mechanisms ='
block: |
passdb {
driver = passwd-file
args = scheme=sha512_crypt /etc/dovecot/passwd
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/var/vmail/%u
}
- name: Create vmail user/group
group:
name: vmail
gid: 5000
state: present
- name: Create vmail user
user:
name: vmail
uid: 5000
group: vmail
create_home: no
- name: Create directories for operational scripts
file:
path: "{{ item }}"
state: directory
mode: '0700'
owner: root
group: root
with_items:
- /opt/c2
- /opt/beacons
- name: Copy clean-logs.sh script
copy:
src: "../../common/files/clean-logs.sh"
dest: /opt/c2/clean-logs.sh
mode: '0700'
owner: root
group: root
- name: Copy secure-exit.sh script
copy:
src: "../../common/files/secure-exit.sh"
dest: /opt/c2/secure-exit.sh
mode: '0700'
owner: root
group: root
- name: Copy serve-beacons.sh script
copy:
src: "../../common/files/serve-beacons.sh"
dest: /opt/c2/serve-beacons.sh
mode: '0700'
owner: root
group: root
- name: Configure serve-beacons.sh with C2 IP
replace:
path: /opt/c2/serve-beacons.sh
regexp: 'C2_HOST=.*'
replace: 'C2_HOST="{{ c2_ip }}"'
- name: Set up cron job for log cleaning if zero-logs enabled
cron:
name: "Clean logs"
minute: "0"
hour: "*/6"
job: "/opt/c2/clean-logs.sh > /dev/null 2>&1"
when: zero_logs | bool
- name: Install Let's Encrypt certificate if domain specified
shell: |
certbot certonly --standalone -d {{ c2_subdomain }}.{{ domain }} --non-interactive --agree-tos -m {{ letsencrypt_email }}
args:
creates: /etc/letsencrypt/live/{{ c2_subdomain }}.{{ domain }}/fullchain.pem
when: domain != "example.com"
- name: Restart Postfix
service:
name: postfix
state: restarted
- name: Restart Dovecot
service:
name: dovecot
state: restarted
- name: FlokiNET-specific security configurations
include_tasks: flokinet-security.yml
when: enable_hardened_security | default(true)
- name: Print deployment summary
debug:
msg:
- "FlokiNET Deployment Complete!"
- "-------------------------------"
- "C2 Server Domain: {{ c2_subdomain }}.{{ domain }} (Update DNS A record)"
- "Redirector Domain: {{ redirector_subdomain }}.{{ domain }} (Update DNS A record)"
- "Shell Handler Port: {{ shell_handler_port }}"
- "GoPhish Admin Port: {{ gophish_admin_port }}"
when: not disable_summary | default(false)
+101
View File
@@ -0,0 +1,101 @@
---
# FlokiNET C2-only Configuration Playbook
# Note: FlokiNET requires pre-provisioned servers
- name: Prepare FlokiNET C2 configuration
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
c2_subdomain: "{{ c2_subdomain | default('mail') }}"
tasks:
- name: Validate required FlokiNET configuration
assert:
that:
- c2_ip is defined and c2_ip != ""
fail_msg: "FlokiNET requires C2 IP address. Set c2_ip in vars.yaml or via --flokinet-c2-ip."
- name: Add C2 to inventory
add_host:
name: "c2"
groups: "c2servers"
ansible_host: "{{ c2_ip }}"
ansible_user: "{{ ssh_user | default('root') }}"
ansible_ssh_private_key_file: "{{ ssh_key_path | replace('.pub', '') }}"
ansible_ssh_port: "{{ ssh_port | default(22) }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Verify SSH connection to C2
wait_for:
host: "{{ c2_ip }}"
port: "{{ ssh_port | default(22) }}"
delay: 10
timeout: 60
state: started
ignore_errors: true
- name: Provision FlokiNET C2 server
hosts: c2servers
become: true
gather_facts: true
vars_files:
- vars.yaml
tasks:
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Set hostname
hostname:
name: "c2"
- name: Update apt cache
apt:
update_cache: yes
- name: Upgrade all packages
apt:
upgrade: dist
- name: Install base security packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- unattended-upgrades
- ufw
- fail2ban
- secure-delete
state: present
- name: Include common security hardening tasks
include_tasks: "../../common/tasks/security_hardening.yml"
- name: Include common tool installation tasks
include_tasks: "../../common/tasks/install_tools.yml"
- name: Include common C2 configuration tasks
include_tasks: "../../modules/c2/tasks/configure_c2.yml"
- name: Include common mail server configuration tasks
include_tasks: "../../common/tasks/configure_mail.yml"
- name: Print deployment summary
debug:
msg:
- "C2 Server Configuration Complete!"
- "-------------------------------"
- "C2 Server IP: {{ ansible_host }}"
- "C2 Server Domain: {{ c2_subdomain }}.{{ domain }} (Update DNS A record)"
- "GoPhish Admin Port: {{ gophish_admin_port }}"
when: not disable_summary | default(false)
+75
View File
@@ -0,0 +1,75 @@
---
# FlokiNET Cleanup Playbook
# Used for documentation since FlokiNET requires manual cleanup through their interface
- name: Document FlokiNET cleanup procedure
hosts: localhost
connection: local
gather_facts: false
vars_files:
- vars.yaml
vars:
cleanup_redirector: "{{ (redirector_ip is defined and redirector_ip != '') | ternary(true, false) }}"
cleanup_c2: "{{ (c2_ip is defined and c2_ip != '') | ternary(true, false) }}"
confirm_cleanup: false # Skip confirmation in automated teardown
tasks:
- name: Confirm cleanup if required
pause:
prompt: "WARNING: This script provides guidance for manual FlokiNET cleanup. Type 'yes' to continue"
register: confirmation
when: confirm_cleanup
- name: Exit if not confirmed
meta: end_play
when: confirm_cleanup and confirmation.user_input != 'yes'
- name: Display cleanup instructions
debug:
msg:
- "FlokiNET Cleanup Instructions"
- "========================================"
- "Since FlokiNET resources must be manually terminated through their control panel,"
- "this playbook provides guidance on the steps needed for cleanup."
- ""
- "Resources to clean up:"
- "{{ cleanup_redirector | ternary('- Redirector server: ' + redirector_ip, '') }}"
- "{{ cleanup_c2 | ternary('- C2 server: ' + c2_ip, '') }}"
- ""
- "Steps to terminate FlokiNET servers:"
- "1. Log in to your FlokiNET control panel"
- "2. Navigate to the Virtual Servers section"
- "3. Select each server from the list"
- "4. Click 'Terminate' and confirm termination"
- ""
- "For security, consider also:"
- "- Manually executing the secure-exit.sh script on each server before termination"
- "- Removing DNS records associated with these servers"
- "- Ensuring any SSH keys used for these servers are removed or rotated"
- name: Remove SSH key file if it's not a shared key
file:
path: "{{ ssh_key_path | replace('.pub', '') }}"
state: absent
when:
- ssh_key_path is defined
- ssh_key_path is search('c2deploy_')
ignore_errors: true
- name: Remove SSH public key file if it's not a shared key
file:
path: "{{ ssh_key_path }}"
state: absent
when:
- ssh_key_path is defined
- ssh_key_path is search('c2deploy_')
ignore_errors: true
- name: Pre-termination security recommendations
debug:
msg:
- "Security Recommendations before manual termination:"
- "------------------------------------------------"
- " SSH Command: ssh -i {{ ssh_key_path | replace('.pub', '') }} {{ ssh_user | default('root') }}@SERVER_IP -p {{ ssh_port | default('22') }}"
- " Then run: /opt/c2/secure-exit.sh"
when: (cleanup_redirector or cleanup_c2) and ssh_key_path is defined
+167
View File
@@ -0,0 +1,167 @@
---
# FlokiNET-specific security tasks
# Enhanced security features for FlokiNET servers
- name: Configure FlokiNET networking for maximum anonymity
lineinfile:
path: /etc/sysctl.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: present
with_items:
- { regexp: '^net.ipv4.tcp_timestamps', line: 'net.ipv4.tcp_timestamps = 0' }
- { regexp: '^net.ipv4.tcp_syncookies', line: 'net.ipv4.tcp_syncookies = 1' }
- { regexp: '^net.ipv4.conf.all.accept_redirects', line: 'net.ipv4.conf.all.accept_redirects = 0' }
- { regexp: '^net.ipv6.conf.all.accept_redirects', line: 'net.ipv6.conf.all.accept_redirects = 0' }
- { regexp: '^net.ipv4.conf.all.send_redirects', line: 'net.ipv4.conf.all.send_redirects = 0' }
- { regexp: '^net.ipv4.conf.all.accept_source_route', line: 'net.ipv4.conf.all.accept_source_route = 0' }
- { regexp: '^net.ipv6.conf.all.accept_source_route', line: 'net.ipv6.conf.all.accept_source_route = 0' }
- { regexp: '^net.ipv4.conf.all.log_martians', line: 'net.ipv4.conf.all.log_martians = 1' }
notify: Apply sysctl settings
- name: Install Tor for anonymous outbound connections
apt:
name:
- tor
- torsocks
- obfs4proxy
state: present
update_cache: yes
register: tor_installed
when: use_tor_proxy | default(true)
- name: Create Tor configuration directory
file:
path: /etc/tor
state: directory
mode: '0755'
when: use_tor_proxy | default(true) and tor_installed is succeeded
- name: Configure Tor for hardened privacy settings
template:
src: torrc.j2
dest: /etc/tor/torrc
owner: root
group: root
mode: '0644'
notify: Restart Tor service
when: use_tor_proxy | default(true) and tor_installed is succeeded
- name: Configure ProxyChains for routing through Tor
template:
src: proxychains.conf.j2
dest: /etc/proxychains.conf
owner: root
group: root
mode: '0644'
when: use_tor_proxy | default(true) and tor_installed is succeeded
- name: Install iptables-persistent for firewall persistence
apt:
name: iptables-persistent
state: present
update_cache: yes
- name: Configure hardened iptables rules for FlokiNET
template:
src: iptables-rules.j2
dest: /etc/iptables/rules.v4
owner: root
group: root
mode: '0644'
notify: Apply iptables rules
- name: Create SSH security hardening script
template:
src: secure-ssh.sh.j2
dest: /opt/c2/secure-ssh.sh
owner: root
group: root
mode: '0700'
when: use_hardened_ssh | default(true)
- name: Run SSH security hardening script
command: /opt/c2/secure-ssh.sh
args:
creates: /opt/c2/.ssh_hardened
when: use_hardened_ssh | default(true)
- name: Install timezone data
apt:
name: tzdata
state: present
- name: Set timezone to UTC
timezone:
name: UTC
- name: Configure DNS to use secure DNS servers
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'
when: enable_dns_encryption | default(true)
- name: Create directory for DNS cache configuration
file:
path: /etc/systemd/resolved.conf.d
state: directory
mode: '0755'
when: enable_dns_encryption | default(true)
- name: Configure DNS caching with DNSCrypt
template:
src: dnscrypt.conf.j2
dest: /etc/systemd/resolved.conf.d/dnscrypt.conf
owner: root
group: root
mode: '0644'
notify: Restart systemd-resolved
when: enable_dns_encryption | default(true)
# Configure memory security if enabled
- name: Set memory security measures
lineinfile:
path: /etc/sysctl.conf
line: "{{ item }}"
state: present
with_items:
- "vm.swappiness=0"
- "kernel.randomize_va_space=2"
when: secure_memory | default(true)
notify: Apply sysctl settings
# Configure history settings if disabled
- name: Disable system history
lineinfile:
path: "{{ item.file }}"
line: "{{ item.line }}"
state: present
create: yes
with_nested:
- [{ file: '/etc/profile' }, { file: '/root/.bashrc' }]
- [{ line: 'export HISTFILESIZE=0' }, { line: 'export HISTSIZE=0' }, { line: 'unset HISTFILE' }]
when: disable_history | default(true)
handlers:
- name: Apply sysctl settings
command: sysctl -p
- name: Restart Tor service
service:
name: tor
state: restarted
enabled: yes
when: use_tor_proxy | default(true)
- name: Apply iptables rules
command: iptables-restore < /etc/iptables/rules.v4
- name: Restart systemd-resolved
service:
name: systemd-resolved
state: restarted
enabled: yes
when: enable_dns_encryption | default(true)
+119
View File
@@ -0,0 +1,119 @@
---
# FlokiNET-specific phishing infrastructure tasks
- name: Create FlokiNET instances for phishing
uri:
url: "{{ flokinet_api_endpoint }}/instances"
method: POST
headers:
Authorization: "Bearer {{ flokinet_api_token }}"
Content-Type: "application/json"
body_format: json
body:
plan: "{{ flokinet_plan | default('basic') }}"
region: "{{ flokinet_region | default('romania') }}"
os: "{{ flokinet_os | default('ubuntu-22.04') }}"
hostname: "{{ deployment_id }}-{{ item }}"
ssh_keys:
- "{{ ssh_public_key }}"
tags:
- "c2itall"
- "phishing"
- "{{ deployment_id }}"
status_code: [200, 201]
register: flokinet_instances
loop: "{{ deployment_components }}"
when: item in deployment_components
- name: Wait for instances to be active
uri:
url: "{{ flokinet_api_endpoint }}/instances/{{ item.json.id }}"
method: GET
headers:
Authorization: "Bearer {{ flokinet_api_token }}"
register: instance_status
until: instance_status.json.status == "active"
retries: 30
delay: 10
loop: "{{ flokinet_instances.results }}"
when: flokinet_instances.results is defined
- name: Get instance details
uri:
url: "{{ flokinet_api_endpoint }}/instances/{{ item.json.id }}"
method: GET
headers:
Authorization: "Bearer {{ flokinet_api_token }}"
register: instance_details
loop: "{{ flokinet_instances.results }}"
when: flokinet_instances.results is defined
- name: Set instance facts
set_fact:
phishing_instances: >-
{% set instances = [] %}
{% for result in instance_details.results %}
{% set instance = {
'id': result.json.id,
'hostname': result.json.hostname,
'ip': result.json.main_ip,
'region': result.json.region,
'plan': result.json.plan,
'status': result.json.status
} %}
{% set _ = instances.append(instance) %}
{% endfor %}
{{ instances }}
when: instance_details.results is defined
- name: Wait for SSH connectivity
wait_for:
host: "{{ item.ip }}"
port: 22
delay: 30
timeout: 300
loop: "{{ phishing_instances }}"
when: phishing_instances is defined
- name: Update instance inventory
add_host:
name: "{{ item.ip }}"
groups: "phishing_{{ item.hostname.split('-')[-1] }}"
ansible_host: "{{ item.ip }}"
ansible_user: root
ansible_ssh_private_key_file: "{{ ssh_private_key_path }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
instance_id: "{{ item.id }}"
instance_hostname: "{{ item.hostname }}"
provider: "flokinet"
loop: "{{ phishing_instances }}"
when: phishing_instances is defined
- name: Configure Gophish servers
include_tasks: ../common/configure_gophish.yml
when: "'gophish' in deployment_components"
- name: Configure MTA fronts
include_tasks: ../common/configure_mta.yml
when: "'mta_front' in deployment_components"
- name: Configure redirectors
include_tasks: ../common/configure_redirector.yml
when: "'redirector' in deployment_components"
- name: Configure web servers
include_tasks: ../common/configure_webserver.yml
when: "'webserver' in deployment_components"
- name: Display deployment summary
debug:
msg: |
🎯 FlokiNET Phishing Infrastructure Deployed
{{ phishing_instances | length }} instances created
Provider: FlokiNET
Deployment ID: {{ deployment_id }}
Instances:
{% for instance in phishing_instances %}
- {{ instance.hostname }}: {{ instance.ip }} ({{ instance.plan }})
{% endfor %}
when: phishing_instances is defined
+65
View File
@@ -0,0 +1,65 @@
---
# Phishing infrastructure deployment playbook
# This is a comprehensive playbook that handles all phishing components
- name: Deploy Phishing Infrastructure
hosts: localhost
gather_facts: true
connection: local
vars_files:
- vars.yaml
tasks:
- name: Display deployment information
debug:
msg: |
Deploying phishing infrastructure
Deployment ID: {{ deployment_id }}
Provider: {{ provider }}
Domain: {{ domain | default(phishing_domain) }}
Components: {{ deployment_type }}
- name: Set deployment facts
set_fact:
deployment_timestamp: "{{ ansible_date_time.epoch }}"
phishing_deployment_results: {}
deployment_components: >-
{% set components = [] %}
{% if deployment_type in ['gophish_only', 'basic_phishing', 'advanced_phishing', 'full_phishing', 'fedramp_phishing'] %}
{% set _ = components.append('gophish') %}
{% endif %}
{% if deployment_type in ['mta_front_only', 'basic_phishing', 'advanced_phishing', 'full_phishing', 'ephemeral_mta'] %}
{% set _ = components.append('mta_front') %}
{% endif %}
{% if deployment_type in ['phishing_redirector_only', 'advanced_phishing', 'full_phishing'] %}
{% set _ = components.append('redirector') %}
{% endif %}
{% if deployment_type in ['phishing_webserver_only', 'full_phishing', 'fedramp_phishing'] %}
{% set _ = components.append('webserver') %}
{% endif %}
{{ components }}
- name: Include provider-specific tasks
include_tasks: "{{ provider }}_phishing.yml"
when: deployment_components | length > 0
- name: Ensure logs directory exists
file:
path: "../../logs"
state: directory
mode: '0755'
delegate_to: localhost
- name: Save deployment information
template:
src: phishing_deployment_info.j2
dest: "{{ playbook_dir }}/logs/phishing_deployment_{{ deployment_id }}.json"
delegate_to: localhost
- name: Display success message
debug:
msg: |
✅ Phishing infrastructure deployment completed
Deployment ID: {{ deployment_id }}
Components deployed: {{ deployment_components | join(', ') }}
Check logs/phishing_deployment_{{ deployment_id }}.json for details
+134
View File
@@ -0,0 +1,134 @@
---
- name: Common provisioning for FlokiNET servers
hosts: all
gather_facts: true
vars_files:
- vars.yaml
tasks:
- name: Set hostname
ansible.builtin.hostname:
name: "{{ inventory_hostname }}"
become: true
- name: Update apt cache
ansible.builtin.apt:
update_cache: yes
become: true
- name: Upgrade all packages
ansible.builtin.apt:
upgrade: dist
become: true
- name: Install base security packages
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- unattended-upgrades
- ufw
- fail2ban
- secure-delete
state: present
become: true
- name: Configure UFW
ansible.builtin.ufw:
state: enabled
policy: deny
logging: 'on'
become: true
- name: Add SSH rule to UFW
ansible.builtin.ufw:
rule: allow
port: "{{ ssh_port }}"
proto: tcp
become: true
- name: Configure SSH for better security
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: present
with_items:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin prohibit-password' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^#?AllowTcpForwarding', line: 'AllowTcpForwarding no' }
- { regexp: '^#?Port', line: 'Port {{ ssh_port }}' }
- { regexp: '^#?LogLevel', line: 'LogLevel ERROR' }
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
become: true
- name: Restart SSH service
ansible.builtin.service:
name: ssh
state: restarted
become: true
- name: Setup directory for operational scripts
ansible.builtin.file:
path: /opt/c2
state: directory
mode: '0700'
owner: root
group: root
become: true
- name: Copy operational scripts
ansible.builtin.copy:
src: "../../common/files/{{ item }}"
dest: "/opt/c2/{{ item }}"
mode: '0700'
owner: root
group: root
with_items:
- clean-logs.sh
- secure-exit.sh
become: true
- name: Setup cron to clean logs
ansible.builtin.cron:
name: "Log cleanup"
minute: "*/{{ log_rotation_hours * 60 }}"
job: "/opt/c2/clean-logs.sh >/dev/null 2>&1"
become: true
when: disable_history | bool
- name: Disable system history
ansible.builtin.lineinfile:
path: "{{ item }}"
line: "{{ line_item }}"
state: present
create: yes
with_items:
- /etc/profile
- /root/.bashrc
with_nested:
- [ 'export HISTFILESIZE=0', 'export HISTSIZE=0', 'unset HISTFILE' ]
loop_control:
loop_var: line_item
become: true
when: disable_history | bool
- name: Set memory security measures
ansible.builtin.lineinfile:
path: /etc/sysctl.conf
line: "{{ item }}"
state: present
with_items:
- "vm.swappiness=0"
- "kernel.randomize_va_space=2"
become: true
when: secure_memory | bool
- name: Apply sysctl settings
ansible.builtin.command: sysctl -p
become: true
when: secure_memory | bool
+97
View File
@@ -0,0 +1,97 @@
---
# FlokiNET Redirector-only Configuration Playbook
# Note: FlokiNET requires pre-provisioned servers
- name: Prepare FlokiNET redirector configuration
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
# Generate random shell handler port if not provided
shell_handler_port: "{{ shell_handler_port | default(4000 + 60000 | random) }}"
redirector_subdomain: "{{ redirector_subdomain | default('cdn') }}"
tasks:
- name: Validate required FlokiNET configuration
assert:
that:
- redirector_ip is defined and redirector_ip != ""
fail_msg: "FlokiNET requires redirector IP address. Set redirector_ip in vars.yaml or via --flokinet-redirector-ip."
- name: Add redirector to inventory
add_host:
name: "redirector"
groups: "redirectors"
ansible_host: "{{ redirector_ip }}"
ansible_user: "{{ ssh_user | default('root') }}"
ansible_ssh_private_key_file: "{{ ssh_key_path | replace('.pub', '') }}"
ansible_ssh_port: "{{ ssh_port | default(22) }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Verify SSH connection to redirector
wait_for:
host: "{{ redirector_ip }}"
port: "{{ ssh_port | default(22) }}"
delay: 10
timeout: 60
state: started
ignore_errors: true
- name: Provision FlokiNET redirector
hosts: redirectors
become: true
gather_facts: true
vars_files:
- vars.yaml
tasks:
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Set hostname
hostname:
name: "redirector"
- name: Update apt cache
apt:
update_cache: yes
- name: Upgrade all packages
apt:
upgrade: dist
- name: Install base security packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- unattended-upgrades
- ufw
- fail2ban
- secure-delete
state: present
- name: Include common security hardening tasks
include_tasks: "../../common/tasks/security_hardening.yml"
- name: Include common redirector configuration tasks
include_tasks: "../../modules/redirectors/tasks/configure_redirector.yml"
- name: Print deployment summary
debug:
msg:
- "Redirector Configuration Complete!"
- "---------------------------------"
- "Redirector IP: {{ ansible_host }}"
- "Redirector Domain: {{ redirector_subdomain }}.{{ domain }} (Update DNS A record)"
- "Shell Handler Port: {{ shell_handler_port }}"
when: not disable_summary | default(false)
@@ -0,0 +1,10 @@
{
"deployment_id": "{{ deployment_id }}",
"deployment_type": "{{ deployment_type }}",
"provider": "{{ provider }}",
"domain": "{{ phishing_domain | default(domain) }}",
"timestamp": "{{ ansible_date_time.iso8601 }}",
"components": {{ deployment_components | to_json }},
"results": {{ phishing_deployment_results | default({}) | to_json }},
"status": "completed"
}
@@ -0,0 +1,12 @@
---
# FlokiNET provision tasks for WEBRUNNER nodes
# FlokiNET does not expose a public API — manual provisioning required.
- name: FlokiNET not supported for automated WEBRUNNER deployment
fail:
msg: >
FlokiNET does not provide a public provisioning API.
To use FlokiNET nodes with WEBRUNNER, provision VPS instances manually,
add their IPs to the [webrunner_nodes] inventory group, and re-run
the scan plays directly. Node chunk {{ node_chunk.node_name }} assigned
{{ node_chunk.ip_count }} IPs.