Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
smolse committed Dec 28, 2024
1 parent 80ee202 commit d33ff4a
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/lint-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Lint Atomic Red Team
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Lint Atomic Red Team
uses: ./
with:
cleanup: true
remote: false
adversary-emulation: true
list-of-atomics: "./koko.csv"
logging_module: File
execution_log_path: ""
technique: T1033
test-numbers: 2
Empty file added .gitignore
Empty file.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "atomic-red-team"]
path = atomic-red-team
url = https://github.com/redcanaryco/atomic-red-team.git
105 changes: 105 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: 'Invoke-AtomicRedTeam Action'
description: 'Run local Atomic Red Team tests and adversary emulation'
author: 'smolse'

inputs:
technique:
description: 'ID of the technique to execute'
required: false
test-names:
description: 'Test names to execute'
required: false
test-numbers:
description: 'Test numbers to execute'
required: false
test-guids:
description: 'Test GUIDs to execute'
required: false
get-prereqs:
description: 'Get prerequisites for tests execution'
required: false
default: 'true'
cleanup:
description: 'Clean up after tests execution'
required: false
default: 'true'
adversary-emulation:
description: 'Execute adversary emulation based on a schedule file'
required: false
default: 'false'
list-of-atomics:
description: 'Path to adversary emulation CSV schedule file'
required: false
private-config:
description: 'Path to the private configuration file'
required: false

runs:
using: 'composite'
steps:

- name: Validate action inputs
shell: pwsh
run: |
Write-Output '${{ toJson(inputs) }}'
if ('${{ inputs.adversary-emulation }}' -ne 'true' -and '${{ inputs.technique }}' -eq '') {
Write-Output "::error::Technique ID must be provided for atomic test execution"
exit 1
}
if ('${{ inputs.adversary-emulation }}' -eq 'true' -and '${{ inputs.list-of-atomics }}' -eq '') {
Write-Output "::error::List of atomics must be provided when adversary emulation is enabled"
exit 1
}
- name: Install Invoke-AtomicRedTeam
shell: pwsh
run: |
if (-not (Get-Module -ListAvailable -Name Invoke-AtomicRedTeam)) {
Install-Module -Name Invoke-AtomicRedTeam -Scope CurrentUser -Force
Write-Output "::notice::Invoke-AtomicRedTeam has been installed"
} else {
Write-Output "::notice::Invoke-AtomicRedTeam is already installed"
}
- name: Install atomics folder
shell: pwsh
run: |
$atomicsPath = if ($IsWindows) { 'C:\AtomicRedTeam\atomics' } else { "$env:HOME/AtomicRedTeam/atomics" }
if (-not (Test-Path $atomicsPath)) {
./scripts/Install-AtomicsFolder.ps1
Write-Output "::notice::Atomics folder has been installed"
} else {
Write-Output "::notice::Atomics folder already exists"
}
working-directory: ${{ github.action_path }}

- name: Execute atomic technique
if: inputs.adversary-emulation != 'true'
shell: pwsh
run: |
$Params = @{}
if ('${{ inputs.test-names }}' -ne '') { $Params.TestNames = '${{ inputs.test-names }}' }
if ('${{ inputs.test-numbers }}' -ne '') { $Params.TestNumbers = '${{ inputs.test-numbers }}' }
if ('${{ inputs.test-guids }}' -ne '') { $Params.TestGuids = '${{ inputs.test-guids }}' }
if ('${{ inputs.get-prereqs }}' -eq 'true') { $Params.GetPrereqs = $true }
if ('${{ inputs.cleanup }}' -eq 'true') { $Params.Cleanup = $true }
Invoke-AtomicTest ${{ inputs.technique }} @Params
- name: Execute adversary emulation
if: inputs.adversary-emulation == 'true'
shell: pwsh
run: |
ls -la ~
$Params = @{
ListOfAtomics = '${{ inputs.list-of-atomics }}'
}
if ('${{ inputs.get-prereqs }}' -eq 'true') { $Params.GetPrereqs = $true }
if ('${{ inputs.cleanup }}' -eq 'true') { $Params.Cleanup = $true }
Invoke-AtomicRunner @Params
branding:
icon: 'shield'
color: 'blue'
1 change: 1 addition & 0 deletions atomic-red-team
Submodule atomic-red-team added at 12afd8
2 changes: 2 additions & 0 deletions koko.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Order,Technique,TestName,auto_generated_guid,supported_platforms,TimeoutSeconds,InputArgs,AtomicsFolder,enabled,notes
1,T1003.002,Scheduled task Local,42f53695-ad4a-4546-abb6-7d837f644a71,linux,120,,Public,TRUE,Emulation converted from https://github.com/Atomics-on-A-Friday/Emulation-Tools
145 changes: 145 additions & 0 deletions scripts/Install-AtomicsFolder.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
function Install-AtomicsFolder {

<#
.SYNOPSIS
This is a simple script to download the atttack definitions in the "atomics" folder of the Red Canary Atomic Red Team project.
License: MIT License
Required Dependencies: powershell-yaml
Optional Dependencies: None
.PARAMETER DownloadPath
Specifies the desired path to download atomics zip archive to.
.PARAMETER InstallPath
Specifies the desired path for where to unzip the atomics folder.
.PARAMETER Force
Delete the existing atomics folder before installation if it exists.
.EXAMPLE
Install atomics folder
PS> Install-AtomicsFolder.ps1
.NOTES
Use the '-Verbose' option to print detailed information.
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $False, Position = 0)]
[string]$InstallPath = $( if ($IsLinux -or $IsMacOS) { $Env:HOME + "/AtomicRedTeam" } else { $env:HOMEDRIVE + "\AtomicRedTeam" }),

