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:
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user