diff --git a/defaults/main.yml b/defaults/main.yml index 9aeb3c3..79982de 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,10 +1,10 @@ --- -disable_fetch_art_index: false +ansible_atomic_red_team_disable_fetch_art_index: false -art_repository_owner: redcanaryco -art_branch: master +ansible_atomic_red_team_repository_owner: redcanaryco +ansible_atomic_red_team_branch: master -banned_tids_linux: +ansible_atomic_red_team_banned_tids_linux: - T1018 # slow ping scan - T1046 # nmap - T1070.004 # delete filesystem @@ -20,26 +20,33 @@ banned_tids_linux: - T1526 # Azure - T1529 # reboot/shutdown - T1530 # cloud + - T1562.001 # breaks the tests - T1562.006 # auditd changes (may break some telemetry collection) - T1562.008 # cloud logging changes - T1574.006 # Dynamic Linker Hijacking (requires manual cleanup / testing - might break subsequent tests) - T1611 # container-based, needs prereqs, and hangs/timeout -banned_tids_macos: +ansible_atomic_red_team_banned_tids_macos: - T1485 # impact - data destruction - T1529 # reboot/shutdown -banned_tids_windows: +ansible_atomic_red_team_banned_tids_windows: - T1485 # impact - data destruction - T1529 # reboot/shutdown # these are updated by tasks/gather-art-tids.yml which polls github to write # playbook_dir/art-tids.yml as a fallback, the tasks/main.yml will load # vars/art-tids.yml, which can be manually updated with vars/update-art-tids.sh -art_tids_linux: [] -art_tids_macos: [] -art_tids_windows: [] +ansible_atomic_red_team_tids_linux: [] +ansible_atomic_red_team_tids_macos: [] +ansible_atomic_red_team_tids_windows: [] + +# Execute the ART tests +ansible_atomic_red_team_execute: false # PowerShell version to install (if needed) -atomic_red_team_pwsh_version: "7.4.1" -atomic_red_team_nix_pwsh_path: "/opt/microsoft/powershell/7" +ansible_atomic_red_team_pwsh_version: "7.4.1" +ansible_atomic_red_team_nix_pwsh_path: "/opt/microsoft/powershell/7" + +# Timeout in seconds for each test +ansible_atomic_red_team_timeout: 20 diff --git a/example-playbook.yml b/example-playbook.yml index ea2d21c..cf01235 100644 --- a/example-playbook.yml +++ b/example-playbook.yml @@ -17,7 +17,7 @@ become: true when: ansible_system == 'Linux' vars: - art_tids_linux: + ansible_atomic_red_team_tids_linux: - T1136.001 - T1053.003 - T1003.008-1,2,3 @@ -34,7 +34,7 @@ become: false when: ansible_system == 'Win32NT' vars: - art_tids_windows: + ansible_atomic_red_team_tids_windows: # https://thedfirreport.com/2022/08/08/bumblebee-roasts-its-way-to-domain-admin/ - T1553.005:c2587b8d-743d-4985-aa50-c83394eaeb68 # download and mount iso, run lnk - T1016 # System Network Configuration Discovery - 8 tests diff --git a/vars/update-art-tids.sh b/files/update-art-tids.sh similarity index 79% rename from vars/update-art-tids.sh rename to files/update-art-tids.sh index 3e1ea5c..7de88aa 100755 --- a/vars/update-art-tids.sh +++ b/files/update-art-tids.sh @@ -8,9 +8,9 @@ echo "---" | tee art-tids.yml function fetch-art-index-to-yml() { url="https://github.com/${ghuser}/atomic-red-team/raw/${branch}/atomics/Indexes/Indexes-CSV/${1}-index.csv" - tidlist=($(curl -sL $url | awk -F, '/T1/{print $2}' | sort -u)) + tidlist=("$(curl -sL "$url" | awk -F, '/T1/{print $2}' | sort -u)") echo "art_tids_${1}:" | tee -a art-tids.yml - for tid in ${tidlist[*]}; do + for tid in "${tidlist[@]}"; do echo " - ${tid}" done | tee -a art-tids.yml } diff --git a/tasks/gather-art-tids.yml b/tasks/gather-art-tids.yml index 1523f3e..1f175c3 100644 --- a/tasks/gather-art-tids.yml +++ b/tasks/gather-art-tids.yml @@ -1,11 +1,28 @@ --- - name: Set Indexes-CSV url ansible.builtin.set_fact: - index_csv_url: "https://github.com/{{ art_repository_owner }}/atomic-red-team/raw/{{ art_branch }}/atomics/Indexes/Indexes-CSV/" + index_csv_url: "https://github.com/{{ ansible_atomic_red_team_repository_owner }}/atomic-red-team/raw/{{ ansible_atomic_red_team_branch }}/atomics/Indexes/Indexes-CSV/" + +- name: Create temporary directory + ansible.builtin.tempfile: + state: directory + suffix: build + delegate_to: localhost + register: art_tids + when: not ansible_atomic_red_team_disable_fetch_art_index + +- name: Copy emergency bash script + ansible.builtin.copy: + src: "update-art-tids.sh" + dest: "{{ art_tids.path }}/update-art-tids.sh" + mode: '0755' + delegate_to: localhost + become: false + when: not ansible_atomic_red_team_disable_fetch_art_index - name: Gather and write art-tids.yml ansible.builtin.blockinfile: - dest: "{{ playbook_dir }}/art-tids.yml" + dest: "{{ art_tids.path }}/art-tids.yml" create: true mode: "0644" block: | @@ -18,7 +35,7 @@ {%- endif -%} {%- endfor -%} {#- build a dict with this for to_nice_yaml so it will indent correctly -#} - {%- set yamloutput = dict(art_tids_windows=list) -%} + {%- set yamloutput = dict(ansible_atomic_red_team_tids_windows=list) -%} {{ yamloutput | to_nice_yaml | indent(2) }} {%- set list = [] -%} {%- for line in lookup('ansible.builtin.url', index_csv_url + 'linux-index.csv', wantlist=True) -%} @@ -28,8 +45,8 @@ {%- endif -%} {%- endif -%} {%- endfor -%} - {%- set yamloutput = dict(art_tids_linux=list) -%} + {%- set yamloutput = dict(ansible_atomic_red_team_tids_linux=list) -%} {{ yamloutput | to_nice_yaml | indent(2) }} delegate_to: localhost become: false - when: not disable_fetch_art_index + when: not ansible_atomic_red_team_disable_fetch_art_index diff --git a/tasks/invoke-atomictest.yml b/tasks/invoke-atomictest.yml index 6a7c1f1..7285e87 100644 --- a/tasks/invoke-atomictest.yml +++ b/tasks/invoke-atomictest.yml @@ -49,6 +49,10 @@ register: check_prereqs failed_when: '"Prerequisites not met:" in check_prereqs.stdout' changed_when: false + + - name: "Set fact for prereq_check_passed" + ansible.builtin.set_fact: + prereq_check_passed: true rescue: - name: "Install prereqs for {{ item }}" ansible.builtin.shell: @@ -64,20 +68,24 @@ ansible.builtin.debug: var: "{{ item }}" loop: "{{ prereqs }}" + ignore_errors: true + register: ignore_errors_register - name: "Execute {{ item }}" ansible.builtin.shell: cmd: | - Invoke-AtomicTest {{ testarg }} -Confirm:$false -TimeoutSeconds 300 -ExecutionLogPath /root/atc_execution.csv + Invoke-AtomicTest {{ testarg }} -Confirm:$false -TimeoutSeconds {{ ansible_atomic_red_team_timeout }} -ExecutionLogPath {{ ansible_user_dir }}/atc_execution.csv args: executable: pwsh register: execute_test ignore_errors: true changed_when: false + when: prereq_check_passed - name: "Execute {{ item }}" ansible.builtin.debug: var: execute_test.stdout + when: prereq_check_passed - name: "Cleanup {{ item }}" ansible.builtin.shell: @@ -117,19 +125,23 @@ - name: Debug prereqs {{ item }} ansible.builtin.meta: debug with_items: "{{ prereqs }}" + ignore_errors: true + register: ignore_errors_register - name: "Execute {{ item }}" - ansible.windows.win_shell: | - Invoke-AtomicTest {{ testarg }} -Confirm:$false -TimeoutSeconds 300 -ExecutionLogPath /root/atc_execution.csv + ansible.windows.win_command: | + Invoke-AtomicTest {{ testarg }} -Confirm:$false -TimeoutSeconds {{ ansible_atomic_red_team_timeout }} -ExecutionLogPath {{ ansible_user_dir }}/atc_execution.csv register: execute_test ignore_errors: true + when: prereq_check_passed - name: "Execute {{ item }}" ansible.builtin.debug: var: execute_test.stdout + when: prereq_check_passed - name: "Cleanup {{ item }}" - ansible.windows.win_shell: | + ansible.windows.win_command: | Invoke-AtomicTest {{ testarg }} -Cleanup register: cleanup_test ignore_errors: true diff --git a/tasks/main.yml b/tasks/main.yml index 74ffd35..972eed3 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,19 +1,4 @@ --- -# tasks file for ansible_atomic_red_team - -- name: "Include the list of available TIDs" - block: - - name: Built list of ART TIDs - ansible.builtin.include_tasks: "gather-art-tids.yml" - when: not disable_fetch_art_index - - - name: Load the ART TIDs - ansible.builtin.include_vars: "{{ playbook_dir }}/art-tids.yml" - rescue: - # this may require manual updates via vars/update-art-tids.sh - - name: Load the ART TIDs from role/vars - ansible.builtin.include_vars: "art-tids.yml" - - name: Setup Windows ansible.builtin.include_tasks: "setup-windows.yml" when: ansible_system == 'Win32NT' @@ -22,14 +7,22 @@ ansible.builtin.include_tasks: "setup-linux.yml" when: ansible_system == 'Linux' -- name: Run invoke-atomictest on each TID (linux) - ansible.builtin.include_tasks: invoke-atomictest.yml - loop: "{{ art_tids_linux | difference(banned_tids_linux) }}" - when: - - ansible_system == 'Linux' +- name: Execute ART tests + when: ansible_atomic_red_team_execute + block: + - name: Built list of ART TIDs + ansible.builtin.include_tasks: gather-art-tids.yml + when: not ansible_atomic_red_team_disable_fetch_art_index + + - name: Load the ART TIDs + ansible.builtin.include_vars: "{{ art_tids.path }}/art-tids.yml" + + - name: Run invoke-atomictest on each TID (linux) + ansible.builtin.include_tasks: invoke-atomictest.yml + loop: "{{ ansible_atomic_red_team_tids_linux | difference(ansible_atomic_red_team_banned_tids_linux) }}" + when: ansible_system == 'Linux' -- name: Run invoke-atomictest on each TID (windows) - ansible.builtin.include_tasks: invoke-atomictest.yml - loop: "{{ art_tids_windows | difference(banned_tids_windows) }}" - when: - - ansible_system == 'Win32NT' + - name: Run invoke-atomictest on each TID (windows) + ansible.builtin.include_tasks: invoke-atomictest.yml + loop: "{{ ansible_atomic_red_team_tids_windows | difference(ansible_atomic_red_team_banned_tids_windows) }}" + when: ansible_system == 'Win32NT' diff --git a/tasks/setup-linux.yml b/tasks/setup-linux.yml index e037783..718920a 100644 --- a/tasks/setup-linux.yml +++ b/tasks/setup-linux.yml @@ -5,9 +5,9 @@ ansible.builtin.include_role: name: cowdogmoo.workstation.package_management vars: - package_management_common_install_packages: "{{ atomic_red_team_common_install_packages }}" - package_management_debian_specific_packages: "{{ atomic_red_team_debian_specific_packages }}" - package_management_redhat_specific_packages: "{{ atomic_red_team_redhat_specific_packages }}" + package_management_common_install_packages: "{{ ansible_atomic_red_team_common_install_packages }}" + package_management_debian_specific_packages: "{{ ansible_atomic_red_team_debian_specific_packages }}" + package_management_redhat_specific_packages: "{{ ansible_atomic_red_team_redhat_specific_packages }}" when: ansible_os_family in ['Debian', 'RedHat'] - name: Set architecture mapping for PowerShell tar.gz packages @@ -18,12 +18,12 @@ - name: Set PowerShell package name based on architecture ansible.builtin.set_fact: - ps_pkg_name: "powershell-{{ atomic_red_team_pwsh_version }}-linux-{{ ps_arch_map[ansible_architecture] }}.tar.gz" + ps_pkg_name: "powershell-{{ ansible_atomic_red_team_pwsh_version }}-linux-{{ ps_arch_map[ansible_architecture] }}.tar.gz" when: ansible_architecture in ps_arch_map - name: Set PowerShell package download URL ansible.builtin.set_fact: - ps_download_url: "https://github.com/PowerShell/PowerShell/releases/download/v{{ atomic_red_team_pwsh_version }}/{{ ps_pkg_name }}" + ps_download_url: "https://github.com/PowerShell/PowerShell/releases/download/v{{ ansible_atomic_red_team_pwsh_version }}/{{ ps_pkg_name }}" when: ansible_architecture in ps_arch_map - name: Download PowerShell package @@ -38,7 +38,7 @@ - name: Create PowerShell directory become: true ansible.builtin.file: - path: "{{ atomic_red_team_nix_pwsh_path }}" + path: "{{ ansible_atomic_red_team_nix_pwsh_path }}" state: directory mode: "0755" owner: "{{ ansible_user_id }}" @@ -49,21 +49,54 @@ become: true ansible.builtin.unarchive: src: "/tmp/{{ ps_pkg_name }}" - dest: "{{ atomic_red_team_nix_pwsh_path }}" + dest: "{{ ansible_atomic_red_team_nix_pwsh_path }}" remote_src: true when: ps_pkg_name is defined - name: Set execute permissions for pwsh become: true ansible.builtin.file: - path: "{{ atomic_red_team_nix_pwsh_path }}/pwsh" + path: "{{ ansible_atomic_red_team_nix_pwsh_path }}/pwsh" mode: "+x" when: ps_pkg_name is defined - name: Create symlink for pwsh become: true ansible.builtin.file: - src: "{{ atomic_red_team_nix_pwsh_path }}/pwsh" + src: "{{ ansible_atomic_red_team_nix_pwsh_path }}/pwsh" dest: "/usr/bin/pwsh" state: link when: ps_pkg_name is defined + +- name: Install Invoke-ART + ansible.builtin.shell: + cmd: | + IEX (IWR 'https://raw.githubusercontent.com/redcanaryco/invoke-atomicredteam/master/install-atomicredteam.ps1' -UseBasicParsing); Install-AtomicRedTeam -getAtomics -Force + args: + executable: pwsh + creates: "{{ ansible_user_dir }}/AtomicRedTeam/atomics/Indexes/index.yaml" + +- name: Find the path to the system powershell profile + ansible.builtin.shell: + cmd: | + $PROFILE.AllUsersAllHosts + changed_when: false + args: + executable: pwsh + register: pwshprofile + +- name: Powershell Profile (debug) + ansible.builtin.debug: + var: pwshprofile.stdout + +- name: Add Invoke-AtomicRedTeam to the powershell profile + ansible.builtin.lineinfile: + path: "{{ pwshprofile.stdout }}" + state: present + regex: '.*Invoke-AtomicRedTeam.*' + line: |- + Import-Module "{{ ansible_user_dir }}/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1" -Force + owner: "{{ ansible_user_id }}" + group: "{{ ansible_user_id }}" + mode: '0644' + create: true diff --git a/tasks/setup-windows.yml b/tasks/setup-windows.yml index 0b37dd5..08a1c5d 100644 --- a/tasks/setup-windows.yml +++ b/tasks/setup-windows.yml @@ -36,11 +36,11 @@ '@ $script | Out-File $PShome\Profile.ps1 -- name: "Install AtomicsFolder from {{ art_repository_owner }}" +- name: "Install AtomicsFolder from {{ ansible_atomic_red_team_repository_owner }}" ansible.windows.win_shell: | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Internet Explorer\Main" -Name "DisableFirstRunCustomize" -Value 2 IEX (IWR 'https://raw.githubusercontent.com/redcanaryco/invoke-atomicredteam/master/install-atomicsfolder.ps1' -UseBasicParsing) - Install-AtomicsFolder -Force -RepoOwner "{{ art_repository_owner }}" -Branch "{{ art_branch }}" + Install-AtomicsFolder -Force -RepoOwner "{{ ansible_atomic_red_team_repository_owner }}" -Branch "{{ ansible_atomic_red_team_branch }}" args: creates: C:\AtomicRedTeam\atomics\used_guids.txt diff --git a/vars/main.yml b/vars/main.yml index 3a0e05b..f875768 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,13 +1,13 @@ --- -atomic_red_team_common_install_packages: +ansible_atomic_red_team_common_install_packages: - wget -atomic_red_team_debian_specific_packages: +ansible_atomic_red_team_debian_specific_packages: - apt-transport-https - ca-certificates - curl - libunwind8 - software-properties-common -atomic_red_team_redhat_specific_packages: +ansible_atomic_red_team_redhat_specific_packages: - libicu