--- # Common tasks for configuring C2 server with Havoc C2 and EDR evasion # Shared across all providers - name: Update apt cache apt: update_cache: yes - name: Disable default Kali MOTD file: path: "{{ ansible_env.HOME }}/.hushlogin" state: touch mode: '0644' when: ansible_distribution == "Kali GNU/Linux" - name: Set a custom MOTD template: src: "../templates/motd.j2" dest: /etc/motd owner: root group: root mode: '0644' - name: Install base utilities and tools via apt 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 - build-essential - zip - unzip - postfix - net-tools - certbot - opendkim - opendkim-tools - dovecot-core - dovecot-imapd - dovecot-pop3d - dovecot-sieve - dovecot-managesieved - yq - build-essential # Additional Havoc C2 dependencies - mingw-w64 - nasm - cmake - ninja-build - libfontconfig1 - libglu1-mesa-dev - libgtest-dev - libspdlog-dev - libboost-all-dev - libncurses5-dev - libgdbm-dev - libssl-dev - libreadline-dev - libffi-dev - libsqlite3-dev - libbz2-dev - mesa-common-dev - qtbase5-dev - qtchooser - qt5-qmake - qtbase5-dev-tools - libqt5websockets5 - libqt5websockets5-dev state: present - name: Create directories for operational scripts file: path: "{{ item }}" state: directory mode: '0700' owner: root group: root with_items: - /root/Tools - /root/Tools/beacons - /root/Tools/payloads - name: Copy operational scripts copy: src: "{{ item }}" dest: "/root/Tools/{{ item | basename }}" mode: '0700' owner: root group: root with_items: - "../files/clean-logs.sh" - "../files/secure-exit.sh" - "../files/havoc_installer.sh" - "../files/havoc_shell_handler.sh" - "../files/secure_payload_sync.sh" - name: Copy post-install script copy: src: "../files/post_install_c2.sh" dest: "/root/Tools/post_install_c2.sh" mode: '0700' owner: root group: root - name: Copy port randomization script copy: src: "../files/randomize_ports.sh" dest: "/root/Tools/randomize_ports.sh" mode: '0700' owner: root group: root - name: Create post-install instructions template: src: "../templates/POST_INSTALL_INSTRUCTIONS.txt.j2" dest: "/root/POST_INSTALL_INSTRUCTIONS.txt" mode: '0644' owner: root group: root - name: Set up systemd timer for payload sync shell: | cat > /etc/systemd/system/payload-sync.service << 'EOF' [Unit] Description=Secure Payload Sync Service After=network-online.target [Service] Type=oneshot ExecStart=/root/Tools/secure_payload_sync.sh User=root Group=root PrivateTmp=true StandardOutput=null [Install] WantedBy=multi-user.target EOF cat > /etc/systemd/system/payload-sync.timer << 'EOF' [Unit] Description=Secure Payload Sync Timer Requires=payload-sync.service [Timer] OnBootSec=5min OnUnitActiveSec=30m RandomizedDelaySec=30m Persistent=true [Install] WantedBy=timers.target EOF systemctl daemon-reload systemctl enable payload-sync.timer systemctl start payload-sync.timer - name: Install Havoc C2 Framework shell: "/root/Tools/havoc_installer.sh" args: creates: "/root/Tools/Havoc" async: 1800 # Allow 30 minutes for completion poll: 0 # Don't wait for completion register: havoc_installation_job - name: Wait for Havoc installation to complete async_status: jid: "{{ havoc_installation_job.ansible_job_id }}" register: job_result until: job_result.finished retries: 60 # Check every 30 seconds for up to 30 minutes delay: 30 when: havoc_installation_job is defined - name: Display Havoc installation output debug: var: havoc_installation_result.stdout_lines when: havoc_installation_result.stdout_lines is defined - name: Create Havoc payload generation script template: src: "../templates/generate_havoc_payloads.sh.j2" dest: "/root/Tools/generate_havoc_payloads.sh" mode: '0700' owner: root group: root - name: Create Havoc C2 configuration from template template: src: "../templates/havoc-config.yaotl.j2" dest: "/root/Tools/Havoc/data/havoc.yaotl" mode: '0600' owner: root group: root - name: Create Linux loader script template template: src: "../templates/linux_loader.sh.j2" dest: "/root/Tools/linux_loader.template" mode: '0644' owner: root group: root - name: Create Windows PowerShell loader template template: src: "../templates/windows_loader.ps1.j2" dest: "/root/Tools/windows_loader.template" mode: '0644' owner: root group: root - name: Create beacon server script from template template: src: "../templates/serve-havoc-payloads.sh.j2" dest: "/root/Tools/serve-havoc-payloads.sh" mode: '0700' owner: root group: root vars: redirector_subdomain: "{{ redirector_subdomain | default('cdn') }}" domain: "{{ domain }}" redirector_port: "{{ redirector_port | default('443') }}" - name: Run port randomization if enabled include_tasks: port_randomization.yml when: randomize_ports | default(false) | bool - name: Generate Havoc payloads shell: "/root/Tools/generate_havoc_payloads.sh" args: creates: "/root/Tools/Havoc/payloads/manifest.json" register: payload_generation_result environment: PATH: "{{ ansible_env.PATH }}:/usr/local/bin" ignore_errors: yes - name: Display payload generation output debug: var: payload_generation_result.stdout_lines when: payload_generation_result.stdout_lines is defined - name: Start payload server shell: | nohup /root/Tools/serve-havoc-payloads.sh > /dev/null 2>&1 & args: executable: /bin/bash register: beacon_server_result - name: Create NGINX configuration fragment for redirector template: src: "../templates/redirector-havoc-fragment.j2" dest: "/root/Tools/redirector-config.conf" mode: '0644' owner: root group: root vars: c2_ip: "{{ ansible_host }}" redirector_domain: "{{ redirector_subdomain }}.{{ domain }}" - name: Create Havoc usage guide template: src: "../templates/havoc-guide.j2" dest: "/root/havoc-guide.txt" mode: '0600' owner: root group: root vars: c2_ip: "{{ ansible_host }}" redirector_domain: "{{ redirector_subdomain }}.{{ domain }}" - name: Include traffic flow configuration include_tasks: "../tasks/traffic_flow_config.yml" - name: Set up cron job for log cleaning if zero-logs enabled cron: name: "Clean logs" minute: "0" hour: "*/6" job: "/root/Tools/clean-logs.sh > /dev/null 2>&1" when: zero_logs | bool - name: Ensure SSH key for redirector access is available block: - name: Copy deployment SSH key to C2 for redirector access (AWS) copy: src: "~/.ssh/c2deploy_{{ deployment_id }}.pem" dest: "/root/.ssh/redirector_key" mode: '0600' owner: root group: root when: provider == "aws" - name: Copy deployment SSH key to C2 for redirector access (non-AWS) copy: src: "{{ ssh_key_path | replace('.pub', '') }}" dest: "/root/.ssh/redirector_key" mode: '0600' owner: root group: root when: provider != "aws" - name: Create SSH config for redirector access blockinfile: path: /root/.ssh/config create: yes mode: '0600' owner: root group: root marker: "# {mark} ANSIBLE MANAGED REDIRECTOR CONFIG" block: | Host redirector HostName {{ redirector_ip }} User {{ ssh_user | default('root') }} IdentityFile /root/.ssh/redirector_key StrictHostKeyChecking no when: not redirector_only | bool and redirector_ip is defined # Include integrated tracker tasks if requested - name: Include integrated tracker setup include_tasks: "configure_integrated_tracker.yml" when: setup_integrated_tracker | default(false) | bool