[Parameter(Mandatory = $False, Position = 1)]
[string]$DownloadPath = $InstallPath,

[Parameter(Mandatory = $False, Position = 2)]
[string]$RepoOwner = "redcanaryco",

[Parameter(Mandatory = $False, Position = 3)]
[string]$Branch = "master",

[Parameter(Mandatory = $False)]
[switch]$Force = $False, # delete the existing install directory and reinstall

[Parameter(Mandatory = $False)]
[switch]$NoPayloads = $False
)
Try {
$InstallPathwAtomics = Join-Path $InstallPath "atomics"
if ($Force -or -Not (Test-Path -Path $InstallPathwAtomics )) {
write-verbose "Directory Creation"
if ($Force) {
Try {
if ((Test-Path $InstallPathwAtomics) -and (-not $NoPayloads)) { Remove-Item -Path $InstallPathwAtomics -Recurse -Force -ErrorAction Stop | Out-Null }
}
Catch {
Write-Host -ForegroundColor Red $_.Exception.Message
return
}
}
if (-not (Test-Path $InstallPath)) { New-Item -ItemType directory -Path $InstallPath | Out-Null }

$url = "https://github.com/$RepoOwner/atomic-red-team/archive/$Branch.zip"
$path = Join-Path $DownloadPath "$Branch.zip"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
write-verbose "Beginning download of atomics folder from Github"

# disable progress bar for faster performances
$ProgressPreference_backup = $global:ProgressPreference
$global:ProgressPreference = "SilentlyContinue"

if ($NoPayloads) {
# download zip to memory and only extract atomic yaml files
# load ZIP methods
Write-Host -ForegroundColor Yellow "Reading the Atomic Red Team repo into a memory stream. This could take up to 3 minutes."
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null

# read github zip archive into memory
$ms = New-Object IO.MemoryStream
[Net.ServicePointManager]::SecurityProtocol = ([Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12)

Add-Type -AssemblyName System.Net.Http
$httpClient = New-Object System.Net.Http.HttpClient
$httpClient.Timeout = New-Object System.TimeSpan(0, 3, 0)
$response = $httpClient.GetAsync($url).Result
$response.Content.CopyToAsync($ms).Wait()
$zip = New-Object System.IO.Compression.ZipArchive($ms)

$Filter = '*.yaml'

# ensure the output folder exists
$exists = Test-Path -Path $InstallPathwAtomics
if ($exists -eq $false) {
$null = New-Item -Path $InstallPathwAtomics -ItemType Directory -Force
}

# find all files in ZIP that match the filter (i.e. file extension)
$zip.Entries |
Where-Object {
($_.FullName -like $Filter) `
-and (($_.FullName | split-path | split-path -Leaf) -eq [System.IO.Path]::GetFileNameWithoutExtension($_.Name)) `
-and ($_.FullName | split-path | split-path | split-path -Leaf) -eq "atomics"
} |
ForEach-Object {
# extract the selected items from the ZIP archive
# and copy them to the out folder
$dstDir = Join-Path $InstallPathwAtomics ($_.FullName | split-path | split-path -Leaf)
New-Item -ItemType Directory -Force -Path $dstDir | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, (Join-Path $dstDir $_.Name), $true)
}
$zip.Dispose()
}
else {
Invoke-WebRequest $url -OutFile $path

write-verbose "Extracting ART to $InstallPath"
$zipDest = Join-Path "$DownloadPath" "tmp"
Microsoft.PowerShell.Archive\Expand-Archive -LiteralPath $path -DestinationPath "$zipDest" -Force:$Force
$atomicsFolderUnzipped = Join-Path (Join-Path $zipDest "atomic-red-team-$Branch") "atomics"
Move-Item $atomicsFolderUnzipped $InstallPath
Remove-Item $zipDest -Recurse -Force
Remove-Item $path
}

# restore progress bar preferences
$global:ProgressPreference = $ProgressPreference_backup
}
else {
Write-Host -ForegroundColor Yellow "An atomics folder already exists at $InstallPathwAtomics. No changes were made."
Write-Host -ForegroundColor Cyan "Try the install again with the '-Force' parameter if you want to delete the existing installion and re-install."
Write-Host -ForegroundColor Red "Warning: All files within the atomics folder ($InstallPathwAtomics) will be deleted when using the '-Force' parameter."
}
}
Catch {
Write-Error "Installation of the AtomicsFolder Failed."
Write-Host $_.Exception.Message`n
}
}

0 comments on commit d33ff4a

Please sign in to comment.