diff --git a/defaults/main.yml b/defaults/main.yml index dd2c428..9aeb3c3 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -39,3 +39,7 @@ banned_tids_windows: art_tids_linux: [] art_tids_macos: [] art_tids_windows: [] + +# PowerShell version to install (if needed) +atomic_red_team_pwsh_version: "7.4.1" +atomic_red_team_nix_pwsh_path: "/opt/microsoft/powershell/7" diff --git a/example-playbook.yml b/example-playbook.yml index 2ceadeb..ea2d21c 100644 --- a/example-playbook.yml +++ b/example-playbook.yml @@ -37,7 +37,7 @@ art_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 Configurration Discovery - 8 tests + - T1016 # System Network Configuration Discovery - 8 tests - T1057 # Process Discovery - 5 tests - T1219-2 # Install Anydesk - T1087.002-5,6,7,8 # Account Discovery(domain) diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..d88a40a --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,13 @@ +--- +- name: Converge + hosts: all + tasks: + - name: Include default variables + ansible.builtin.include_vars: + file: "../../defaults/main.yml" + - name: Include variables + ansible.builtin.include_vars: + file: "../../vars/main.yml" + roles: + - name: Run the atomic-red-team role + role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" diff --git a/molecule/default/inventory b/molecule/default/inventory new file mode 100644 index 0000000..2fbb50c --- /dev/null +++ b/molecule/default/inventory @@ -0,0 +1 @@ +localhost diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..dfbf3ff --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,41 @@ +--- +# Run molecule inside of a docker container +driver: + name: docker + +platforms: + - name: ubuntu-atomic-red-team + image: "geerlingguy/docker-ubuntu2204-ansible:latest" + # Setting the command to this is necessary for systemd containers + command: "" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + + - name: kali-atomic-red-team + image: cisagov/docker-kali-ansible:latest + # Setting the command to this is necessary for systemd containers + command: "" + pre_build_image: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + + - name: redhat-atomic-red-team + image: "geerlingguy/docker-rockylinux9-ansible:latest" + # Setting the command to this is necessary for systemd containers + command: "" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + playbooks: + converge: ${MOLECULE_PLAYBOOK:-converge.yml} + +verifier: + name: ansible diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml new file mode 100644 index 0000000..f8970ca --- /dev/null +++ b/molecule/default/verify.yml @@ -0,0 +1,34 @@ +--- +- name: Verify + hosts: all + gather_facts: true + tasks: + - name: Include default variables + ansible.builtin.include_vars: + file: "../../defaults/main.yml" + + - name: Include variables + ansible.builtin.include_vars: + file: "../../vars/main.yml" + + - name: Check if PowerShell is installed + ansible.builtin.shell: + cmd: "pwsh -NoProfile -v" + register: pwsh_version + ignore_errors: true + + - name: Assert that PowerShell is installed + ansible.builtin.assert: + that: + - pwsh_version.rc == 0 + - "'PowerShell' in pwsh_version.stdout" + + - name: Check if /usr/local/bin is in $PATH + ansible.builtin.shell: + cmd: "echo $PATH" + register: system_path + + - name: Assert that /usr/local/bin is in $PATH + ansible.builtin.assert: + that: + - "'/usr/local/bin' in system_path.stdout" diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..e15e0ec --- /dev/null +++ b/requirements.yml @@ -0,0 +1,4 @@ +--- +collections: + - name: ansible.windows + - name: cowdogmoo.workstation \ No newline at end of file diff --git a/tasks/invoke-atomictest.yml b/tasks/invoke-atomictest.yml index 717edda..6a7c1f1 100644 --- a/tasks/invoke-atomictest.yml +++ b/tasks/invoke-atomictest.yml @@ -45,7 +45,7 @@ cmd: | Invoke-AtomicTest {{ testarg }} -CheckPrereqs args: - executable: /usr/bin/pwsh + executable: pwsh register: check_prereqs failed_when: '"Prerequisites not met:" in check_prereqs.stdout' changed_when: false @@ -55,23 +55,25 @@ cmd: | Invoke-AtomicTest {{ testarg }} -GetPrereqs args: - executable: /usr/bin/pwsh + executable: pwsh register: get_prereqs ignore_errors: true + changed_when: true - - name: "Debug prereqs {{ item }}" + - name: Debug prereqs {{ item }} ansible.builtin.debug: - var: get_prereqs.stdout - when: get_prereqs.changed + var: "{{ item }}" + loop: "{{ prereqs }}" - name: "Execute {{ item }}" ansible.builtin.shell: cmd: | Invoke-AtomicTest {{ testarg }} -Confirm:$false -TimeoutSeconds 300 -ExecutionLogPath /root/atc_execution.csv args: - executable: /usr/bin/pwsh + executable: pwsh register: execute_test ignore_errors: true + changed_when: false - name: "Execute {{ item }}" ansible.builtin.debug: @@ -82,9 +84,10 @@ cmd: | Invoke-AtomicTest {{ testarg }} -Cleanup args: - executable: /usr/bin/pwsh + executable: pwsh register: cleanup_test ignore_errors: true + changed_when: false - name: "Cleanup {{ item }}" ansible.builtin.debug: @@ -108,11 +111,12 @@ Invoke-AtomicTest {{ testarg }} -GetPrereqs register: get_prereqs ignore_errors: true + changed_when: false + notify: debug_prereqs - - name: "Debug prereqs {{ item }}" - ansible.builtin.debug: - var: get_prereqs.stdout - when: get_prereqs.changed + - name: Debug prereqs {{ item }} + ansible.builtin.meta: debug + with_items: "{{ prereqs }}" - name: "Execute {{ item }}" ansible.windows.win_shell: | diff --git a/tasks/setup-linux.yml b/tasks/setup-linux.yml index 3352834..e037783 100644 --- a/tasks/setup-linux.yml +++ b/tasks/setup-linux.yml @@ -1,90 +1,69 @@ --- -- name: Powershell +- name: Install PowerShell on Linux block: - - name: Check for powershell - ansible.builtin.shell: - cmd: pwsh -c '$true' - changed_when: false - rescue: + - name: Install dependencies + 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 }}" + when: ansible_os_family in ['Debian', 'RedHat'] - # ------- Ubuntu + - name: Set architecture mapping for PowerShell tar.gz packages + ansible.builtin.set_fact: + ps_arch_map: + x86_64: "x64" + aarch64: "arm64" - - name: Ensure powershell is installed (prereq) - ansible.builtin.package: - name: - - wget - - apt-transport-https - - software-properties-common - state: present - when: ansible_facts['distribution'] == 'Ubuntu' + - 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" + when: ansible_architecture in ps_arch_map - - name: Ensure powershell is installed (repo keys) - ansible.builtin.apt: - deb: "https://packages.microsoft.com/config/ubuntu/{{ ansible_distribution_version }}/packages-microsoft-prod.deb" - when: ansible_facts['distribution'] == 'Ubuntu' + - 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 }}" + when: ansible_architecture in ps_arch_map - - name: Ensure powershell is installed (deb) - ansible.builtin.apt: - name: powershell - update_cache: yes - when: >- - ansible_facts['distribution'] == 'Ubuntu' - - # ------- Amazon / CentOS - - - name: Add Microsoft Repo (Amazon Linux 2) + - name: Download PowerShell package ansible.builtin.get_url: - url: https://packages.microsoft.com/config/rhel/8/prod.repo - dest: /etc/yum.repos.d/microsoft.repo - mode: '0644' - owner: root - when: ansible_facts['distribution'] == 'Amazon' - - - name: Add Microsoft Repo (CentOS) - ansible.builtin.get_url: - url: https://packages.microsoft.com/config/rhel/{{ ansible_distribution_major_version }}/prod.repo - dest: /etc/yum.repos.d/microsoft.repo - mode: '0644' - owner: root - when: ansible_facts['distribution'] == 'CentOS' - - - name: Ensure powershell is installed (rpm) - ansible.builtin.yum: - name: powershell - update_cache: yes - when: >- - ansible_facts['distribution'] == 'CentOS' or - ansible_facts['distribution'] == 'Amazon' + url: "{{ ps_download_url }}" + dest: "/tmp/{{ ps_pkg_name }}" + mode: "0755" + owner: "{{ ansible_user_id }}" + group: "{{ ansible_user_id }}" + 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: /usr/bin/pwsh - creates: /root/AtomicRedTeam/atomics/Indexes/index.yaml + - name: Create PowerShell directory + become: true + ansible.builtin.file: + path: "{{ atomic_red_team_nix_pwsh_path }}" + state: directory + mode: "0755" + owner: "{{ ansible_user_id }}" + group: "{{ ansible_user_id }}" + when: ps_pkg_name is defined -- name: Find the path to the system powershell profile - ansible.builtin.shell: - cmd: | - $PROFILE.AllUsersAllHosts - changed_when: false - args: - executable: /usr/bin/pwsh - register: pwshprofile + - name: Extract PowerShell tar.gz + become: true + ansible.builtin.unarchive: + src: "/tmp/{{ ps_pkg_name }}" + dest: "{{ atomic_red_team_nix_pwsh_path }}" + remote_src: true + when: ps_pkg_name is defined -- name: Powershell Profile (debug) - ansible.builtin.debug: - var: pwshprofile.stdout + - name: Set execute permissions for pwsh + become: true + ansible.builtin.file: + path: "{{ atomic_red_team_nix_pwsh_path }}/pwsh" + mode: "+x" + when: ps_pkg_name is defined -- name: Add Invoke-AtomicRedTeam to the powershell profile - ansible.builtin.lineinfile: - path: "{{ pwshprofile.stdout }}" - state: present - regex: '.*Inovke-AtomicRedTeam.*' - line: |- - Import-Module "/root/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1" -Force - owner: root - group: root - mode: '0644' - create: yes + - name: Create symlink for pwsh + become: true + ansible.builtin.file: + src: "{{ atomic_red_team_nix_pwsh_path }}/pwsh" + dest: "/usr/bin/pwsh" + state: link + when: ps_pkg_name is defined diff --git a/vars/main.yml b/vars/main.yml index ed97d53..9d2efb3 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1 +1,14 @@ --- +atomic_red_team_common_install_packages: + - wget + +atomic_red_team_debian_specific_packages: + - apt-transport-https + - ca-certificates + - curl + - libunwind8 + - software-properties-common + - wget + +atomic_red_team_redhat_specific_packages: + - libicu diff --git a/vars/update-art-tids.sh b/vars/update-art-tids.sh index eb3f0b2..3e1ea5c 100755 --- a/vars/update-art-tids.sh +++ b/vars/update-art-tids.sh @@ -6,15 +6,15 @@ branch="master" 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) ) - echo "art_tids_${1}:" | tee -a art-tids.yml - for tid in ${tidlist[*]}; do - echo " - ${tid}" - done | tee -a 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)) + echo "art_tids_${1}:" | tee -a art-tids.yml + for tid in ${tidlist[*]}; do + echo " - ${tid}" + done | tee -a art-tids.yml } for os in linux macos windows; do - fetch-art-index-to-yml ${os} + fetch-art-index-to-yml ${os} done